Domain-Driven Design — Tactical Design

Masoud Chelongar
8 min readDec 14, 2022

--

Isfahan, Iran — Photo by erfun ghodoosi from Unsplash

Overview

As I explained in my first article about Domain-Driven Design, DDD can be categorized in two main groups, Strategic Design and Tactical Design. On the contrary to Strategic Design that deals with "Why" in DDD, Tactical Design covers "How" by introducing the concepts below:

▹ Business Logic Implementation Patterns

▹ Bounded Context’s Architectures

▹ Bounded Context’s Communication Styles

Business Logic Implementation Patterns

Consider a software that has more than one subdomain and each subdomain has different level of technical complexity and effect on the business and company goals.

Respecting effect and value of subdomains, we can have variant pattern to implement the business logic and deliver the software features in proper way.

Transaction Script Pattern

The transaction script pattern tries to divide the business logic dedicated to the bounded context into procedures or transactional behavior.

Martin Fowler introduces transaction script as below:

It organizes business logic by procedures where each procedure handles a single request from the presentation.

One of the most famous examples of this pattern is the ETL (Extract, Transform, Load) operations which extracted data after transformation (cleaning, sanitizing and scrubbing), will be loaded into the destination.

ETL Transformation

Because of simplicity, this pattern is the first choice for supporting subdomains because their business logic is simple to implement.

Active Record Pattern

The Active Record is used when the business logic is simple, the same as transaction script pattern. What makes this pattern different from the previous one, is the scope of its operation. It is engaged with more complex data structures that could not be operated via simple transaction pattern appropriately.

The most relevant example of Active Record is such transitions that support the database accessibility and mostly CRUD operations.

Domain Model Pattern

Business problems are not always simple to model by active records or transaction script patterns. Tackling the real world business domains and providing the suitable solution for them requires the more advanced and comprehensive pattern.

Let's assume that our client asked to implement the online retailer software. To cover the defined requirements (functional and non-functional) like SLA, extensibility and security, we can not use only two mentioned patterns above.

Domain Model, the pattern to model complex business problems, is the combination of two separate and complementary descriptions:

  1. Domain: It is the business related word and represents the problem that we want to solve.
  2. Model: It is the process map that shows the solution for the business problem inquired by domain.

Martin Fowler describes Domain Model in his book as below:

A domain model is an object model of the domain that incorporates both behavior and data.

Tactical Design Pattern consists of components or building blocks that we are going to learn them in this section.

Value Object: It is an object whose its values are the definition of its identity. For example color object can be defined by RGB factors or the object that describes a house has values like street and zip code that they are inseparable from its description.

▹ Entity: In the contrary to the value object, an entity is an object that has explicit identification but its state could be change over the time. The trivial sample is a person that has name, age and ID. The age and even name can be changed over time but the person ID remains the same.

▹ Aggregate: It is a group of objects (value objects and entities) that are gathered together in a single cluster. An aggregate is an state machine that is responsible for maintaining all the business invariant. The only way to access aggregate is an entity that is defined as interface or root entity.

Aggregate and its building blocks

▹ Domain Event: It is a message that describes events that has occurred in business domain or aggregate. Aggregates publish its domain events via root entity. An example of event domain is the produced event by CDC Debezium when relational database is updated or changed in general.

Domain Event

Bounded Context’s Architectures

There is different ways and methodologies to define communication between components inside bounded context. These techniques, that we name them Architectural Patterns, indicate the solutions to implement requirements, functional and non-functional, in code base.

The correct decision to choose the architectural pattern is crucial because it will affect directly the business domain and architectural decisions in short-term and long-term respectively.

In this section we discuss 3 main application architectural patterns as here:

  1. Layered Architecture
  2. Hexagonal (Port & Adapter) Architecture
  3. Command-Query Responsibility Segregation (CQRS) Architecture

Layered Architecture

This pattern is named layered architecture because it represents the bounded context (code base) is three separate and independent layers in the classic form.

Each layer can and only can communicate with one layer above and under itself. On the other words, directly communication of the mostly upper layer (presentation) with the lowest layer (Data Access) is not possible and it MUST be done via business logic layer.

Layered Architecture

▹ Presentation Layer: This layer is responsible for customer interaction and user interface implementation. For example in console applications the command line interface (CLI) is responsible for user interaction and in web applications, graphical user interface (GUI) performs it. The other methodologies that can act as presentation layer is APIs or gateways.

