ddd-architecture · diff
git:20261018.79d9b05 to git:20261019.5b4b1ea
33 added, 137 removed. Audit A to A.
---
name: ddd-architecture
description: 'Domain-Driven Design for complex business domains. Use when deciding whether DDD is warranted, defining bounded contexts, aggregates, entities and value objects, applying CQRS or domain events, or organizing a domain layer.'
---
# Domain-Driven Design Architecture
- Strategic and tactical DDD patterns for NestJS + Angular applications.
+ Decide whether DDD is warranted, then load the one reference that owns the pattern. Do not implement a base class or a library layout from this file.
## When to Engage User
DDD is highly context-dependent. ALWAYS discuss with user before implementing.
- If this skill is invoked from `saas-workspace-initializer` Step a2, the Round 1 discovery answers (jobs-to-be-done, candidate domains, MVP scope) are already available — use them and do not re-ask the equivalent questions below. Ask only what those answers leave open.
-
- ### Discovery Questions
-
- 1. **Domain Complexity**
- - "What are your core business entities and their relationships?"
- - "Which operations are most critical to get right?"
- - "Are there complex business rules or validations?"
-
- 2. **Bounded Contexts**
- - "Are there distinct business areas that use different terminology?"
- - "Which teams will own which parts of the system?"
- - "Do you have existing systems that define boundaries?"
-
- 3. **Scale & Performance**
- - "Which read operations are most frequent?"
- - "Are there operations that need eventual consistency?"
- - "Do you need audit trails for business operations?"
-
- ## DDD Building Blocks
-
- | Concept | Purpose | Example |
- | -------------- | ----------------------- | --------------------------- |
- | Entity | Identity + lifecycle | User, Order, Product |
- | Value Object | Attributes, no identity | Money, Address, Email |
- | Aggregate | Consistency boundary | Order + OrderItems |
- | Domain Event | Record what happened | OrderPlaced, UserRegistered |
- | Repository | Persistence abstraction | OrderRepository |
- | Domain Service | Cross-aggregate logic | PaymentProcessor |
-
- ## Project Structure
-
- ```
- libs/
- ├── [domain]/
- │ ├── domain/ # Core domain logic
- │ │ └── src/lib/
- │ │ ├── entities/ # Entities and aggregates
- │ │ ├── value-objects/ # Value objects
- │ │ ├── events/ # Domain events
- │ │ ├── services/ # Domain services
- │ │ └── repositories/ # Repository interfaces
- │ │
- │ ├── application/ # Use cases / application services
- │ │ └── src/lib/
- │ │ ├── commands/ # Command handlers
- │ │ ├── queries/ # Query handlers
- │ │ └── services/ # Application services
- │ │
- │ ├── infrastructure/ # Technical implementations
- │ │ └── src/lib/
- │ │ ├── persistence/ # Repository implementations
- │ │ ├── messaging/ # Event publishing
- │ │ └── external/ # External service adapters
- │ │
- │ └── feature/ # Controllers / presenters
- │ └── src/lib/
- │ └── controllers/
- ```
-
- ## Entity Pattern
-
- An entity has identity, a private constructor reached through factories, and
- business methods that guard their own invariants. It extends a shared `Entity`
- base that supplies identity equality and the domain-event buffer:
-
- ```typescript
- export class Order extends Entity<OrderId> {
- private constructor(
- id: OrderId,
- private _customerId: CustomerId,
- ) {
- super(id);
- }
-
- static create(customerId: CustomerId): Order; // new, raises OrderCreated
- static reconstitute(data: OrderData): Order; // rehydrate, raises nothing
-
- addItem(productId: ProductId, qty: number, unitPrice: Money): void;
- place(): void; // rejects an empty order
- get total(): Money; // derived, never stored
- }
- ```
-
- Read [references/entities-aggregates.md](references/entities-aggregates.md) for
- the full pattern — the `Entity` and `EntityId` base classes, the invariant
- guards, and the aggregate-root rules. Do not hand-roll a base class from this
- sketch; the reference version is the one to copy.
-
- ## Value Object Pattern
-
- A value object has no identity — two instances with equal contents are equal. It
- is immutable, validates in its constructor, and returns new instances from every
- operation. It extends a shared `ValueObject` base that supplies structural
- equality:
-
- ```typescript
- export class Money extends ValueObject<{ amount: number; currency: string }> {
- static of(amount: number, currency?: string): Money; // throws if negative
- static zero(currency?: string): Money;
+ If this skill is invoked from `saas-workspace-initializer` Step a2, the Round 1 discovery answers (jobs-to-be-done, candidate domains, MVP scope) are already available — use them and do not re-ask the equivalent questions. Ask only what those answers leave open.
- add(other: Money): Money; // all three reject a currency mismatch
- subtract(other: Money): Money;
- multiply(factor: number): Money;
- }
- ```
+ Load [references/discovery.md](references/discovery.md) only when domain discovery is still open.
- Read [references/value-objects.md](references/value-objects.md) for the
- `ValueObject` base class and further worked examples (`Email`, `Address`,
- `DateRange`, `Percentage`).
+ ## Warrant
- ## Decision Matrix: When to Use DDD
+ Score with the user before writing pattern code. Count the yeses:
- | Scenario | Recommendation |
- | ---------------------- | ----------------------------------------------- |
- | Simple CRUD | Skip DDD, use basic services |
- | Medium complexity | Use tactical patterns (entities, value objects) |
- | Complex domain | Full DDD with bounded contexts |
- | Multiple teams | Bounded contexts essential |
- | High consistency needs | Aggregates + domain events |
+ - Do entities have complex lifecycles? (draft → approved → fulfilled)
+ - Are there business rules that span multiple entities?
+ - Do different parts of the system use different terminology?
+ - Is there a need for audit trails or event sourcing?
+ - Will multiple teams work on different parts?
- ## Complexity Assessment
+ | Yeses | Do |
+ | ----- | -- |
+ | 0-1 | Simple services. Stop. |
+ | 2-3 | Tactical patterns only (entities, value objects). |
+ | 4-5 | Strategic DDD: bounded contexts, then the tactical patterns. |
- Before implementing DDD, assess with user:
+ Situations the score alone does not route:
- ```
- □ Do entities have complex lifecycles? (draft → approved → fulfilled)
- □ Are there business rules that span multiple entities?
- □ Do different parts of the system use different terminology?
- □ Is there a need for audit trails or event sourcing?
- □ Will multiple teams work on different parts?
- ```
+ | Situation | Do |
+ | --------- | -- |
+ | Simple CRUD | Same as 0-1. Skip DDD. |
+ | Multiple teams | Bounded contexts, even when the rest of the score is low. |
+ | High consistency needs | Aggregates + domain events. Load entities-aggregates.md and domain-events.md. |
- **Score:**
+ ## Rules that break silently
- - 0-1: Simple services sufficient
- - 2-3: Tactical DDD patterns
- - 4-5: Full strategic DDD
+ 1. Copy `Entity`, `EntityId`, and `ValueObject` from the references below. Do not hand-roll a base class.
+ 2. One aggregate per transaction; refer to other aggregates by id. Home: [references/entities-aggregates.md](references/entities-aggregates.md).
## References
- Load for detailed implementation:
-
- - [entities-aggregates.md](references/entities-aggregates.md) - Entity and aggregate patterns
- - [value-objects.md](references/value-objects.md) - Value object implementations
- - [cqrs-pattern.md](references/cqrs-pattern.md) - Command/Query separation
- - [domain-events.md](references/domain-events.md) - Event-driven patterns
- - [repository-pattern.md](references/repository-pattern.md) - Persistence abstraction
+ | Load | When |
+ | ---- | ---- |
+ | [references/discovery.md](references/discovery.md) | Domain discovery is still open. |
+ | [references/entities-aggregates.md](references/entities-aggregates.md) | Writing an entity, an identity type, or an aggregate. |
+ | [references/value-objects.md](references/value-objects.md) | Writing a value object. |
+ | [references/cqrs-pattern.md](references/cqrs-pattern.md) | Separating commands from queries, or a read path dominates. |
+ | [references/domain-events.md](references/domain-events.md) | Recording what happened, eventual consistency, or an audit trail. |
+ | [references/repository-pattern.md](references/repository-pattern.md) | Persisting an aggregate. |
+ | [references/layer-layout.md](references/layer-layout.md) | Organizing a domain library into folders. |