Flow
Decisions

0010 — Identifier strategy

bigserial for event ordering, UUIDv7 for entities, FID for principals.

Decided. Three jobs, three types.

JobTypeGenerated by
Ordering (events.id)bigserialPostgres sequence
Entity identityuuid (v7)Go, uuid.NewV7()
Principalstext (FID)identity — minted or adopted

Why UUIDv7 in Go, not Postgres

v7 over v4 because it embeds a timestamp prefix, so inserts append in the b-tree instead of scattering.

Generated in the application because native uuidv7() requires Postgres 18, and the pg_uuidv7 extension is not reliably available on managed providers. Generating in Go removes the dependency entirely — the column is a plain uuid not null. This is the same rule as "no extensions" generally: anything an extension would do, do in Go.

Why not auto-incrementing integers as public ids

Enumerable, and they leak volume — create two records and subtract to learn how many exist. bigserial is correct for events.id, which is a cursor and never exposed as an identity.

Why FIDs stay text

identity mints FIDs or adopts ones assigned upstream — Google Workspace staff arrive with theirs. An adopted id cannot be a UUIDv7, so the type has to be an opaque string.

FIDs identify principals only — persons, organizations, systems, groups. They are not a universal identifier scheme. A work order is not a principal and gets a UUID.

Consequences

  • Entity ids are safe to expose in URLs.
  • FIDs appear in other services as columns, and become real foreign keys where the local principals copy exists.
  • Postgres never generates an id, so tests and fixtures control ids fully.

On this page