Loading...
Loading...
Marco Franssen

While applying domain-driven design (DDD) together with Command Query Responsibility Segregation (CQRS), I learned some valuable lessons about domain boundaries, responsibilities, and testing.
I took an agile approach using Scrum. I started by breaking the use cases into smaller user stories, then used those stories to plan the first sprint.
In that first sprint, I hadn't designed the complete domain. I wasn't sure I'd chosen the right aggregate roots and bounded contexts for the work ahead. In the second sprint, I realized those choices weren't right. That sprint involved so much refactoring that I didn't get to the new user stories and functionality.
1. My first lesson: before applying DDD, identify at least your domain's bounded contexts and define your aggregate roots. This can save a lot of refactoring and gives you an overview of the project.
Once I'd defined my bounded contexts and started implementing the designs, I discovered that I'd assigned some responsibilities to the wrong aggregate roots (ARs). Here are the lessons I learned.
2. Tell, don't ask. This reduces complexity and dependencies in your design. Use a domain service to tell one AR what it needs to know about another AR to make its decisions.
3. You can use read models for validation in these domain services to keep complexity low. Don't hesitate to relax consistency where strict consistency isn't important.
To keep complexity low, I made myself avoid properties in my implementation. The first question that came to mind was: how can I check whether the conditions are right if I can't access them through properties? The solution was simple.
4. Use a guard method on the object that can make the decision, and pass the dependencies to that method. If all conditions are met, do nothing; otherwise, throw an exception.
5. Avoiding properties helps you put logic in the right place and prevents you from creating a "God" object or class.
Last but not least, I found dependency injection essential for keeping dependencies low and unit-testing domain scenarios. Because my unit tests execute commands and check the resulting events, I rarely have to rewrite them when refactoring the domain. The components affected by those changes are my CommandExecutors, which are injected into the framework in both production and test environments.
After just a few weeks of applying DDD and CQRS, I felt I'd become a better architect by changing my mindset. I was impressed by how simple I could keep the code while solving problems that had initially seemed complex.
By the way, I'm using the ncqrs framework. You can get a version I found stable enough from my fork on GitHub. Although the framework is far from perfect, it saves a lot of work. Anything that doesn't fit can be changed because the source code is open source. It's also fun to participate and share ideas for improvements. Don't hesitate to try this kind of framework: some issues only become apparent when you apply it to a real project. I think that's the best way to identify pain points and improve these frameworks.
I hope you enjoyed this post. Feedback and additions are welcome, because learning never ends. Finally, I'd like to thank the people who helped me change my mindset.
Marco Franssen
Build country, state, and city dropdowns with Knockout.js observables and jQuery JSON requests, with an ASP.NET MVC 3 server-side example.
Marco Franssen
How the order of FakeItEasy call configurations affected my CQRS domain tests when aggregate roots prepared the event store before an edit.
Marco Franssen
Configure Castle Windsor dependency injection in ASP.NET MVC 3 with an installer, controller factory, constructor injection, and repository disposal.
Marco Franssen
My notes from Greg Young's DDD/CQRS course on aggregates, entities, value objects, command-query separation, and consistency through event merging.