Domain-Driven Design with Protean
The Foundation
This is the simplest way to use Protean and the foundation for the other two approaches. Start here if you're new to Protean or building a straightforward application.
Overview
In the pure DDD approach, you model your domain with aggregates, entities, and value objects, then use application services to orchestrate use cases. Repositories handle persistence, and domain events propagate side effects to event handlers.
This approach follows the patterns from Eric Evans' Domain-Driven Design (the "Blue Book"). There are no commands or command handlers — application services receive requests directly and coordinate the domain logic.
Request Flow
sequenceDiagram
autonumber
participant API as API Layer
participant AS as Application Service
participant R as Repository
participant Agg as Aggregate
participant EH as Event Handler
API->>AS: Call use case method
AS->>R: Load aggregate
R-->>AS: Aggregate
AS->>Agg: Invoke domain method
Agg->>Agg: Mutate state, raise event
AS->>R: Persist aggregate
R-->>EH: Domain event dispatched
EH->>EH: Handle side effects
AS-->>API: Return result
- The API layer calls a method on an Application Service
- The Application Service loads the aggregate from a Repository
- It invokes the appropriate domain method on the aggregate
- The aggregate mutates its state and raises domain events
- The Application Service persists the aggregate back through the repository
- Domain events are dispatched to Event Handlers for side effects
- The result is returned synchronously to the caller
Elements You'll Use
| Element | Purpose |
|---|---|
| Aggregates | Root entities that encapsulate business logic and enforce invariants |
| Entities | Objects with identity that live within an aggregate |
| Value Objects | Immutable descriptive objects (Money, Address, Email) |
| Fields | Typed attributes on domain elements |
| Application Services | Orchestrate use cases with @use_case methods |
| Repositories | Persist and retrieve aggregates |
| Events | Record state changes as domain events |
| Event Handlers | React to events with side effects |
| Subscribers | Consume messages from external message brokers |
| Domain Services | Coordinate logic spanning multiple aggregates |
Guided Reading Order
Work through these guides in order to build a complete understanding of the DDD approach:
| Step | Guide | What You'll Learn |
|---|---|---|
| 1 | Compose a Domain | Register elements, initialize and activate your domain |
| 2 | Aggregates | Define your root entities and aggregate boundaries |
| 3 | Entities | Add child objects with identity |
| 4 | Value Objects | Model immutable descriptive concepts |
| 5 | Fields | Understand the field system and data types |
| 6 | Relationships | Connect entities with HasOne, HasMany, Reference |
| 7 | Validations & Invariants | Enforce business rules |
| 8 | Aggregate Mutation | Change aggregate state safely |
| 9 | Raising Events | Emit domain events from aggregates |
| 10 | Application Services | Orchestrate use cases with @use_case |
| 11 | Persist Aggregates | Save aggregates through repositories |
| 12 | Retrieve Aggregates | Load aggregates by ID or query |
| 13 | Event Handlers | React to domain events |
| 14 | Subscribers | Consume messages from external brokers |
| 15 | Domain Services | Coordinate cross-aggregate logic |
| 16 | Testing | Test your domain model and application layer |
When to Evolve to CQRS
Consider moving to CQRS when:
- Your read and write models have diverging requirements
- You need read-optimized views (projections) independent of your aggregates
- You want explicit, auditable commands representing user intent
- You need asynchronous command processing
- Your application would benefit from separating the write path from the read path
See the Architecture Decision guide for a systematic decision framework.