Book notes: Patterns, Principles, and Practices of Domain-Driven Design

This book has the responses to many questions I asked myself during the last year of exploring DDD. My team and I had te opportunity to discuss and apply many of the concepts and patterns and dynamics of this book in our recent projects. Definitely, give it a try is a recommended experience.

What is Domain Driven Design?

It’s a development philosophy, defined by Eric Evans, for managing the construction and maintenance of complex software problem domains.
The keys to this development style are:

  • Divide and conquer, this approach is based on the decomposition of the problem, and find ways to focus the resources in the more important parts, the core parts of the domain.
  • Collaboration, to create an appropriate environment to solve complex problem domains, it’s necessary a good collaboration between teams and domain experts.
  • Keep it simple. Keep the solutions as boring as you can. Enforce simply relations between contexts and entities. Make pragmatic use of patterns.

You can find two parts in DDD: Strategic and Tactical.

Strategic: The problem space

This is the most important part of DDD. Here you have to focus on the problem domain. The idea here is to explore the domain, and to reveal what is important, the core. To find this, we have to create a very well collaboration between domain experts and the Team.
In this collaboration, we find the ubiquitous language, as a common language between domain experts and developers teams. This will drive the modeling of the solutions. These models, its boundaries, and their relationship are discovered and evolved through this collaboration.

Some dynamics to explore the problem space, and work on the solution modeling are:

For new domains knowledge crunching:

  • Focus on interesting conversations.
  • Start from the use cases.
  • Ask powerful questions.
  • Synergy with domain experts, analysts, etc.
  • Discus using diagrams.
  • Use class responsibility collaboration (CRC) cards.
  • Do not put names to concepts in the model when there is too soon.
  • BDD.
  • Rapid prototyping.
  • Look on existing paper-based systems.

For existing models:

  • Understand the intents, find motivations behind requirements. Find real needs, not solutions or suggestions.
  • Event storming.
  • Impact mapping.
  • Business model canvas.
  • Deliberate discovery, devote time to learning about areas of the problem domain that you are ignorant about.
  • Model exploration Whirlpool.

To distill a problem domain and focus on the core domain:

  • Decompose the problem domain into subdomains, according to the capability of the business process and functionality of the system.
  • Identify the core domains: What are the parts of the product that will make it a success? Why are these parts of the system important? And why can’t they be bought off the shelf? In other words, what makes your system worth building? (competitive advantage), set the efforts here.
  • Treat the core domains as a product.
  • Identify the generic domains. Not core but the business can’t operate without them. Not focus efforts on them.
  • Identify the supporting domains. Not defining what your system does, help to support your core domains. They need to work but do not require your prolonged attention. As with the generic domains, if possible, you should look to buy off‐the‐shelf solutions. Maybe, can be a manual process.
  • Clean Boundaries over perfect models.
  • Build subdomains for replacement over build subdomains for reuse.
  • If there is any core domain, do not apply DDD.

Solution space: Domain Model

Traditional software processes keep the code model and analysis model separate, which leads to an implementation that rarely resembles the blueprint due to new insight and constraints of the technical solution. DDD acknowledges the need to produce a single model that serves as an analysis model for business people to understand and which is implemented using the same terminology and concepts in code.

It is formed first as an analysis model through the collaboration between a development team and business experts during knowledge‐crunching sessions. It represents a view, not the reality, of the problem domain designed only to meet the needs of business use cases. It is described in a shared language that the team speaks and the diagrams that the team sketches. When it is expressed as a code implementation, it is bound to the analysis model through the use of the shared language. Its usefulness comes from its ability to represent complex logic and polices in the domain to solve business use cases. The model contains only what is relevant to solve problems in the context of the application being created. It needs to constantly evolve with the business to keep itself useful and valid.

  • The domain is the problem, it is the reality of the situation.
  • The domain model is an abstraction of the problem domain, expressed as code implementation that represents a view of the problem. Not reality.
  • Build a domain model using the ubiquitous language, collaboration contract between domain experts and teams.
  • Model only what is relevant.
  • Focus on behavior, not in implementations.
  • Rebuild many times the domain model, probably you need to create it 3 times to get a good model.

Domain model implementation

Domain Model is an implementation detail of the domain layer.Implementation patterns:

  • Domain Model (pattern).
  • Transaction script.
  • Table module.
  • Active record.
  • Anemic Domain Model.

Keep in mind:

  • Split models based on subdomains.
  • Terminology and business capabilities define the bounded contexts limits.
  • Defined around language and aligned to business capabilities.
  • Contexts should be build around teams.
  • Bounded context boundaries can be influenced by terminology ambiguity.
  • Subdomains should be alignment to business capabilities.
  • Team organization and physical location.
  • Legacy codebase.
  • Third-party integration.

Relationships between bounded contexts:

  • Context Map, the big picture of relations between contexts Easy way to identify the core parts of the system, ownership and responsibility. Reveals areas of consufion. Adds many value to onboardings.
  • Anticorruption layer.
  • Shared kernel.
  • Open host service.
  • Upstream/Downstream Relationships.
  • Customer-supplier.
  • Conformist.

Application architecture

Isolate domain model from technical complexities like: persistence, presentation, etc. Separation between domain, application service and infrastructural layers:

  • Dependency inversion.
  • Comunication flow between layers from outside to inside.

Examples: Hexagonal architecture, Onion architecture, Clean architecture.

