Skip to main content

Documentation Index

Fetch the complete documentation index at: https://woltz.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Overview

@woltz/rich-domain-cli provides CLI tools for scaffolding DDD projects and generating domain entities from Prisma schemas.
npm install -D @woltz/rich-domain-cli

# Or run directly with npx
npx rich-domain <command>

Project Scaffolding

Initialize complete projects from templates

Prisma Introspection

Generate domain from existing schemas

Smart Classification

Auto-detects Aggregates vs Entities

Full Stack Generation

Schemas, entities, repositories, mappers

Requirements

  • Node.js >= 20
  • Prisma schema file (for generate command)
  • @woltz/rich-domain (required)
  • @woltz/rich-domain-prisma (optional, for repositories/mappers)
  • Validation library (optional, for runtime validation)

Commands

The CLI provides three main commands:
CommandDescription
initInitialize a new project from a template
generateGenerate domain entities from Prisma schema
addManually create entity, aggregate, or value object

init

Initialize a new DDD project from a template with all infrastructure pre-configured.
npx rich-domain init [directory] [options]

Options

OptionAliasDefaultDescription
--template <n>-tPromptTemplate to use
--package-manager <pm>-pPromptnpm, pnpm, yarn, or bun
--force-ffalseSkip prompts and overwrite existing

Examples

# Interactive mode
npx rich-domain init my-app

# Specify template and package manager
npx rich-domain init my-app --template fullstack-prisma -p pnpm

# Initialize in current directory
npx rich-domain init . --template fullstack-drizzle

# Skip all prompts
npx rich-domain init my-app -t fullstack-typeorm -p npm -f

Templates

Available Templates

TemplateStackDescription
fullstack-prismaFastify + Prisma + BullMQ + ZodPrisma v7 with driver adapter (pg)
fullstack-drizzleFastify + Drizzle ORM + BullMQ + ZodDrizzle with node-postgres
fullstack-typeormFastify + TypeORM + BullMQ + ZodTypeORM with decorators and DataSource
All templates share the same DDD architecture and generate the same project structure. They differ only in the ORM layer.

Generated Project Structure

my-app/
├── prisma.config.ts              # ORM config (Prisma) / drizzle.config.ts (Drizzle)
├── docker-compose.yml            # PostgreSQL + Redis
├── .env.example
├── tsconfig.json
└── src/
    ├── env.ts                    # Zod-validated environment variables
    ├── server.ts                 # Fastify app entry point
    ├── constants.ts              # Queue name constants
    ├── domain/
    │   ├── entities/
    │   │   └── user.aggregate.ts
    │   ├── repositories/
    │   │   └── user.repository.ts
    │   ├── events/
    │   │   └── user-created.event.ts
    │   └── value-objects/
    │       └── email.ts
    ├── application/
    │   ├── service/
    │   │   └── user.service.ts
    │   └── processor/
    │       ├── events.processor.ts   # Observe-only handlers (logging, emails)
    │       └── action.processor.ts   # Mutation handlers
    ├── infra/
    │   ├── database/
    │   │   ├── <orm>/              # Schema/entity files
    │   │   ├── mappers/            # Domain ↔ persistence mappers
    │   │   ├── repositories/       # Concrete repository implementations
    │   │   ├── schemas/            # Persistence type definitions
    │   │   └── seed/
    │   ├── queue/
    │   │   ├── connection.ts       # IORedis connection
    │   │   ├── event-bus.ts        # BullMQ IDomainEventBus implementation
    │   │   ├── event-worker.ts     # BullMQ domain event worker
    │   │   └── queue-publisher.ts
    │   └── di/
    │       ├── container.ts        # DI container
    │       └── fastify-plugin.ts   # Fastify DI decorator
    └── presentation/
        ├── controllers/
        │   └── user.controller.ts
        └── dto/
            └── user/
                └── user.dto.ts

Scripts

All templates include the same base scripts:
ScriptDescription
devStart dev server with tsx watch
buildCompile TypeScript
startStart production server
lintType-check without emitting
db:generateGenerate ORM client/migrations
db:migrateRun migrations
db:pushPush schema to database
db:studioOpen ORM studio UI
db:seedRun seed script
docker:upStart PostgreSQL + Redis
docker:downStop Docker services
TypeORM does not include db:generate, db:migrate, db:push, or db:studio — schema synchronization is handled by synchronize: true in development mode.

fullstack-prisma

Uses Prisma v7 with the @prisma/adapter-pg driver adapter (required in v7) and prisma.config.ts for schema configuration. Key dependencies: @prisma/client, @prisma/adapter-pg, pg, @woltz/rich-domain-prisma
// src/infra/database/prisma.ts
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";

