Tenant-schema primary keys are ULIDs stored in a PostgreSQL uuid column.
In models
Apply it to any model that follows this strategy. The trait ships in
fapost/support, so extension packages get it on the same terms Core does.
In migrations
No database default. The application generates the identifier.
Foreign keys:
Or the local equivalent, matching the style of the migrations around you.
Why ULID in a uuid column
A ULID is lexicographically sortable by creation time, which an ordinary UUIDv4 is
not. That matters for index locality: sequential inserts land near each other in
the B-tree instead of scattering across it, so the index stays compact.
Storing it as uuid rather than char(26) keeps PostgreSQL’s native 16-byte
representation, its comparison operators, and its index behaviour. The application
sees a ULID; the database sees a uuid; neither pays for the other’s convenience.
Generating in the application rather than the database means an object has its
identity before it is persisted — so related records can be built in one pass
without a round trip to find out what the id turned out to be.
Do not change special public identifiers, such as a webhook public hash, on the
strength of this rule. Those are separate decisions with their own constraints —
a webhook hash is in URLs held by external providers.