Skip to main content

Custom Internal Id Generator

Overview

An internal ID is the persistence identity used by TeaQL entities. It is usually the value stored in the database primary-key column and referenced by related objects.

TeaQL projects can customize internal ID generation when the default strategy does not match the deployment environment, database policy, or distributed-system requirement.

Typical reasons include:

  • Using Snowflake-style distributed IDs
  • Generating IDs before database insert
  • Avoiding database sequences in multi-region deployments
  • Keeping IDs sortable by creation time
  • Integrating with an existing enterprise ID service

Internal ID vs Common ID

TeaQL commonly separates two concepts:

ID TypePurposeTypical Visibility
Internal IDDatabase identity and object referenceBackend/internal
Common IDBusiness-facing or external referenceAPI/UI/integration

This page focuses on internal ID.

Use internal IDs for persistence consistency. Do not expose them as stable public business numbers unless your project has explicitly decided to do so.


Implement internal ID generation as an infrastructure service and access it through CustomUserContext.

Recommended flow:

Entity.save(userContext)
-> userContext.checkAndFix(entity)
-> userContext resolves internal ID generator
-> generator assigns missing internal IDs
-> repository persists entity graph

The important rule is:

Domain code should not know how IDs are generated.

Business logic should create objects naturally. The context should apply the project-specific ID policy.


Example Generator Shape

The exact interface name depends on your TeaQL runtime setup, but a project-level generator usually looks like this:

public interface InternalIdGenerator {

long nextId(String entityType, CustomUserContext userContext);
}

A Snowflake-style implementation:

public class SnowflakeInternalIdGenerator implements InternalIdGenerator {

private final SnowflakeClient snowflakeClient;

public SnowflakeInternalIdGenerator(SnowflakeClient snowflakeClient) {
this.snowflakeClient = snowflakeClient;
}

@Override
public long nextId(String entityType, CustomUserContext userContext) {
return snowflakeClient.nextId(entityType);
}
}

Register it in Spring:

@Configuration
public class TeaQLIdConfiguration {

@Bean
public InternalIdGenerator internalIdGenerator(SnowflakeClient client) {
return new SnowflakeInternalIdGenerator(client);
}
}

Expose it from CustomUserContext:

public class CustomUserContext extends UserContext {

public InternalIdGenerator internalIdGenerator() {
return getBean(InternalIdGenerator.class);
}

public long nextInternalId(String entityType) {
return internalIdGenerator().nextId(entityType, this);
}
}

Assigning IDs During Validation

The safest place to assign IDs is during object checking or before persistence.

Example pattern:

public class CustomUserContext extends UserContext {

@Override
public void checkAndFix(Object entity) {
super.checkAndFix(entity);

if (entity instanceof BaseEntity baseEntity && baseEntity.getId() == null) {
baseEntity.setId(nextInternalId(baseEntity.getClass().getSimpleName()));
}
}
}

Adapt this to the actual base entity type used in your generated project.


Entity-Specific Policies

Some projects need different strategies per entity:

public long nextInternalId(String entityType) {
if ("AuditRecord".equals(entityType)) {
return auditIdGenerator().nextId(entityType, this);
}
if ("Order".equals(entityType)) {
return orderIdGenerator().nextId(entityType, this);
}
return defaultInternalIdGenerator().nextId(entityType, this);
}

Keep this routing inside CustomUserContext or a dedicated ID policy service.


Best Practices

  • Generate internal IDs in one place.
  • Make the generator thread-safe.
  • Avoid random UUID strings for hot relational tables unless your database design supports them.
  • Keep IDs immutable after first persistence.
  • Ensure ID generation works before repository insert.
  • Include entity type, worker ID, or region when using distributed generation.
  • Write tests for uniqueness, ordering, and concurrency.

Testing Checklist

  • Multiple entities can be saved in one object graph.
  • IDs are assigned before insert.
  • Concurrent requests do not generate duplicate IDs.
  • IDs remain stable after update.
  • Deleted entities keep their historical ID for auditability.
  • Test and production environments use compatible strategies.

Summary

Internal ID customization belongs in CustomUserContext or a service resolved through it. This keeps generated entities clean while giving the project full control over persistence identity, distributed ID generation, and database compatibility.