Domain Driven Design: My Reading Notes

  • Facing a new system/project, usually the expertise is fragmented and spread all over different documents and the minds of different people, like puzzles.
  • At first, we don’t know “what we don’t know”, and such ignorance usually lead us to wrong estimations.
  • [Action] Cultivate a common language so the 2 groups of people, engineers and domain experts, can increase mutual understandings and shape the model together.
  • [Action] Distilling the model: Start from a naive model. During the iterations, abandon the unimportant or unfit parts and make new ones.
  • [Principle] Do not separate the ones who create models and the ones who write codes. Let people do both. Otherwise, (1) the intent of the model may be lost during the implementation; (2) the modeler will not sense the performance issues (or other system issues) directly.
  • [Principle] Use layered architecture. Extract a domain layer to achieve “separation of concerns”. So that, (1) later changes of UI won’t affect domain layer logic; (2) people can “read” domain knowledge in the same place, instead of spreading all over different components.
  • [Principle] Make micro-refactorings one at a time. Don’t seek for giant step, because a big refactoring causes a big impact to the system and the schedule. Instead, use small steps to accumulate deeper domain knowledge, and prepare for a breakthrough idea to come at some point. When it comes, find a proper timing in the project lifecycle to implement it. Chapter 8 provides a very expressive example.
  • [Principle] Intention-revealing interfaces: The name of a function or a module should explicitly reveal its role and design intention. So that the later joining developer will not mis-use it and introduce a different intention into the code.
  • [Principle] Make side-effect-free functions: Separate command (setters) and query (getters).
  • [Principle] In most cases, we build systems that will leverage existing legacy systems. When interfacing with legacy systems, the obvious effort is in the infrastructure layer: the other system may have different data types or protocols, and those are easy to notice. A common mistake is we usually neglect the differences in the domain layer, that the other system probably has a different model. For example, the same terminology in 2 different systems may refer to similar objects with different constraints.=> Use an anticorruption layer to translate the models between systems.
  • Entity: an object that we care about its identity.
  • Value object: an object that we don’t care about its identity.
  • Service
  • Constraint
  • Process
  • Specification
  • Aggregate: contains an entity as root, and the boundary to define which objects are included.
  • Factory: When an aggregate is too complicated, use Factory pattern to encapsulate its construction.
  • Repository: stores the objects/aggregates (created either by Factory or by simple constructor), and let users to find them through associations.