Skip to main content

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 delivery
For a complete working example, see the fastify-with-drizzle example in the repository.

DrizzleUnitOfWork

Manages transactions with per-request isolation using AsyncLocalStorage.

Transaction Execution

Nested Transactions (Idempotent)

If transaction() 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:
  1. Decorator parameter@Transactional(myUow)
  2. this.uow property
  3. this._uow property
  4. Any property that is a DrizzleUnitOfWork instance

Behavior


DrizzleRepository

Base class for repositories. Provides find, findById, findManyByIds, save, delete, and more with full Criteria support.

Transactional Outbox

Pass an optional outboxStore 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

Use this.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

Unlike Prisma, Drizzle always requires an explicit junction config for reference collections. Drizzle does not manage junction tables automatically. Omitting junction will throw a MissingJunctionConfigError at runtime.

Collection Types

tableMap

The tableMap maps entity names (and junction table names) to the actual Drizzle table objects. It is used by DrizzleBatchExecutor to execute queries.
The keys in tableMap must exactly match the entity names used in registry.register({ entity: "..." }) and the junction table names in junction.table. A wrong key throws TableNotFoundError with a list of available keys.

DrizzleToPersistence

Base mapper class for persisting aggregates. You control onCreate manually; onUpdate defaults to DrizzleBatchExecutor.

onUpdate Default Behavior

If you do not override onUpdate, the adapter uses DrizzleBatchExecutor.execute(changes) automatically:

DrizzleBatchExecutor

Executes AggregateChanges in the correct order, respecting referential integrity.

Execution Order

  1. Deletes — Leaf → Root (depth DESC)
    • owned: DELETE FROM table WHERE id IN (...)
    • reference: DELETE FROM junction WHERE sourceKey = ? AND targetKey IN (...)
  2. Creates — Root → Leaf (depth ASC)
    • owned: INSERT INTO table VALUES (...)
    • reference: INSERT INTO junction VALUES (...) ON CONFLICT DO NOTHING
  3. 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.
For cross-table filtering or ordering, add a custom method to your repository using Drizzle’s SQL API with explicit JOINs:

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 explicit junction config for every reference collection. Omitting it throws MissingJunctionConfigError with an example config.

Error Reference


API Reference

Exports

DrizzleRepositoryConfig

DrizzleRepository Methods

DrizzleUnitOfWork Methods