CONCEPT Cited by 1 source
Entity ID convention¶
Definition¶
An entity ID convention is a project-wide rule that
every identifier exposed on an API boundary follows the
same structural pattern, typically <prefix>:<type>:<natural-id>
or similar, such that:
- The type of the referenced entity is recoverable from the ID string alone (no out-of-band type hint required).
- The prefix namespaces IDs to the origin system, preventing collision with IDs from other systems.
- The natural ID (SKU, UUID, etc.) is preserved inside the string for debugging and database lookup.
Zalando's UBFF uses the pattern
entity:<typename-lowercase>:<natural-id> — for example
entity:product:1234 for a Product with SKU 1234
(Source: sources/2023-10-18-zalando-understanding-graphql-directives-practical-use-cases-at-zalando).
The @resolveEntityId directive as a mechanism¶
Zalando encodes the convention declaratively with a schema directive:
directive @resolveEntityId(
"An optional override name for the entity name in its ID"
override: String
) on OBJECT
type Product implements Entity @resolveEntityId {
id: ID!
}
The directive drives two implementation layers:
- Build-time code generation. A TypeScript codegen
step walks the schema, finds every
@resolveEntityId- marked type, and emits the boilerplate: theidresolver, the__typenameresolver, the ID-type definitions, the entity-lookup dispatcher. Engineers don't write the boilerplate; they annotate the type. - Runtime ID wrapping. At resolver-execution time,
the
idresolver wraps the natural ID (the SKU) with the prefix. The data layer stores1234; the API layer returnsentity:product:1234.
The optional override: String argument lets a
typename-rename coexist with ID stability. If Product
is renamed to Article but the external ID must remain
entity:product:1234 to stay stable for consumers, the
directive supports @resolveEntityId(override: "product").
Why type-self-identifying IDs are valuable¶
- Debugging. Pasting
entity:product:1234into a log line or ticket immediately communicates the referenced entity kind; a bare1234requires the reader to infer context. - Generic entity resolvers. A generic
lookup(id: ID!): Entityquery can dispatch to the correct resolver without a separate type hint — it parses the middle segment and looks up the resolver registry. - Cross-surface consistency. The same ID can travel through URLs, logs, tickets, analytics events, and external partner systems without losing its type context.
- Entity-based page composition integration. Zalando's UBFF composes pages out of entities — Product, Campaign, Brand — per the entity-based page composition discipline. A consistent ID format is the substrate that entity graphs traverse.
The double-click-selectable property¶
The wiki's concepts/double-click-selectable-identifier
concept observes that identifiers where every character is
part of the same token (alphanumeric + limited
punctuation) double-click-select cleanly in editors and
logs. The entity:product:1234 format — with colons that
most editors treat as word separators — may not
double-click-select as a single token; instead, users
triple-click-select the whole line. Organisations choosing
an entity-ID convention often pick delimiters based on
their editor-ergonomics preference; Zalando's colon
convention matches the more general JavaScript / URN
conventions (e.g. urn:aws:s3:::bucket,
stripe:cus_ABC123) and accepts the
not-double-click-selectable trade-off.
Contrast with structure-less IDs¶
Alternatives that this pattern explicitly rejects:
- Opaque UUIDs.
550e8400-e29b-41d4-a716-446655440000carries no type context; consumers must tag types separately. - Auto-increment integers.
1234alone is type-ambiguous across the schema — the same integer can be a Product, an Order, or a Customer. - Typed but non-prefixed IDs.
productIdas a field name carries type context in the field name, not in the value. When the value travels (log lines, event payloads), the type context is lost.
Seen in¶
- Zalando UBFF — canonical instance.
@resolveEntityIddirective + TypeScript codegen + runtime ID wrapping. Pattern:entity:product:1234whereproductis the lowercased typename,1234is the SKU (sources/2023-10-18-zalando-understanding-graphql-directives-practical-use-cases-at-zalando, systems/zalando-graphql-ubff). - Adjacent industry examples (not from this source):
AWS ARN format
arn:<partition>:<service>:<region>:<account>:<resource>(the canonical prior art for prefix-namespaced IDs); Stripe'scus_ABC123/pi_XYZshort-prefix + nanoid combination.
Related¶
- patterns/directive-driven-entity-codegen — canonical pattern form: directive → codegen → runtime wrap.
- concepts/graphql-schema-directive — the primitive
carrying the
@resolveEntityIdmarker. - concepts/entity-based-page-composition — the Zalando UBFF pattern where entity IDs thread through page assembly.
- concepts/double-click-selectable-identifier — ergonomic trade-off in ID-format choice.
- systems/graphql · systems/zalando-graphql-ubff