---
name: domain-driven-design-expert
description: "Expert guide for Domain-Driven Design (DDD). Covers tactical patterns (Aggregates, Value Objects), strategic patterns (Bounded Contexts), event storming, and CQRS / Panduan ahli Desain Berbasis Domain (DDD)."
author: "Roedy Rustam"
version: "4.4.2"
---
# Domain-Driven Design (DDD) Expert (Strategic & Tactical 2026)
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
---
## English
### Orchestration & Integration
Connects and orchestrates with domain skills:
- **Architecture & Modularity**: `scalability-clean-code` (SOLID, Hexagonal/Ports & Adapters), `event-driven-architect` (Domain Events, Kafka/RabbitMQ), and `saas-architect` (Bounded Context isolation).
- **Persistence & Mapping**: `database-orm-expert` (Repository pattern, Data Mappers, pure domain models without ORM leakage), `double-entry-ledger-fintech-expert`.
- **Formal Verification & Quality**: `formal-spec-z3-verifier` (mathematical invariant verification) and `anti-slop` (pure domain model hygiene, zero anemic domain models).
- **Orchestration**: `brainstorming` and `zero-to-prod-orchestrator` (Phase 2 & Phase 3: Domain Discovery & Architecture).
### Description
Comprehensive architectural guide for tackling core business complexity using Domain-Driven Design (DDD). Covers Strategic Design (Bounded Contexts, Context Mapping, Anti-Corruption Layers, Ubiquitous Language) and Tactical Design (immutable Value Objects, Aggregate Roots, Domain Events, Repositories, Specification pattern, and CQRS separation).
### 1. Strategic & Tactical DDD Pattern Matrix
| DDD Pattern | Architectural Role | Boundary Rule | Anti-Pattern to Reject |
| :--- | :--- | :--- | :--- |
| **Bounded Context** | Autonomous semantic boundary (`Billing`, `Identity`, `Catalog`) | Communicates across contexts via Domain Events or Anti-Corruption Layer (ACL) | Sharing mutable ORM database models across microservice boundaries |
| **Value Object** | Immutable domain measure (`Money`, `Percentage`, `EmailAddress`) | Validated on construction; equality by structural value, never by ID | Primitive Obsession (passing raw `number` for currency or `string` for status) |
| **Aggregate Root** | Transactional consistency boundary (`OrderAggregate`, `InvoiceAggregate`) | 1 transaction = 1 Aggregate Root mutated; referenced externally only by ID | Modifying child entities directly or spanning 5 aggregates in one lock |
| **Anti-Corruption Layer (ACL)** | Translates external 3rd-party payloads (Stripe/DOKU/Legacy) into pure Domain types | Lives in the Infrastructure layer (`src/infrastructure/acl/`) | Leaking external webhook DTO shapes into core Domain Entities |
### 2. Tactical Pattern: Immutable Value Object & Aggregate Root (TypeScript)
```typescript
// src/domain/shared/value-objects/Money.ts
export type CurrencyCode = 'USD' | 'IDR' | 'EUR';
export class Money {
public readonly amountMinor: bigint;
public readonly currency: CurrencyCode;
private constructor(amountMinor: bigint, currency: CurrencyCode) {
if (amountMinor < 0n) {
throw new Error('DOMAIN_INVARIANT_VIOLATION: Money amount cannot be negative.');
}
this.amountMinor = amountMinor;
this.currency = currency;
}
public static fromMinorUnits(amountMinor: bigint | number, currency: CurrencyCode): Money {
const normalized = typeof amountMinor === 'number' ? BigInt(Math.round(amountMinor)) : amountMinor;
return new Money(normalized, currency);
}
public add(other: Money): Money {
if (this.currency !== other.currency) {
throw new Error(`CURRENCY_MISMATCH: Cannot add ${other.currency} to ${this.currency}.`);
}
return new Money(this.amountMinor + other.amountMinor, this.currency);
}
public equals(other: Money): boolean {
return this.amountMinor === other.amountMinor && this.currency === other.currency;
}
}
// src/domain/order/OrderAggregate.ts
export interface DomainEvent {
readonly eventName: string;
readonly occurredAtIso: string;
readonly aggregateId: string;
}
export class OrderPaidEvent implements DomainEvent {
public readonly eventName = 'order.paid.v1';
public readonly occurredAtIso = new Date().toISOString();
constructor(
public readonly aggregateId: string,
public readonly paidAmountMinor: string,
public readonly currency: CurrencyCode
) {}
}
export class OrderAggregate {
private readonly domainEvents: DomainEvent[] = [];
constructor(
public readonly id: string,
public readonly tenantId: string,
private total: Money,
private status: 'DRAFT' | 'PAID' | 'CANCELLED' = 'DRAFT',
private version: number = 1
) {}
public getStatus(): 'DRAFT' | 'PAID' | 'CANCELLED' {
return this.status;
}
public getTotal(): Money {
return this.total;
}
public getVersion(): number {
return this.version;
}
public markAsPaid(paymentAmount: Money): void {
if (this.status !== 'DRAFT') {
throw new Error(`INVALID_STATE_TRANSITION: Cannot pay order in ${this.status} state.`);
}
if (!this.total.equals(paymentAmount)) {
throw new Error('PAYMENT_AMOUNT_MISMATCH: Paid amount does not match order total.');
}
this.status = 'PAID';
this.version += 1;
this.domainEvents.push(
new OrderPaidEvent(this.id, this.total.amountMinor.toString(), this.total.currency)
);
}
public pullDomainEvents(): DomainEvent[] {
const events = [...this.domainEvents];
this.domainEvents.length = 0;
return events;
}
}
```
### 3. Production Verification Checklist
- [ ] **Model Purity**: Domain Entities and Value Objects in `src/domain/` have zero imports from ORM (`drizzle-orm`, `@prisma/client`) or HTTP (`hono`, `next`, `express`) packages.
- [ ] **Access Strictly Through Aggregate Roots**: Child entities are mutated exclusively via invariant-checked methods on their Aggregate Root.
- [ ] **Optimistic Concurrency Version**: Every Aggregate Root tracks a monotonic `version` column checked during `Repository.save()` to prevent lost updates.
- [ ] **Anti-Corruption Layer (ACL)**: External API responses and webhooks are mapped through an ACL translator before entering the domain layer.
---
#### Transactional Outbox & Domain Event Dispatcher (TypeScript)
```typescript
export interface DomainEventPayload {
readonly eventId: string;
readonly aggregateId: string;
readonly eventType: string;
readonly occurredAt: string;
readonly payload: Record;
}
export interface OutboxRepository {
insertOutboxEvents(events: readonly DomainEventPayload[]): Promise;
}
export abstract class AggregateRoot {
private readonly uncommittedEvents: DomainEventPayload[] = [];
constructor(public readonly id: TId) {}
protected recordDomainEvent(eventType: string, payload: Record): void {
this.uncommittedEvents.push({
eventId: crypto.randomUUID(),
aggregateId: this.id,
eventType,
occurredAt: new Date().toISOString(),
payload
});
}
public pullDomainEvents(): readonly DomainEventPayload[] {
const events = [...this.uncommittedEvents];
this.uncommittedEvents.length = 0;
return events;
}
}
```
### Sovereign Execution, Token Efficiency & Invariants
| Dimension | Sovereign Mandate & Deterministic Contract |
| :--- | :--- |
| **Frontier Model Cascade** | Medium / High (`Gemini 4 Argon`, `Claude Sonnet 5.5 High`, `Muse Wonder High`, `Composer 2.5 Fast`) |
| **Availability Fallback** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Zero unhandled missing-model errors) |
| **KV-Cache & Token Protocol** | Pin static instructions & schemas at prompt prefix (`kv-cache-prefix-optimizer`); pass surgical `CONTEXT_MAP.md` slices (<250 tokens/call) during Swarm Fan-Out |
| **SaaT CLI & MCP Dispatch** | CLI: `vibes skill exec domain-driven-design-expert ` / MCP: `vibes_skill_dispatch`, `vibes_visual_audit`, `vibes_ast_query`, `vibes_verify_gate` |
| **NFR & Anti-Slop Invariants** | LCP < 1.8s, INP < 100ms, CLS < 0.05, 1px crisp borders, 3-tier radius (6px/8px/12px), `tabular-nums`, 5-state interactive contract; 100% complete zero-placeholder output verified via `node scripts/check-anti-slop.js --strict` |
---
---
## Bahasa Indonesia
### Integrasi Orkestrasi
Terhubung dan mengorkestrasi bersama skill domain:
- **Arsitektur & Modularitas**: `scalability-clean-code` (SOLID, Hexagonal/Ports & Adapters), `event-driven-architect` (Domain Events, Kafka/RabbitMQ), dan `saas-architect` (isolasi Bounded Context).
- **Persistensi & Pemetaan**: `database-orm-expert` (pola Repository, Data Mapper, model domain murni tanpa kebocoran ORM), `double-entry-ledger-fintech-expert`.
- **Verifikasi Formal & Kualitas**: `formal-spec-z3-verifier` (verifikasi invarian matematis) dan `anti-slop` (kebersihan model domain murni, anti anemic domain model).
- **Orkestrator Utama**: `brainstorming` dan `zero-to-prod-orchestrator` (Fase 2 & Fase 3: Penemuan Domain & Arsitektur).
### Deskripsi
Panduan arsitektur komprehensif untuk menangani kompleksitas bisnis inti menggunakan *Domain-Driven Design* (DDD), baik Desain Strategis (*Bounded Contexts*, *Anti-Corruption Layer*, *Ubiquitous Language*) maupun Desain Taktis (*Value Objects* imutabel, *Aggregate Roots*, *Domain Events*, dan *Repositories*).
### Checklist Verifikasi Produksi
- [ ] **Kemurnian Model Domain**: Folder `src/domain/` dilarang mengimpor pustaka database/ORM atau framework HTTP; seluruh logika domain harus dapat diuji unit secara murni di memori.
- [ ] **Mutasi Melalui Aggregate Root**: Seluruh perubahan state wajib melewati metode pada Aggregate Root yang memvalidasi aturan bisnis.
- [ ] **Satu Repository per Aggregate Root**: Hanya buat interface Repository untuk Aggregate Root (`IOrderRepository`), lengkap dengan pemeriksaan versi (*optimistic locking*).
### Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian
| Dimensi | Mandat Berdaulat & Kontrak Deterministik |
| :--- | :--- |
| **Kaskade Model Frontier** | Medium / High (`Gemini 4 Argon`, `Claude Sonnet 5.5 High`, `Muse Wonder High`, `Composer 2.5 Fast`) |
| **Invarian Fallback Ketersediaan** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Bebas gagal saat model belum tersedia/kuota penuh) |
| **Protokol Hemat Token & KV-Cache** | Kunci instruksi & skema statis di prefix awal (`kv-cache-prefix-optimizer`); kirim potongan `CONTEXT_MAP.md` (<250 token/panggilan) saat Fan-Out |
| **Eksekusi SaaT CLI & MCP** | CLI: `vibes skill exec domain-driven-design-expert ` / MCP: `vibes_skill_dispatch`, `vibes_visual_audit`, `vibes_ast_query`, `vibes_verify_gate` |
| **Invarian NFR & Anti-Slop** | LCP < 1.8s, INP < 100ms, CLS < 0.05, border tegas 1px, radius 3-tier (6px/8px/12px), `tabular-nums`, kontrak interaktif 5-state; kode 100% tuntas tanpa placeholder diverifikasi melalui `node scripts/check-anti-slop.js --strict` |