The DDD Way toward Screaming Design — Our way continues
Part II-2: Tactical Design — Organizing Business Logic

DDD is about drawing lines, either during the decomposition of the business domain into sub-domains, putting them into bounded contexts, and deciding the relationship between them using strategic design that ends up with a context map, or during the decomposition of each bounded context into components using tactical design, each component having its own responsibility and being able to communicate with other components.
In order to find out the component’s boundaries, Uncle Bob said in his book Clean Architecture — A Craftsman’s Guide to Software Structure and Design:
You draw lines between things that matter and things that don’t.
The things that matter within a component are those that change for the same reason and whose change is not costly, either because they have little impact on other things or because they require minimal resources to be produced.
When one source code modules changes, other source code modules may have to be changed or recompiled, and then redeployed. Managing and building firewalls against this change is what boundaries are all about
— Uncle Bob
DDD is one of the most insightful ways of making significant design decisions that shape a system, while the cost of change measures significance. In the DDD’s way, we deal with change as follows:
- Breaking down a complex domain into sub-domains to detect where change is most likely to occur
- Finding bounded contexts to significantly group sub-domains (business rules) and manage dependency issues that may arise when implementing the change.
- Tackle business logic using modelling patterns according to sub-domain type. Then, it will be organized through small components using architecture and communication patterns based on the chosen modelling pattern in order to make change easy and simple.
The difficulty in making such a change should be proportional only to the scope of the change, and not to the shape of the change.
— Uncle Bob
So in this blog post, we’ll dive in the architecture patterns that we can adopt to organize our business logic throughout small units of source code: components.
The several responsibilities that source code has to accomplish in order to achieve the functional and non-functional requirements are:
- Interact with users to gather inputs and provide outputs
- Implement persistence mechanisms to store the system’s state within a database
- Communicate with external systems and be able to integrate them
The reason why a component’s source code needs to be changed must be related to one and only one of these responsibilities, which architectural patterns aim to bound. Let’s take a look at the three most common patterns and their use cases: layered architecture, ports and adapters, and CQRS.
Layered Architecture:
The classic layered architecture is chosen in most business applications to organize the elements of the system into layers:
- The presentation layer manages interaction with users, for example by implementing a REST API. It represents an entry point to the business logic.
- The business logic layer is where business rules are implemented using the chosen modelling pattern, such as active records or the domain model (details are in Part II-1).
- The data access layer implements persistence mechanisms and interaction with a database. It includes the implementation that enables communication with external systems and the integration of their APIs.
Code is sliced horizontally into layers, which are used as a way to group similar types of things. In a strict layered architecture, layers should depend only on the next adjacent lower layer
— Simon Brown, the missing chapter in Clean Architecture: A Craftsman’s Guide to Software Structure and Design
Layered architecture doesn’t scream anything about the business domain. It focuses on the database. In fact, business logic is directly dependent on the database due to the transitive dependencies to the database. So, it makes it hard to implement the domain model pattern because the business logic should not depend on infrastructure matters such as database details. The dependency between the layers makes this pattern a good choice when the business logic is implemented using transaction script or active records. It’s still possible to use it with the domain model, but the next pattern is a much better choice.
Ports & Adapters:
It puts the business logic at the center, isolated from any dependency upon the outside world. The business logic doesn’t depend on any implementation details, thanks to both “ports” and “adapters”.
Ports are the public interfaces allowing the business logic to communicate with external systems or components. Ports can be either inbound or outbound.
Inbound Ports are the API that declares methods allowing external systems to invoke the business logic, while Outbound Ports are the SPI that declare methods for the business logic to invoke external systems.
Adapters encapsulate the logic required to connect the business logic to external components. They provide the concrete implementations of the methods defined by the ports which keep the business logic unaware from specific external implementation. They can be also inbound or outbound.
Inbound Adapters are responsible for handling external requests by invoking the corresponding inbound ports, whereas Outbound Adapters are responsible for interacting with external resources and services on behalf of the core business logic by implementing the corresponding outbound port.
The isolation of the core business logic and ensuring it holds no technical details is achieved by applying the dependency rule. All source code dependencies goes from the outside to the core business logic (opposing the flow of control).
The dependency rule: source code dependencies must point only inward, toward higher-level policies
— Uncle Bob
The dependency inversion principle is also used to ensure that the domain logic can access the outside world (such as access to a database) so that the source code dependency is always directed to the center. In this way, everything depends on the business logic. However, the business logic does not depend on anything.
The dependency inversion principle tells us that the most flexible systems are those which source code dependencies refer only to abstractions, not to concretions
— Uncle Bob
The ports & adapters architecture encourages clear separation of concerns, it enables replacing or updating external systems and components without affecting the core business logic of an application as well as adding new types of external components without affecting the existing codebase, as long as they conform to the defined ports.
Moreover, this architecture fits very well the use of DDD’s fundamental blocks by having three layers as follows:
- The domain layer contains the domain model, including aggregates, value objects, domain events and domain services. It contains the definition of inbound and outbound ports.
- The application layer contains the applicative services that act as entry points to the domain layer.
- The infrastructure layer contains the inbound and outbound adapters that hold the implementation of the inbound and outbound ports.
There are many variants of Ports and Adapters architecture, notably hexagonal architecture, clean architecture, and onion architecture.
Ports and adapters architecture and its variants are based on isolating business logic from all technological concerns for that they are a good choice when domain model pattern is used to implement the business logic.
Command-Query Responsibility Segregation CQRS:
The CQRS pattern is based on the same principles used by ports and adapters for organizing business logic and managing the infrastructure matters. However, CQRS has a different way of managing the system’s data.
As its name suggests, this pattern ensures the separation of concerns: reading and writing data from and to the database.
It splits a persistent data model and the modules that use it into two parts: the command side and the query side.
— Chris Richardson, Microservices Pattern
The command side is the command execution model, which implements operations to write in the database, such as creating, updating, and deleting data. In other words, it implements the business logic and ensures the validation of the business rules. However, the query side contains the read model implementing operations to get data from the database. The query side should ensure synchronization between its model and the command-side data model even by implementing synchronous or asynchronous projections.
CQRS pattern allow to represent the same data in multiple models for that it’s used when systems require working with multiple persistent models. Moreover, CQRS is a good choice when the business logic is implemented using the event-sourced domain model.
The choice between these architectural models depends on the scope of knowledge you’ve acquired in the business domain. Just keep in mind that change is permanent, no matter what business domain you’re working in, so your goal is to build a software solution to solve a problem whose shape can be changed at any time.

Dear software engineers, as always, I leave you with this advice from my favourite instructor, Uncle Bob:
Software was invented to be “soft.” It was intended to be a way to easily change the behavior of machines.
Read Previous Parts
Part II-1: Tactical Design — Tackling Business Logic
Thank you for reading ^^