In this page, I will share the lessons learned from the book “Domain-Driven Design” by Eric Evans.
Domain-driven design is an approach to software development that emphasizes understanding the business domain and creating a model of the domain that can be used to guide software development. This approach has gained popularity because it can help developers build more effective and maintainable software systems.
Benefit of Domain-Driven Design
- To enable software development with RICH functionality. Otherwise, take MASSIVE investment of ad hoc development.
- Complexity in the heart of software has to be tackled head-on. To do otherwise is to risk irrelevance.
- Improve in code quality:
- The model, code can be interpret by understanding the model. Code expresses the concepts of a model.
- The vocabulary, a language that used by all team member.
- The distilled knowledge, a structured domain knowledge that drop irrelevant topics.
Domain-Driven Design require a strong Leadership
Leaders within a team who understand the centrality of the domain can put their software project back on course when development of a model that reflects deep understanding gets lost in the shuffle. Leader should able to recognize that developer focus on minor detail, but loose major concept.
During development, code reviewers focus on minor defect, but the software architect see those editor focus diminish the design, a major focus.
Focus on the business domain
Domain-driven design is all about understanding the business domain and creating a model that reflects the business concepts, processes, and rules. This means that developers must collaborate closely with domain experts, such as business analysts or subject matter experts, to gain a deep understanding of the domain. By focusing on the business domain, developers can create software that better aligns with the needs of the business.
Use models to guide development
Models are at the heart of domain-driven design. A model is a representation of the business domain, and it is used to guide software development. The model should reflect the business concepts, processes, and rules. Developers should use the model as a guide when designing the software, and they should update the model as the software evolves. By using models to guide development, developers can create software that better reflects the business domain. Below are key points in modeling your software:
- Binding the model and the implementation.
- Cultivating a language based on the model.
- Developing a knowledge-rich model.
- The model not just a data schema
- It’s contain behavior and enforced rules, it’s integral part to solve complex problem.
- Distilling the model: drop irrelevant topic as iteration goes.
- Brainstorming and experimenting, is the only way to get the 4 keys above.
The pitfall
Waterfall
What is waterfall: expert share to business analyst → business analyst digest and make model → model passed to developer. Why it failed: knowledge trickle in 1 direction, but does not accumulate. Why?
- Analyst has 1 input, and has no feedback from developer.
- No opportunity to learn from developers or gain experience with earlier version.
Solution: remove business analyst.
Agile, but no abstraction.
What is agile: Iterative on requirement gathering and implementation. Rapid feedback during iteration: developers show to expert → expert approve → developers ask what to do next.
However, developer not BUILD ABSTRACTION, developer develop in a MECHANICAL way. The result is a product that SHOULD DO, not PRINCIPLES behind it. Product can be USEFUL, but never arrive to POWERFUL new feature. Even programmer do constant REFACTOR, it will help to clean up the code, and reorganize, but never arrive to KNOWLEDGE-RICH design.
Solution: during iteration, when developer listen to expert, developer should seek for ambiguity and inconsistency. Once model completed, without ambiguity and inconsistency, developers should start abstracting the model.
DB as global object
Client code not using aggregate root for object traversal. Domain logic spread into query and client code. Entity and value object become a container rather than express the model. Eventually, model become irrelevant
Solution: use DB only for persistence purpose, not object repository. Use aggregate root for object repository.
Knowledge Leak
First, Code and document does not express hard earned knowledge. People come and go. When oral tradition interrupted, lead to knowledge lost.
Second, Absent of factory pattern. We can use car as analogy to OOP: Object creation = car instantiation → car factory responsible to instantiate car object.
Without factory, client need to know how to construct a car. Internal structure of a car will be leaked, even couple parameter in constructor, make car and it’s client coupled. If a domain need car object, and application layer instantiate the car, then knowledge leak ripple to application layer, make refactoring more expensive. Car client only know how to drive car, or you can say: interface as a contract.
Solution: pay attention to possibility to use factory pattern.
Documentation is easy to obsolete
Code is ultimate and detailed software specification and documentation. However, detailed thing not always clear to read. So you need helps of visualization (it could be UML diagram) and speech. But speech is easy to forget, you need to make it written.
Solution: This writing should support the code, not duplicate! This writing should explain the idea from the model design, not explain how the idea implemented. When it’s come to implementation, just show the code.
Key activities
Requirement gathering (iteration)
It’s not developer job to challenge the design from business expert. Developer ask and ask the expert to seek ambiguity and inconsistency. This activity will direct to more concise design. The developer goal is to make a rock solid basis for next step: make abstraction from the raw design. With abstraction, we will able to make generalization, that lead to knowledge rich design.
Create a shared language
To make iteration even smother, developer should use same term with expert. We call it ubiquitous language. Ubiquitous language exists in the model, design, code, and documentation. For example is term “transaction” in a banking application, or “itinerary” in cargo application. Those word have specific meaning in the respective industry. Make it explicit in the design, and it will lead you to a breakthrough, that lead you to even more solid design.
Constant refactoring
Make a supple design: angles of attack. Carve off subdomains, draw on established formalism, when you can.
Code review
Self-discipline to agree on the AGGREGATES and code consistently with them. This aggregate give us big benefit, for example in cargo application: we have copied everything inside the Cargo AGGREGATE boundary, we have made some modifications to the copy, but we have affected nothing outside the AGGREGATE boundary at all.
This disciplined achieved by the feedback loop through implementation. It’s required a lot of self-discipline as we use the UBIQUITOUS LANGUAGE of the domain model in discussions. Message of code using current standard technology requires discipline and a certain way of thinking about design.
Pay attention to factory pattern. Creation of object must atomic. Creation of entity, must include entire aggregate. Creation of value object, must in final state. Return as type of interface, not concrete.
Express model in software. Entity identified by ID only. Entity not always has their own ID, it could borrow ID from their parent, or aggregate root. Entity provide continuity so behavior is predictable and clear. When to add behavior: when its essential. When to add state: when required by behavior. Value object identified by one or more attribute(s). Value object has no history and continuity.
Pay attention to repository pattern. Simple interface to persists model. Decoupling application to persistence implementation. Communicate design of object access allow substitution for different persistence implementation. Let client handle transaction, not in repository.
Pay attention to explicit design. Use business term in the code. Put constraint into separate object. One of example is specification pattern. Another example is business rule.
Try to extract function from method. For example: CQRS: command query separation. It will give us side effect free function. For function with side effect, we call it command. Make explicit by put assertion on before an post execution, so programmer need not to infer meaning by examine code. Put unit test on both function and method, with intention revealing test case, so programmer need not to infer meaning by examine code.
Keep model stay current
In conclusion, Domain-driven design is a powerful approach to software development that emphasizes understanding the business domain and creating a model that reflects that domain. By focusing on the business domain, creating a shared language, using models to guide development, applying strategic design, and implementing tactical patterns, developers can create software that better aligns with the needs of the business and is more effective and maintainable over time.
Further reading
Learn Domain-Driven Design by Example.
Get in touch
If you’re looking for a software development partner that can create modifiable software that is cost-effective in the long run, look no further than Wirabumi Software. Contact us today to learn more about our services and how we can help your enterprise thrive.