Introduction
What is noddde and why use it
What is noDDDe?
noDDDe is a TypeScript framework for building applications using Domain-Driven Design (DDD), Command Query Responsibility Segregation (CQRS), and Event Sourcing patterns.
It provides building blocks — not an opinionated runtime. You define your domain as typed objects and pure functions, and noDDDe gives you the type infrastructure to wire them together safely.
Core Ideas
Aggregates
Aggregates are consistency boundaries — the units that process commands and enforce business rules. In noDDDe, an aggregate is defined using the functional Decider pattern:
const BankAccount = defineAggregate<BankAccountDef>({
initialState: { balance: 0, transactions: [] },
commands: {
AuthorizeTransaction: (command, state, infra) => {
/* return events */
},
},
apply: {
TransactionAuthorized: (event, state) => {
/* return new state */
},
},
});No base class. No decorators. No this. Just an object with handler maps.
Commands
Commands express intent to change state. They are typed messages with a name, a targetAggregateId (for routing), and an optional payload:
type BankAccountCommand = DefineCommands<{
CreateBankAccount: void;
AuthorizeTransaction: { amount: number; merchant: string };
}>;Events
Events are immutable facts about what happened. They use past tense and are produced by command handlers:
type BankAccountEvent = DefineEvents<{
BankAccountCreated: { id: string };
TransactionAuthorized: { id: string; amount: number; merchant: string };
}>;Projections
Projections build read-optimized views from event streams. They are the query side of CQRS:
const BankAccountProjection = defineProjection<BankAccountProjectionDef>({
reducers: {
BankAccountCreated: (event, view) => ({ ...view, id: event.payload.id }),
TransactionAuthorized: (event, view) => ({
...view,
balance: view.balance + event.payload.amount,
}),
},
queryHandlers: {
/* ... */
},
});Sagas
Sagas are event-driven process managers that coordinate workflows across multiple aggregates. They are the structural inverse of aggregates — events in, commands out:
const OrderFulfillmentSaga = defineSaga<OrderFulfillmentSagaDef>({
initialState: { status: null },
startedBy: ["OrderPlaced"],
associations: { OrderPlaced: (event) => event.payload.orderId },
handlers: {
OrderPlaced: (event, state) => ({
state: { ...state, status: "awaiting_payment" },
commands: { name: "RequestPayment", targetAggregateId: "...", payload: { ... } },
}),
},
});Domain Configuration
configureDomain wires aggregates, projections, sagas, and infrastructure into a running domain:
const domain = await configureDomain<MyInfrastructure>({
writeModel: { aggregates: { BankAccount } },
readModel: { projections: { BankAccount: BankAccountProjection } },
processModel: { sagas: { OrderFulfillment: OrderFulfillmentSaga } },
infrastructure: {
/* persistence, buses, custom infra */
},
});How noDDDe Differs
| Traditional DDD | noDDDe | |
|---|---|---|
| Aggregates | Classes extending AggregateRoot | Plain objects via defineAggregate |
| State changes | this.apply(event) mutates state | Apply handlers return new state (immutable) |
| Command handling | Decorated methods | Typed handler maps |
| Type safety | Manual generic parameters | Inferred from AggregateTypes bundle |
| Event definitions | Enum + interface + union | DefineEvents<{ ... }> single declaration |
| Testing | Requires framework setup | Call functions directly |
| Infrastructure | Decorator-based injection | Function parameter injection |
noDDDe draws inspiration from modern TypeScript API design (Zod, tRPC, Drizzle) — declarative, inference-heavy, zero-boilerplate.
Who Is This For?
noDDDe is for TypeScript developers building event-driven or domain-rich applications who want:
- Type-safe domain modeling without class hierarchies
- Testable business logic without mock frameworks
- Flexible persistence (event sourcing or state storage) with built-in adapters for Drizzle, Prisma, and TypeORM
- A functional approach to DDD patterns
Next Steps
- Quick Start — Install noDDDe and build your first aggregate
- Core Concepts — Understand the foundations