A while ago a took the “Implementing DDD” course with no less then Vaughn Vernon (@VaughnVernon) and Patrik Fredriksson (@weakreference) at Citerus. Vernon has written the book with the same name as the course.
Here is my notes from that course.
Disclaimer: This is my notes and my interpretation of the information. I can really recommend going the course.
Day 1
Part 1: Introduction
The expectations was high and after a short round robin introduction of the participants we were ready to go.
Vernon presented what will be in the course by taken a short introduction to Aggregates, Repositories, Services, Domain events, Entities and Value objects.
We were going to hear about how we can integrate the different context with each-other without increasing the complexity. How to use model naming strategies when modelling it by the ubiquitous language.
For those who was unfamiliar with domain events, since it’s not explicit mentioned in [Evans] book, here is a short introduction.
Domain events are created by the aggregate and published to the event publisher that delivers it to the subscribers. This can be seen as a publish-subscriber pattern or observer.
Benefits with domain events:
- Domain events can be used to keep a record of what have happen in the domain.
- Domain events can also ease the integration between different (sub-) domains.
Part 2: Bounded Context and the Ubiquitous language
This is DDD. The bounded context and the Ubiquitous language is the most important part of DDD. The thing to take with you from this day is the Bounded Context and the Ubiquitous language. It is the foundation of DDD.
Observe that expert thinks of the domain and usage while developers tend to thinks of code and technical stuff. So have a feedback loop between the developer and the expert.
Remember to always involve the Experts!
So what can be inside a bounded context?
- Domain model
- Service interfaces: RPC (SOAP), Rest, Messaging
- User interface components
- Application services/command handler
- Database schemas
The word domain is somewhat overloaded (which domain are we talking about). The Domain in the whole business that is helping the business forward. The parts that builds up the domain are sub-domains.
To map the domain you must start with defining the problem space and solution space. The problem space is the context, and the solution space is the domain.
Start with defining what your core domain is. The Core domain is the sweet-spot of our system. The core is often supported by sub-domains. But your core domain can be another teams sub-domain. So there can be different perspective in the view of the domain between different teams.
Remember that multiple sub-domains can belong to the same bounded context. But try to only have one bounded context per (sub-) domain.
Try to keep your core domain as clean as possible using the single responsibility principle. Try to identify possible sub-domains that can be moved out of the core domain. This can be done using linguistics to identify sub-domains (terms that do not fit in the current core domain).
Always remember that context is King! You need to know your context to know the meaning if the model.
To handle legacy system aka BBoM (Big Ball of Mud). Try to map sub-domains to the BBoM (Big Ball of Mud) to visualize what’s there. This is a great way to visual the vision of how we want the end result to be (even if it’s not possible to reach in polynomial time).
Part 3: Context mapping
Use context mapping to describe the relation between different contexts. Especially defining what are upstream and downstream in the relation. Upstream serve the downstream, think of it as a river metaphor, making the downstream dependent of what the upstream produces and offers.
Partnership
- Upstream and downstream dependencies are equal
- No one can create value on there own
- If one fail all fails
Shard kernel
- A shard (sub-) domain between different teams
- Needed by more the one team
- Hard to maintain
Customer-supplier
- Upstream can create value of there own
- Upstream supplies the downstream
- Downstream must request changes from the upstream team
- Downstream must conform to upstream changes => conformist or different ways
Conformist
- Downstream must conform to upstream changes and upstream leaves the downstream helpless
Anti-corruption layer
- Translate other system model to match your needs
- Isolate your domain to prevent leaking of other domains
Open-Host service
- A protocol published to let you talk to other services
Published language
- A common translation between two (or more) bounded contexts
- Often combined with an Open-Host service
Separate ways
- Create your own model and leave the other context/sub-domain
Big Ball of Mud
- The name says it, stay away from it if possible
Part 4: Architecture
- Classic architectural styles
- Enduring architecture
Layers
Using the dependency inversion principle to manage the call hierarchy and loosen the coupling to other parts such as storage. This can be done by defining the persistence storage access as interface in the domain and placing the actual implementation on top of everything by using DI.
Hexagonal (Ports and adapters)
Define input and output ports to communicate on. Use one port per client type and let the application layer handle the conversion and calls to the domain services.
CQRS
Separate query and command (view and action). Making every command doing exactly one thing. Retrieve the new view to see the changes making the model having separated write and read. The command should never change the state directly of the part that is retread when reading.
Pipes and filters
Look at it as pipes in *nix systems. Where every command is a filter that changing the data and every pipe forwards the result between the commands. In the same way we can implement a system where every filter do computations on the data and the pipes sends it to the next filter.
Actor model
Messaging between aggregates (See Akka (JVM), Scala (JVM) or ActorFX (MS)).
Think of it the internet where every computer is an actor. Every actor can communicate with every actor. The actor state can be recreated form all events that it has received and the events are persisted when the event is received. This makes the model robust when the actor can be recreated at any time to receive a new event.
Quote of the day
In short, code what you mean.
Tell, don’t ask objects what to do.
Use roll-based access to the system.
Further reading
Responsibility-Driven Design by Rebecca J. Wirfs-Brock
Dependency inversion principle by Martin Fowler
Actor Frameworks:
Akka and Scala for the JVM and ActorFX for the MS platform.
Day 2
Part 5: Entities
- Using DDD tactical design
- Avoid Entity-think and entity overuse
- Unique identity and construction
- Designing intrinsic qualities and characteristics
Use entities when:
- An identified instance may change over time
Entity-think = bad
Don’t think of every thing as an entity
Unique identity
- User provides an identity (must ensure its uniqueness)
- Application generates the unique identity
- Persistence store generates an unique identity
- Another bounded context provides the unique identity
Order can matter when the entity gets the unique identity.
Early
The identity is provided by the user or application. It can be managed with a surrogate identity to prevent inconsistency in the persistent storage.
Late
The identity is given to the entity when it is stored in the persistence storage.
Validation
- Validate properties in setters.
- Validate whole object can be done using a special validator eg. ProductValidator.
- Validate deferred may involve other objects eg. single object is fine but the whole model fails due to some constraint.
Part 6: Value Objects
- DDD Tactical design prefers value objects over entities
- The common characteristics of value objects
- Integrate with minimalism and design standard types
- Persistence considerations
Value-think = Good
Choosing between a Value object or entity depends of the context but favour Value Object over Entities.
Immutable and Replaceable
Private setters for validation, public “getter” but ignore the get part and express the getter with the ubiquitous language.
Use methods for modifications but return a new object instead such as the Javas String substring method.
Part 7: Services
- Domain services are not SOA or Application services
- When to model a Domain service
- Sample Domain services
Domain services may
- Dispatch to the model
- Be double-dispatched by and from the model
Domain services in the architecture
Domain services is
- Lightweight
- Stateless operation
- Not transactional
Needed if
- No natural operation home
- Force-fit on entity
- Wants to be static
Part 8: Domain events
- Collecting the facts about your domain by use of events
- Publish events in your application and beyond
- Modelling events and event-driven modelling
When and way
Listen for:
- When
- If it happens
- Notify me
The end of Batch? It can reduce the batch window when we gets notifications of when things happens in the system.
- PubSub
- Acronym for Publish Subscribe
Facts forever (event sourcing).
From the events that are handled you can create a glossary over the events.
Bounded Context | Event Name | Origin | Description |
Authorization | Login | Authentication | Dispatched when a user log-in into the system. |
Further reading
Checks pattern language by Ward Cunningham
Day 3
Part 9: Modules
- Modules do’s and don’t – Practical guidelines
- High level modules and modules in the model
- Other areas of the application
Simplest form Java packages and C# namespaces
Do
- Design modules to fit modelling concepts
- Typically a model for one or a few aggregates that are cohesive, if only by reference
- Name modules per the ubiquitous language
- Design loosely coupled modules
- Strive for acyclic dependencies between modules
- Relax the rules a bit between child and parent modules
Don’t
- Create modules mechanically according to a general component type of pattern
- Make modules a static concept of the model, but allow the to moiled with the objects
The name of the module should be something useful but not to long
eg.
com.sassovation.identityaccess.domain.model
com.sassovation.agilepm.domain.team
<< service >>
com.sassovation.agilepm.domain.team.MemberService
<< aggregate root >>
com.sassovation.agilepm.domain.team.ProductoOwner
com.sassovation.agilepm.domain.team.Team
com.sassovation.agilepm.domain.team.TeamMemeber
com.sassovation.agilepm.domain.product
<< aggregate root >>
com.sassovation.agilepm.domain.product.Product
com.sassovation.agilepm.domain.product.backlogitem
<< aggregate root >>
com.sassovation.agilepm.domain.product.backlogitem.BacklogItem
com.sassovation.agilepm.domain.product.release
<< aggregate root >>
com.sassovation.agilepm.domain.product.release.Release
Other areas of the application
com.sassovation.agilepm.resources // Rest
com.sassovation.agilepm.resources.view // UI
com.sassovation.agilepm.application.product // Application services and commands
com.sassovation.agilepm.application.team
com.sassovation.agilepm.application.tenent
Modules before creating a new bounded context
Part 10: Aggregates
- Part 1: Common pitfalls of aggregate design
- Part 2: Follow the rules of aggregate domain (mostly) design
- Part 3: Reimplementing the BacklogItem design
Every aggregate have a root entity. The root entity must have global unique identifier.
By design every aggregate must be transactional.
Part 1 – Common pitfalls
- Focus on object graphs and navigational convenience
- Imagine incorrect invariants (consistency constraints)
- Tune persistence to support previous two pitfalls
Large cluster aggregate object graph.
Positive: Navigational convenience for model client
Negative: Transactional failures
Negative: Size and performance
Incorrect invariants
Invariants say: When this object changes that object must also change.
Imagine false business constraints
Aggregate design are influenced by wrong assumptions often lead to large-cluster aggregate problems.
Force persistence mapping to fit large-cluster aggregates. Enabling large-cluster aggregates leads to some issues still leads to performance and scalability issues.
Aggregate boundaries are for transactional reasons. So persisting a aggregate is a atomic action.
Rule: Model true invariants in consistency boundaries.
Aggregate boundaries for invariants
Rule: Design small aggregates
This is not small a small aggregate.
Use Eventual consistency to manage a situation where an aggregate might affect another aggregate.
Part II
How many aggregates are there in this picure?
Rule: Reference by identity
Should aggregates use repositories?
Rule of thumb: No.
Rule: Use eventual consistency outside the boundary [Evans, p128]
Ask who’s job it is
Transactional or eventual consistency?
Is it the job of the user – Transactional
Is it the job of another user or system – Eventual consistency
Reason to break the rules
- User interface convenience
- Lack of technical mechanisms
- Global transactions
- Query performance
Adhere to the rules and only break them with a good reason.
Part III
Model the BacklogItem in the sassovation agilepm application. See the book.
Part 11: Factories
Motivations for using factories in the model. See the book.
Part 12: Repositories
- Collection oriented repositories
- Persistence oriented repositories
- Look at sample repositories
Collection oriented eg. java collection (Set). Can be used with a document based database eg. MongoDB.
Use one repository per aggregate. And try to not call repositories from aggregate, use a domain service instead.
Part 13: Integrate the bounded context
If all context provides an open host service through eg. REST it is easy to integrate other context with that one using a small anti-corruption layer.
See the book and look at the example code for more information.
Part 14: Application
Designing the application with regards to
- Application without architecture
- User Interface
- Application services
Common things to think about.
- Render DTO from aggregate instance
- Use a mediator to publish aggregate internal state
- Render aggregate instance from domain payload object
- State representation of aggregate instance
- Presentation model
- Use-case and optimal query (CQRS)
- Using commands
Workshop: Walk-through of parts of the example code
Futher Reading
Programming Pearls, second ed. by Jon Bentley
Life Beyond Distributed transactions by Pat Helland
Example code by Vaughn Vernon
Idempotent = Side effect free (wikipedia).
Question
To implement a Event Sourcing model (to be extended as an actor model). How must I store the events to let them recreate the state of the aggregates?
Answer
You have to store each event together with the id for the root entity for the aggregate. Use this id as a key to retrieve all the events that has affected the aggregate.