const adapter = new PrismaPg({ connectionString: env.DATABASE_URL });
export const prisma = new PrismaClient({ adapter });
// prisma.config.ts
import { defineConfig } from "prisma/config";

export default defineConfig({
  schema: path.join("src", "infra", "database", "prisma", "schema.prisma"),
  datasource: {
    url: env.DATABASE_URL,
  },
  migrations: {
    path: path.join("src", "infra", "database", "prisma", "migrations"),
    seed: path.join("src", "infra", "database", "seed", "seed.ts"),
  },
});

fullstack-drizzle

Uses Drizzle ORM with drizzle-orm/node-postgres and a lazy-initialized connection pool. Key dependencies: drizzle-orm, pg, @woltz/rich-domain-drizzle
// src/infra/database/db.ts
export async function initializeDatabase() {
  pool = new Pool({ connectionString: env.DATABASE_URL });
  db = drizzle(pool, { schema });
}
// drizzle.config.ts
export default defineConfig({
  schema: "./src/infra/database/drizzle/schema.ts",
  out: "./src/infra/database/drizzle/migrations",
  dialect: "postgresql",
});

fullstack-typeorm

Uses TypeORM with DataSource, decorator-based entities, and synchronize: true in development. Key dependencies: typeorm, pg, reflect-metadata, @woltz/rich-domain-typeorm
// src/infra/database/db.ts
export const AppDataSource = new DataSource({
  type: "postgres",
  url: env.DATABASE_URL,
  synchronize: env.NODE_ENV === "development",
  entities: [UserEntity],
});
The TypeORM template enables synchronize: true in development mode. Disable this and use migrations in production.

Getting Started

After running init:
cd my-app
pnpm install
cp .env.example .env
pnpm docker:up
pnpm db:push       # or db:migrate for Prisma/Drizzle
pnpm dev
API:     http://localhost:3000
Swagger: http://localhost:3000/doc

generate

Generate domain entities, repositories, and mappers from your Prisma schema.
npx rich-domain generate [options]

Options

OptionAliasDefaultDescription
--schema <path>-sAuto-detectPath to Prisma schema file
--output <path>-osrcOutput directory
--validation <type>-vAuto-detectzod, valibot, arktype, or none
--models <names>-mAll modelsComma-separated list of models
--dry-run-falsePreview without writing files
--force-ffalseSkip confirmation prompts

Examples

# Basic usage (auto-detect schema and validation)
npx rich-domain generate

# Specify schema path
npx rich-domain generate --schema ./prisma/schema.prisma

# Generate to custom directory with Zod
npx rich-domain generate --output ./src/domain --validation zod

# Generate only specific models
npx rich-domain generate --models User,Post,Comment

# Preview without writing files
npx rich-domain generate --dry-run

Package Detection

The CLI automatically detects installed packages and adjusts generation accordingly:
🔍 Rich Domain Generator

Reading schema from prisma/schema.prisma

Detected packages:
 @woltz/rich-domain-prisma: installed
 Validation library: zod
PackageEffect
@woltz/rich-domain-prismaGenerates repositories and mappers
zodUses Zod for schema validation
valibotUses Valibot for schema validation
arktypeUses ArkType for schema validation
NoneGenerates TypeScript interfaces only
Repository and mapper files are only generated when @woltz/rich-domain-prisma is installed.

Generated Structure

For a Prisma schema with User, Post, and Comment models:
src/
├── shared/
│   └── enums.ts                        # All Prisma enums
├── user/
│   ├── index.ts                        # Barrel export
│   ├── user.schema.ts                  # Zod schema
│   ├── user.aggregate.ts               # Aggregate class
│   ├── user.repository.ts              # Repository*
│   ├── user-to-domain.mapper.ts        # Domain mapper*
│   └── user-to-persistence.mapper.ts   # Persistence mapper*
├── post/
│   ├── index.ts
│   ├── post.schema.ts
│   ├── post.aggregate.ts               # Aggregate (has children)
│   └── ...
└── comment/
    ├── index.ts
    ├── comment.schema.ts
    └── comment.entity.ts               # Entity (owned by Post)

* Only generated when @woltz/rich-domain-prisma is installed

Classification Logic

The CLI classifies models as Aggregates or Entities based on their relationships.

Classification Rules

ConditionClassificationReason
Referenced by others via FKAggregateIt’s a parent/root
Has list relations (1:N)AggregateIt owns children
Has N:N relationsAggregateBoth sides are roots
Only has FKs, not referencedEntityIt’s a child/owned
No relationsAggregateStandalone root

Validation Libraries

Zod (Default)

