Identity Persistence¶
Users and organizations are application-owned SQLAlchemy models. Services query them
through the same request-scoped AsyncSession used for sessions, OAuth2 state,
identity workflow tokens, and the outbox. This makes security-sensitive lifecycle
changes visibly atomic without an identity repository abstraction.
app.identity.mapping contains only ORM-to-DTO conversion helpers. Services
continue to expose identity DTOs rather than leaking ORM rows to HTTP handlers.
Email normalization, availability, and lifecycle transitions remain focused
shared helpers because every registration and lifecycle path must apply
identical rules. UserEmailDB is the source of truth: services explicitly load
the current and, when needed, pending address before projecting the stable HTTP
fields email, pending_email, and email_verified.
The request dependency commits on a successful response and rolls back when an
exception crosses the boundary. Services normally flush internal mutations.
An operation that must end the request transaction before expensive work uses
an explicitly named autonomous method, such as
change_password_autonomously(). It first rolls back request-scoped state,
including automatic browser-session activity, then owns its short independent
write transaction.