keep in mind:

  • Bounded contexts do not share the data model and the domain model.

  • The scope of architecture: If there are many bounded contexts in one Application, There is one architecture in each bounded context. Its possible to find different pattern in each domain layer.

Common problems for teams starting out with DDD

  • Overemphasizing the importance of tactical patterns.
  • Same architecture for all bounded contexts.
  • Striving for tactical pattern perfection.
  • Mistaking the building blocks for the value of DDD.
  • Focusing on Code Rather Than the principles of DDD.
  • Missing the real value of DDD, collaboration, communication, and context.
  • Producing a Big Ball of Mud Due to Underestimating the importance of context.
  • Causing ambiguity and Misinterpretations by Failing to Create a UL.
  • Designing Technical-Focused SOlution Due to a Lack Collaboration.
  • Spending too much time on what’s not important.
  • Making simple problems complex.
  • Applying DDD Principles to a Trivial Domain with Little Business Expectation.
  • Disregarding CRUD is an Antipattern.
  • Using the domain model pattern for every Bounded Context.
  • Ask yourself: Is it worth this extra complexity?.
  • Underestimating the cost of applying DDD.
  • Trying to succeed without a motivated focused team.
  • Attempting collaboration when a domain expert is not behind the project.
  • Learning in a noniterative development methodology.
  • Applying DDD to Every Problem.
  • Sacrificing pragmatism for needless purity.
  • The wasted effort by seeking validation.
  • Always Striving for Beautiful Code.
  • DDD is about providing value.

Selling DDD

  • Is not a silver bullet.
  • Won’t solve all your problems.
  • Powerful and extremely philosophy when used in the correct.

keep in mind that is required:

  • A skilled, motivated, passionate team is eager to learn.
  • A nontrivial problem domain that is important to your business.
  • Access to domain experts who are aligned to the vision of the project.
  • An iterative development methodology.

Applying DDD

  • Replay your model.
  • Making implicit explicit.
  • Keep the big picture in mind, and the value in the focus.

Communicating between bounded contexts

  • Bounded contexts should be autonomous.
  • You can start make separation using: namespaces, projects and solutions.
  • Physical boundaries enforce clean models. Separation of persistence, code repository, components of deployment etc.

Legacy systems integration

  • Bubble context.
  • Legacy systems as services.

Distributed Bounded contexts

  • Database Integration.
  • Flat file integration.
  • RPC, this is adds more cost to scale, and resilence. Its the most tight coupling.
  • Messaging.
  • REST.

Keep in mind:

  • Favour eventual consistency over distributed transactions.
  • Event-driven reactive DDD: more resilence, less logical, and temporal coupling, best easy to scale. But adds much more complexity.
  • RPC Still relevant approach, but depends on the context.
  • SOA and Reactive DDD makes more scalable, fault-tolerant distributed systems.
  • Applying SOA means view your bounded contexts as SOA Services.
  • Decompose bounded context into business components, based on business responsibilities.
  • Decompose business components into components. Applying the single responsibility principle.
  • Architecting with components makes that your business can invest in further resources, infrastructure, and better strategies only on necessary components.
  • This components a.k.a. autonomous components (Udi Dahan), or microservices.
  • Messaging patterns like: messaging gateway, messaging bridge can be found here or in the book.
  • Each bounded context can store the data they need.
  • Messaging should include message versioning.
  • RPC works over HTTP, Clasic RPC evolved to SOAP and REST.
  • Important to well understand the values behind REST: Resources, Hypermeddia, Stateless.

Tactical: The solution space

The objective in the solution space is to create the domain model.

Domain Model Building Blocks

Model:

  • Is not the real world.
  • Is not the data model.
  • Its the domain behaviour.

Value Objects

  • Value equality.
  • Context-Dependent.
  • Behavior-Rich.
  • Cohesive.
  • Inmutable.
  • Self-Validating.
  • Identity-less.

Entities

  • Identity equality.
  • Invariants validation.
  • Context-Dependent.
  • Behavior oriented.

Domain Services

  • Stateless.
  • Process Oriented.
  • Avoid anemic domain models when overusing them.

Modules

  • You can take into account the modules and namespaces when you are designing this models. And the relations between them.

Lifecycle:

Aggregates

  • Consistency boundary.
  • Build around invariants, not relationships.
  • One-Way relationships in modeling.
  • Build around business use cases, not real life. Avoid this TRAP.
  • Concepts in the solution space.
  • Ids over objects references.
  • Design consistency. Persistence and concurrency feedback.
  • Aggregate root: All communications through root entity.
  • Domain objects only exist as a part of an aggregate, as a part of a conceptual whole.
  • Reference isolation boundary.
  • Versioning can solve some concurrency problem.

Repositories

  • Aggregate Root repositories has only getById and add/save methods.
  • Query hydration/persistence synchronization entry points.
  • Transactional behavior.

Factories

Using factories will improve your code legibility and you will enfacy the isolation and complexity of complex models creation.

New Patterns (events):

Domain Events

  • External events.
  • Internal events.
  • Event Sourcing: projections, snapshots. temporal queries, and competitive advantage vs complexity.

Application User Interfaces

Based on bounded context Owning:

  • Autonomous.
  • Authoritative.

Based on user interface call owning:

  • Client-side composition.
  •  Server-side aggregation.

Architectural recommendations based on context:

  • Clean/Ports and Adapters.
  • CQRS.
  • Communication based on messages.
  • Event sourcing with CQRS.

Application Services:

  •  Command processor.
  •  Publisher subscriber.
  •  Request/Reply.
  •  Asynchronous patterns.