Context
A shared database is a shared failure. A shared entity makes two services release together. The persistence library must work with whatever connection the service runtime provides, and it is chosen once.Decision
- MikroORM is the one ORM. Prisma, Drizzle, TypeORM and Knex are rejected (0011).
- Each service owns its one database, its schema and its migrations, applied by one migrator in one order. No service reads or writes another service’s tables.
- Entities and repositories live inside the owner. In a domain package they are in
infrastructure/persistenceand implement ports thatapplicationdeclares (0004). There is no global entity registry. - Cross-service reads and writes use the owner’s API or its published events, never a shared entity. A read model kept from events is local to its reader.
- MikroORM runs over the connection the runtime provides. It does not provision databases.
- Raw SQL is an approved escape hatch for triggers, generated columns, extensions and specialised indexes, and it lives in migrations.
- Tenant isolation is a consumer decision made at the first tenant-owned table. The mechanism is a MikroORM scoped entity plus row-level security, enforced at two layers; the rule itself is
/constitution/tenancy. Until a tenant-owned table exists, nothing here presumes tenancy.
Consequences
- Schema changes ship with the service that owns them.
- Reporting across services needs events or an explicit read model.
Enforcement
FORBIDDEN_DEPENDENCIESrejects the other ORMs.- The oxlint services override bans importing another service’s
entities/anddb. - Planned: a schema-drift test per service (the entities produce an empty update-schema diff), and a tenant isolation test once the first scoped table exists.
Supersedes and sources
Supersedes: none. Sources: basaltadr/0049-database-ownership, 0006-orm-direction; figentra docs/adr/0006-data-access-wave-0; juristra docs/adr/0004-tenancy-and-isolation (tenancy half).