Skip to main content

Custom Common Id Generator

Overview

A common ID is a business-facing identifier. It is usually shown in APIs, UI pages, customer service tools, reports, exported files, and integration messages.

Examples:

  • ORD-2026-000001
  • CUS-CN-000123
  • INV-202604-893021
  • CASE-SH-2026-00088

Unlike internal IDs, common IDs should be understandable, stable, and safe to share outside the persistence layer.


When to Customize Common IDs

Customize common ID generation when your project needs:

  • Human-readable business numbers
  • Prefixes by entity type
  • Region, tenant, channel, or business-unit codes
  • Date-based numbering
  • Sequence numbers per tenant or per day
  • Integration-compatible identifiers
  • Legacy ID compatibility

Common ID generation should be controlled through CustomUserContext, because the context knows:

  • Current tenant
  • Current user
  • Locale and region
  • Request channel
  • Current business date
  • Available infrastructure services

Recommended flow:

Create entity
-> userContext.checkAndFix(entity)
-> common ID policy decides whether a common ID is required
-> generator builds and assigns the ID
-> entity.save(userContext)

Example Generator Shape

The exact class names depend on your project, but the pattern is:

public interface CommonIdGenerator {

String nextCommonId(String entityType, CustomUserContext userContext);
}

Example implementation:

public class TenantDateCommonIdGenerator implements CommonIdGenerator {

private final BusinessSequenceService sequenceService;

public TenantDateCommonIdGenerator(BusinessSequenceService sequenceService) {
this.sequenceService = sequenceService;
}

@Override
public String nextCommonId(String entityType, CustomUserContext userContext) {
String prefix = prefixFor(entityType);
String tenant = userContext.currentTenantCode();
String date = userContext.businessDate().format(DateTimeFormatter.BASIC_ISO_DATE);
long sequence = sequenceService.next(entityType, tenant, date);

return "%s-%s-%s-%06d".formatted(prefix, tenant, date, sequence);
}

private String prefixFor(String entityType) {
return switch (entityType) {
case "Order" -> "ORD";
case "Customer" -> "CUS";
case "Invoice" -> "INV";
default -> "TQL";
};
}
}

Expose it through CustomUserContext:

public class CustomUserContext extends UserContext {

public CommonIdGenerator commonIdGenerator() {
return getBean(CommonIdGenerator.class);
}

public String nextCommonId(String entityType) {
return commonIdGenerator().nextCommonId(entityType, this);
}
}

Assigning Common IDs

Common IDs should normally be assigned only when missing:

public class CustomUserContext extends UserContext {

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

if (entity instanceof Order order && isBlank(order.getOrderNumber())) {
order.updateOrderNumber(nextCommonId("Order"));
}
}
}

This keeps update operations safe and prevents accidental renumbering.


Per-Entity Policy

Not every entity needs a common ID.

Good candidates:

  • Orders
  • Customers
  • Invoices
  • Tickets
  • Cases
  • Shipments
  • Audit documents

Poor candidates:

  • Join tables
  • Internal permission rows
  • Small configuration records
  • Temporary objects

Use a policy method:

public boolean requiresCommonId(Object entity) {
return entity instanceof Order
|| entity instanceof Customer
|| entity instanceof Invoice;
}

Best Practices

  • Treat common IDs as immutable after creation.
  • Keep common IDs unique within the correct business scope.
  • Do not encode sensitive user or tenant information.
  • Avoid relying only on timestamps under high concurrency.
  • Use database-backed or distributed sequences when IDs must be gap-aware or strictly ordered.
  • Keep formatting logic separate from persistence logic.

Testing Checklist

  • New entities receive common IDs.
  • Existing entities keep their common IDs after update.
  • Concurrent creation does not generate duplicates.
  • Tenant-specific prefixes and sequences work correctly.
  • Common IDs are stable across retries.
  • Failed transactions do not corrupt the sequence policy.

Summary

Common ID customization should be treated as a business policy, not a database detail. Put the policy behind CustomUserContext so generated entities can remain clean while the project controls naming, sequencing, tenant rules, and integration compatibility.