▹ Business Logic Layer: In most software that use layered architecture, this part is named core and it is the place that the business logic and requirements are implemented. The patters that were described in previous section as business logic patterns like active record and transaction script would be implemented in this layer.

▹ Data Access Layer: In classical form of layered architecture, it is responsible for database management. But nowadays and in newer definition of data access layer tries to handle data persistence when there are multiple types of data and databases (SQL, NoSQL, …).

Port & Adapter (Hexagonal) Architecture

Hexagonal Architecture is the developed version of layered architecture pattern. There is no any reason behind choosing hexagon for this architectural pattern. As illustrated in figure below, it consists of 3 nested hexagons:

  1. Infrastructure
  2. Application
  3. Business Logic

Strictly speaking, the business logic layer that represents high-level module is independent from infrastructure layer that is low-level module. Furthermore, dependency inversion principle is the key feature of port & adapter architecture.

Port & Adapter or Hexagonal Architecture

▹ Business Logic Layer: It our domain models that are wrapped by application layer. On the other words, application layer is acting as an moderator for domain models in business logic layer.

▹ Infrastructure Layer: As in layered architecture the upper layer (presentation) and lower layer (data access) are independent from business logic and represent the system infrastructure, in this pattern are packed together to the outer layer and named infrastructure layer.

▹ Application Layer: The layer to implement use-cases.

▹ Port: To access business logic layer and use-cases in application layer, we need to develop ports. They help to avoid calling infrastructure components directly by business logic layer. They act as an interface in hexagonal architecture in both ingress and egress way.

▹ Adapter: The corresponding port implementation in infrastructure layer to handle different types of technology stacks such as database and cloud computing is named adapter. On the other words, they are plugins that provide specific in/out format for outside of the hexagon.

One of the most famous hexagon architecture is microservice.

Command-Query Responsibility Segregation (CQRS) Architecture

Not only in our business, but also in all real world projects there is no any perfect model as a single business domain model to satisfy all domain's requirements. Furthermore, a single type of database and data type can not state the system requirements as well and polyglot persistence model that can cover different databases would be necessary.

Regarding mentioned limitations to define a model for a business domain, Greg Young suggested CQRS pattern that can help us to overcome this challenges by separating the responsibilities of domain in command (command execution) model and read model.

CQRS is described by Martin Fowler as below:

At its heart is the notion that you can use a different model to update information than the model you use to read information.

CQRS — Source[Martin Fowler]

▹ Command Model: This is a single model (the only reference) that modifies the system state by executing operations such as rule validation by policy engine.

Vlad Khononov describes command model responsibilities as following:

This model is used to implement the business logic, validate rules, and enforce invariants

▹ Read/Query Model: The defined system models to provide data and information in different types and formats for other systems and users with various data requirements.

Vlad Khononov describes read model as following:

A read model is a pre-cached projection. It can reside in a durable database, flat file, or in-memory cache.

It is suggested to separate these models in 2 independent models and update the query module based on execution commands in command module. In contrary to the query model that is single and only model to make change in the system, we can have more than one read model.

CQRS — Source[Learning Domain-Driven Design]

Last but not least, we can update the read/query model in two ways, synchronously and asynchronously that is described briefly in next section.

Bounded Context’s Communication Styles

Information transmission between bounded contexts , in one hand, could be one of the most challenging part of Domain-Driven Design because of its direct effect and efficacy on non-functional requirements. On the other hand, it could be most obvious and conspicuous section regarding clearly defined communication styles.

Synchronous Communication

When a bounded context tries to communicate with the others in a synchronous manner, it means it waits for response to continue its defined procedure and does not do any action before receiving a feedback from called bounded context. One of the most reliable styles is REST that two endpoints communicate with each other in synchronous manner.

The most important non-functional requirement that persuade you, as an architect or developer, to use this style is data and service availability.

Asynchronous Communication

Waiting for feedback from called service is not always necessary and it causes many problems like performance issues in bounded context. That’s why using asynchronous communication style is the best option when feedback is not important for caller bounded context.

Message brokers like Kafka, RabbitMQ and ActiveMQ are the most popular ones.

--

--

Masoud Chelongar

Software Architect , Technology Evangelist & Passionate Software Developer