DDD

DDD

Mykhailo Spryniuk

DDD: Domain Driven Design - approach to building software with a focus on the domain

This article is a content list that explains the concepts and realization of DDD in projects


What is Domain?

Domain in a broad sense is what an organization does and the world it does in it. A business identifies a market and sells products and services.

When referring to just one area of the business, generally it qualifies as Core Domain, Subdomain, etc.

Domain vs Bounded Context

Domain has both a problem space and solution space. The problem space enables us to think of a strategic business challenge to be solved, while solution space focuses on how we will implement the software to solve the problem of the business challenge.

The problem space is the parts of the Domain that need to be developed to deliver a new Core Domain. Assessing the problem space involves examining Subdomains that already exist and those that are needed. Thus your problem space is the combination of the Core Domain and the Subdomains it must use. The Subdomains in the problem space are usually different from project to project since they are used to explore a current strategic business problem. This makes Subdomains a very useful tool in assessing the problem space. Subdomains allow us to rapidly view different parts of the Domain that are necessary to solve a specific problem. 

The solution space is or more Bounded Contexts, a set of specific domain models. The Bonded Context is used to realize a solution as software.

A Bounded Context is principally a linguistic boundary. The meaning of each concept in each Context is certain.

For example, An account can have different meanings for banking context and literary context.

Context maps are a tool for describing the mapping between two or more existing Bounded Contexts. It contains contexts that are involved in the project and integration relationships between them.

Bounded context relation types:

Partnership - Bounded Context teams coordinate the planning of development and joint management of integration. Teams must cooperate on the evolution of their interfaces to accommodate the dev needs of both systems. Interdependent features should be scheduled so they would complete for the same release.

Shared Kernel - When one BC(Bounded Context) team shares the subset of the own domain model. Keep the kernel small. Explicitly shared stuff has a special status and shouldn't be changed without consultation with the other BC team.

Customer-Supplier - When two BC teams are in an upstream-downstream relationship. Downstream priorities are based on upstream team planning.

Conformist - When two dev teams have an upstream-downstream relationship and the upstream team has no motivation to provide for the downstream team needs. The downstream teams eliminate the complexity of translation between BC and strictly adhering to the upstream team model.

Anticorruption-layer - facade or adapter between different subsystems that don't share the same semantics. This layer translates requests that one subsystem makes to other subsystems (Trivial example: repository is anti-corruption layer between DB and business-logic layer: business-logic -> (repository as anticorruption-layer) -> Db)

Open-Host Service: define a protocol that gives access to your subsystems as a set of services. Can be implemented as REST-based resources that client Bounded-Context interact with

Published language: translation between the models of two Bounded Contexts requires a common language. Often combined with Open-Host Service

Separate Ways - If two sets of functionality have no significant relationship, they can be completely cut loose from each other. Declare a BC to have no connection to the others at all, to find simple, specialized solutions within the small scope

Big Ball Of Mud - There are parts and systems where models are mixed and boundaries are inconsistent. Do not try to apply sophisticated modeling within this Context.

Domain events

Domain event - something happens that domain experts care about. Model information about activity in the domain as a series of discrete events. A domain event is a full-fledged part of the domain model, a representation of something that happened in the domain.

When events are delivered to interested parties, in either local or foreign systems, they are generally used to facilitate eventual consistency. It can eliminate the need for two-phase commits (global transactions) and support the rules of Aggregates.

One rule of Aggregates states that only a single instance should be modified in a single transaction.

Domain events modeling suggestions:
  • A timestamp that indicates when the Event occurred
  • Event behavior properties (status, tenantId, etc.)
  • Identity (especially when we need to compare events or events outside of local BC)
Domani events consistency:

There are three basic ways of eventual consistency:

  • Domain model and messaging infrastructure share the same persistence store. This approach will allow the changes to the model and the insertion of the new message to commit under the same local transaction. It has a disadvantage that the messaging system storage areas must reside in the same database as your model. This is not a viable option if the choice of model store and your messaging mechanism's store cannot be shared
  • Domain model's persistence store and messaging persistence store are controlled under a global, XA transaction (two-faced commit). This has the advantage that you can keep model and messaging storage separated from each other. It has the disadvantage that global transactions require special support, which may not be available for all persistence stores or messaging systems
  • Create a similar storage area to option 1, however, this storage area is not owned by your messaging mechanism but instead by your own Bounded Context known as EventStore. An out-of-band component that you create uses the Event Store to publish all stored, unpublished Events through the messaging mechanism. This has the advantage that your model and your Events are guaranteed to be consistent within a single, local transaction. It has the further advantages that are characteristic of an Event Store, including the ability to produce REST-based notification feeds. This approach allows the use of a messaging infrastructure whose message store is completely private. Given that a middleware messaging mechanism can be used after Event storage, this approach has the disadvantage that the Event forwarder must be custom-developed in order to send through the messaging mechanism, and that clients must be designed to de-duplicate incoming messages
Domain event forwarding architecture styles
  • Publishing notifications as RESTful resources
  • Publishing notifications through Messaging Middleware
Aggregates

Aggregates rules:

1) Model true invariants in Consistency Boundaries:

An invariant is a business rule that must always be consistent. By consistency we mean transactional consistency - transaction is immediate and atomic. For example, we have an invariant represented as c = a + b, therefore when a is 3 and b is 2 according to that rule if c is anything but 5 (ни что иное как 5) invariant is violated

A properly designed aggregate is one that can be modified in any way according to its business invariant completely consistent within a single transaction.

transactional consistency means that an aggregate is guaranteed to be consistent and up to date at the end of business action

2) Design small aggregates

Large aggregate example:

First of existed problems are possible transactional issues: For example when user1 requests a Product of version 1 and user2 requests a Product of version 1. After changes from user2 Product, the version was increased to 2. When user1 trying to save his changes, his commit fail because it was based on version 1

The second problem is related to performance and scalability. For example, some project has multitenancy architecture and with the fact that project growth it contains a lot of tenants and each tenant has a lot of data related to these aggregates. So a the time when you need to update any aggregate object it should load to memory all related objects.

So to avoid such problems we can use a better design of aggregates: make it a smaller

What does it mean to "make it smaller"? Limit the aggregate to just the Root Entity and a minimal number of attributes /or Value object properties. There is no proper size of an aggregate. The correct minimum is many are necessary, and no more.

3) Reference other aggregates by Identity

4) Use eventual consistency outside the boundary


Application Service vs Domain Service

We should strive to push all business domain logic into domain model, whetever that be in Aggregates, Value Objects, Domain Serivces. Keep Application Service thin, using them just to coordinate tasks on the model.

Report Page