DDD Book picture

Separating business logic concerns

Sometimes the business logic is 10 lines, but the DB procedure logic is 1000 lines. (Failure of separation of concerns)

Since the business logic is mostly in SQL, it naturally becomes a bottleneck due to constant database performance degradation.

Databases are about storing and processing data and are optimized for that.

It is better to have a strict separation of concerns between business logic and data processing than a tightly coupled database-centric architecture.

Layered Architecture (Hierarchical Architecture)

  1. Presentation

  2. Business Logic

  3. Data Access

  4. Screen Processing

  5. Business Flow Control

  6. Data Processing

However, this architecture violates the principle of open closed. This is because every layer defines and owns its own interfaces to each function.

Here we apply DIP to move the interfaces defined by the data access layer across the boundary to the business logic layer. The implementations of the data access layer are then forced to look at the interfaces of the business logic layer.

Hexagonal Architecture

  • A big feature of the hexagonal architecture is that the high-level internal zones do not depend on external adapters at all.
  • This is made possible by the configured ports of the internal regions.

In a hexagonal architecture, the following distinctions are made

— An internal domain that expresses business logic — An outer zone that is responsible for handling interfaces

The outer zone is an inbound adapter that handles requests coming in from the outside. Outbound adapters are called by the business logic to connect with the outside world.

Inbound adapters can be controllers that issue REST APIs, command handlers, event message subscription handlers, etc,

Outbound adapters can be DAOs, event message issuing classes, proxies for calling external services, etc.

Clean Architecture

Clean Architecture is an architecture proposed by Robert C. Martin and is very similar to Hexagonal. Software has behavioral and structural values, with structural values being more important. This is because it is the structural value that makes the software smooth.

Clean architecture is divided into entities, use cases, internal interfaces, frameworks, and devices (external interfaces) from the center.

Entities are data-driven objects of core business rules. Core rules and data are inherently combined, so they are easy to create as objects.

A use case captures the flow of using the system while honoring the core business rules inside the entity.

All other areas are details that wrap around the use case. — I/O devices — Storage — Web system — Servers — Frameworks — Communication protocols As shown above, a clear separation of coupling can enhance testability and development/deployment independence. In a microservice architecture, each service should be clearly separated according to its goals and utilization, and it is recommended to define the appropriate development language, repository, and internal architecture based on the purpose of each service.

Internal Zone — Business Rules

Within the internal realm of defining business rules, there are service interfaces, service implementations, domains, repository interfaces, domain event interfaces, and API proxy interfaces.

There are useful patterns for organizing these internal areas, especially the relationship between services and domains. They are Martin Fowler’s transaction script pattern and the domain model pattern.

Transaction Script Pattern

This pattern is good for handling simple business. Because it’s exactly like procedural programming, there’s a lot of room for sophistication in the service and for problems that we’ve seen in database-driven architectures.

The objects that represent business concepts don’t have behaviors; the responsibility for doing something lies with the service. This approach leads to service bloat over time, with domain objects acting as bundles of information. Also, because services are the unit of use case processing, and most of the business logic is done in services, services can end up with duplicate code for similar use cases. This can make maintenance difficult.

Domain Model Pattern

The domain model pattern is that domain objects have business behaviors as well as data. The data owned by an object is hidden by its behavior. Service methods are simplified because the responsibilities of the service are properly distributed across domains. The domain model of the Domain Model pattern is the object model of object-oriented design. It can be further evolved to apply the aggregate pattern of domain-driven design.

The Aggregate Pattern

An entire collection of conceptually bound entities is called an aggregate. It’s a pattern that compensates for the shortcomings of object modeling.

— External Zone

API Publishing Adapter

Call the service interface of the internal realm and provide it as a REST-formatted API. It is recommended that you create your own DTOs to convert and map entities and deliver them.

API Proxy Adapter

This is an outbound adapter that calls the API of another service. It implements the proxy interface defined in the internal realm, and the API of the other service can be a REST API, Socket, SOAP protocol, etc.

Storage Processing Adapter

There are OR and SQL mappings. OR is JPA, Spring Data, etc. and SQL is MyBatis, etc.

Domain Event Publishing Adapter

Domain events are changes in state as a result of some event, implemented as classes with names like ordered, canceled, and so on, and issued through the domain event issuing adapter for delivery to consumers. When applying the aggregate pattern, domain events are events that occur in the aggregate.

In reality, where domain events are generated is in the internal realm, and the domain event issuing adapter is responsible for issuing them outbound to a specific message queue or stream storage.

Domain Event Handler

Subscribes to externally issued domain events and forwards them to internal zones. Forward events to internal zones based on the state of the event.

The problem becomes apparent when business logic and database access logic are mixed together, making maintenance and performance optimization difficult.

In this situation, it is important to apply structures such as clean architecture or hexagonal architecture to maximize the separation of concerns.

In particular, it is necessary to apply domain model patterns and aggregate patterns to design business logic as object-centric and to clearly separate interfaces with external areas.

This makes your code more cohesive and less coupled, which improves maintainability.

Also, when considering a microservice architecture, it’s important to define clear boundaries so that each service can be managed and scaled independently. This minimizes dependencies between services and allows each service to be developed and deployed freely.

Finally, it’s necessary to actively leverage object-oriented programming to address the complexity of your business. This helps you design systems that are behavior-driven rather than data-driven and improves the readability and maintainability.