import { z } from "zod";

export const userSchema = z.object({
  id: z.custom<Id>((v) => v instanceof Id),
  email: z.string().email(),
  name: z.string(),
  age: z.number().int().min(0),
  isActive: z.boolean(),
  createdAt: z.date(),
});

Valibot

import * as v from "valibot";

export const userSchema = v.object({
  id: v.custom<Id>((v) => v instanceof Id),
  email: v.pipe(v.string(), v.email()),
  name: v.string(),
  age: v.pipe(v.number(), v.integer(), v.minValue(0)),
  isActive: v.boolean(),
  createdAt: v.date(),
});

ArkType

import { type } from "arktype";

export const userSchema = type({
  id: "instanceof Id",
  email: "string.email",
  name: "string",
  age: "integer >= 0",
  isActive: "boolean",
  createdAt: "Date",
});

None (Interfaces Only)

import { Id } from "@woltz/rich-domain";

export interface UserProps {
  id: Id;
  email: string;
  name: string;
  age: number;
  isActive: boolean;
  createdAt: Date;
}

Best Practices

After Generation

  1. Review classifications - Adjust Aggregate/Entity based on actual domain boundaries
  2. Add business logic - The generated code is a starting point
  3. Add validation rules - Customize schemas with business constraints
  4. Configure hooks - Add lifecycle hooks as needed

When to Re-generate

  • After changing Prisma schema structure
  • After adding new models
  • Use --models flag to regenerate specific models only
Use --dry-run first to preview changes before overwriting files.

Manual Adjustments

Some scenarios require manual adjustment:
ScenarioAdjustment Needed
Lookup tables (Tag, Category)May want to be Value Objects
Self-referential relationsReview aggregate boundaries
Complex N:NDecide which side owns the relation

add

Manually create an entity, aggregate, or value object without requiring a Prisma schema.
npx rich-domain add <name> [...props] [options]

Options

OptionAliasDefaultDescription
--aggregate-atrueCreate an Aggregate (default)
--entity-efalseCreate an Entity
--value-object-vfalseCreate a Value Object
--with-repo-falseGenerate repository interface and implementation
--with-mapper-falseGenerate ToDomain and ToPersistence mappers
--with-all-falseGenerate repository and mappers
--output <path>-oAuto-detectOutput directory
--force-ffalseSkip prompts and overwrite

Examples

# Create a simple aggregate
npx rich-domain add User

# Create with properties
npx rich-domain add User name:string email:string

# Create with optional and enum properties
npx rich-domain add User name:string role:(USER,ADMIN) age:number?

# Create a Value Object (single primitive property)
npx rich-domain add Email -v email:string

# Create an Entity
npx rich-domain add OrderItem -e quantity:number price:number

# Create with relations
npx rich-domain add Product name:string author:User tags:Tag[]

# Create with repository and mappers
npx rich-domain add Order --with-all

Props Syntax

The props follow a name:type format with support for various modifiers:
SyntaxDescriptionExample
name:stringRequired stringemail:string
name:numberRequired numberage:number
name:booleanRequired booleanisActive:boolean
name:dateRequired datecreatedAt:date
name:type?Optional (nullable)bio:string?
name:type[]Arraytags:string[]
name:(A,B,C)Inline enumstatus:(ACTIVE,INACTIVE)
name:EntityRelation (PascalCase)author:User
name:Entity[]Array relationposts:Post[]

Props Examples

# Basic types
npx rich-domain add User \
  name:string \
  email:string \
  age:number \
  isAdmin:boolean \
  createdAt:date

# Optional fields
npx rich-domain add Profile \
  bio:string? \
  website:string? \
  avatar:string?

# Arrays
npx rich-domain add Article \
  title:string \
  tags:string[] \
  categories:string[]

# Enums
npx rich-domain add Order \
  status:(PENDING,PROCESSING,SHIPPED,DELIVERED) \
  priority:(LOW,MEDIUM,HIGH)

# Relations
npx rich-domain add Post \
  title:string \
  content:string \
  author:User \
  comments:Comment[]

# Combined
npx rich-domain add Product \
  name:string \
  price:number \
  description:string? \
  status:(DRAFT,PUBLISHED,ARCHIVED) \
  tags:string[] \
  category:Category \
  reviews:Review[]

Auto-Detection

The add command automatically detects:
  1. Validation Library: Checks package.json for zod, valibot, or arktype
  2. Prisma Adapter: Detects @woltz/rich-domain-prisma for repository/mapper generation
  3. Output Directory: Looks for existing src/domain or src directories
If @woltz/rich-domain-prisma is not installed, generic implementations are generated that you’ll need to customize for your database.