Context
CustomerAccount (first aggregate root, introduced with customer registration) currently uses database-generated identity: it's created with id = null, the autoincrement value is assigned inside SiteUserRepository::save() via CustomerAccount::assignId(), and only then can the aggregate emit CustomerAccountRegistrationRequested (hence the emitPendingEvents() indirection).
This is a known DDD anti-pattern (Evans, Vernon): the aggregate is not valid immediately after construction — it has no identity until persisted, which is why the registration event can't carry the accountId at construction time.
Problem
- Aggregate has a "half-created" state (
id = null) between register() and save().
- Domain events can't carry the id before persist → forced the
assignId() + emitPendingEvents() workaround in RegisterCustomerHandler.
- Identity is owned by infrastructure, not the domain.
Proposed direction
Move to application-generated identity using UUIDv7 (time-ordered, so it keeps B-tree index locality like autoincrement, unlike random UUIDv4):
- Domain generates
CustomerAccountId before construction: CustomerAccount::register($id, $email, ...) — valid from birth, event carries id immediately, no emitPendingEvents.
- Store as
BINARY(16) in MySQL (not CHAR(36)) for index/space efficiency; convert via UUID_TO_BIN() / BIN_TO_UUID(). Library: symfony/uid.
Scope (this is an ADR-level change, not a single feature)
This touches the whole identity model from ADR-014, which currently defines admin_users / partner_users / customer_users with id BIGINT auto_increment and a polymorphic remember_tokens.user_id.
Doing UUID for customer_users only would break schema symmetry and complicate the polymorphic remember_tokens.user_id. So the migration should cover all three audience tables at once:
Not doing now
Customer registration ships with BIGINT + assignId() as a deliberate trade-off: it keeps symmetry with admin/partner (ADR-014) and avoids expanding the registration feature into a project-wide identity rewrite. This issue tracks the dedicated initiative.
Reference: backend/src/SharedKernel/Domain/Security/Aggregate/CustomerAccount.php (register(), assignId(), emitPendingEvents()).
Context
CustomerAccount(first aggregate root, introduced with customer registration) currently uses database-generated identity: it's created withid = null, the autoincrement value is assigned insideSiteUserRepository::save()viaCustomerAccount::assignId(), and only then can the aggregate emitCustomerAccountRegistrationRequested(hence theemitPendingEvents()indirection).This is a known DDD anti-pattern (Evans, Vernon): the aggregate is not valid immediately after construction — it has no identity until persisted, which is why the registration event can't carry the
accountIdat construction time.Problem
id = null) betweenregister()andsave().assignId()+emitPendingEvents()workaround inRegisterCustomerHandler.Proposed direction
Move to application-generated identity using UUIDv7 (time-ordered, so it keeps B-tree index locality like autoincrement, unlike random UUIDv4):
CustomerAccountIdbefore construction:CustomerAccount::register($id, $email, ...)— valid from birth, event carries id immediately, noemitPendingEvents.BINARY(16)in MySQL (notCHAR(36)) for index/space efficiency; convert viaUUID_TO_BIN()/BIN_TO_UUID(). Library:symfony/uid.Scope (this is an ADR-level change, not a single feature)
This touches the whole identity model from ADR-014, which currently defines
admin_users/partner_users/customer_userswithid BIGINT auto_incrementand a polymorphicremember_tokens.user_id.Doing UUID for
customer_usersonly would break schema symmetry and complicate the polymorphicremember_tokens.user_id. So the migration should cover all three audience tables at once:admin_users/partner_users/customer_usersPKs toBINARY(16)*_user_rolesFKsremember_tokens.user_idemail_verification_codes.customer_user_idFKCustomerAccountIdvalue object + generator; removeassignId()/emitPendingEvents()workaround fromCustomerAccount+RegisterCustomerHandlerNot doing now
Customer registration ships with BIGINT +
assignId()as a deliberate trade-off: it keeps symmetry with admin/partner (ADR-014) and avoids expanding the registration feature into a project-wide identity rewrite. This issue tracks the dedicated initiative.Reference:
backend/src/SharedKernel/Domain/Security/Aggregate/CustomerAccount.php(register(),assignId(),emitPendingEvents()).