Overview
@woltz/rich-domain-drizzle integrates rich-domain with Drizzle ORM. Unlike Prisma, Drizzle works at the SQL level, which means you have full control over queries but must configure things explicitly — junction tables, column mappings, and eager-loaded relations are all opt-in.
Unit of Work
Request-isolated transactions via AsyncLocalStorage
Repository Base Class
DrizzleRepository with built-in Criteria support
Change Tracking
DrizzleToPersistence with automatic change detection
Batch Operations
DrizzleBatchExecutor for efficient bulk writes
Transactional Outbox
Optional
outboxStore in repository config for guaranteed event deliveryFor a complete working example, see the fastify-with-drizzle example in the repository.
DrizzleUnitOfWork
Manages transactions with per-request isolation usingAsyncLocalStorage.
Transaction Execution
Nested Transactions (Idempotent)
Iftransaction() is called inside an already-active transaction, it reuses the same context instead of nesting:
API Reference
@Transactional Decorator
Wraps a method in a transaction automatically. Reuses the existing transaction if one is already active.With Explicit UoW Parameter
UoW Resolution Order
The decorator looks for the UoW instance in this order:- Decorator parameter —
@Transactional(myUow) this.uowpropertythis._uowproperty- Any property that is a
DrizzleUnitOfWorkinstance
Behavior
DrizzleRepository
Base class for repositories. Providesfind, findById, findManyByIds, save, delete, and more with full Criteria support.
Transactional Outbox
Pass an optionaloutboxStore in DrizzleRepositoryConfig. When set, save() automatically persists uncommitted domain events to the outbox table in the same database transaction as the aggregate write.
Use
DrizzleOutboxStore from @woltz/rich-domain-drizzle. For the full setup (schema, event bus decorator, background publisher), see Transactional Outbox.
Complete Implementation
Context-Aware Queries
Usethis.context in custom methods — it automatically switches to the transaction client when inside a transaction:
EntitySchemaRegistry
Maps domain entities to Drizzle tables, configures FK relationships, and describes collection types.See the complete Schema Registry documentation for all features.
Basic Registration
Owned Collections (1:N)
Reference Collections (N:N) — Junction Required
Collection Types
tableMap
ThetableMap maps entity names (and junction table names) to the actual Drizzle table objects. It is used by DrizzleBatchExecutor to execute queries.
DrizzleToPersistence
Base mapper class for persisting aggregates. You controlonCreate manually; onUpdate defaults to DrizzleBatchExecutor.
onUpdate Default Behavior
If you do not overrideonUpdate, the adapter uses DrizzleBatchExecutor.execute(changes) automatically:
DrizzleBatchExecutor
ExecutesAggregateChanges in the correct order, respecting referential integrity.
Execution Order
-
Deletes — Leaf → Root (depth DESC)
owned:DELETE FROM table WHERE id IN (...)reference:DELETE FROM junction WHERE sourceKey = ? AND targetKey IN (...)
-
Creates — Root → Leaf (depth ASC)
owned:INSERT INTO table VALUES (...)reference:INSERT INTO junction VALUES (...) ON CONFLICT DO NOTHING
- Updates — Any order
Criteria Support
DrizzleQueryBuilder translates a Criteria instance into Drizzle where, orderBy, limit, and offset clauses.
Supported Operators
Limitations
No Dot-Notation Field Paths
Criteria filters, ordering, and search fields must reference top-level columns on the repository’s primary table. Dot paths like"profile.name" or "posts.title" are not supported and will throw a DrizzleAdapterError.
No Relation Quantifiers
Criteria quantifiers (some, every, none) are not supported. Use raw Drizzle queries with EXISTS subqueries for these cases.
contains is Case-Insensitive (PostgreSQL Only)
The contains, startsWith, and endsWith operators use ILIKE, which is a PostgreSQL-specific operator. They will not work on SQLite or MySQL without customization.
Junction Config is Always Required
Unlike Prisma (which handles implicit N:N automatically), Drizzle requires an explicitjunction config for every reference collection. Omitting it throws MissingJunctionConfigError with an example config.