Domain-driven Architecture – Rhombus symmetric Architecture

In domain-driven design, there is a guiding idea for architectural styles: different architectural styles can be selected in different bounded contexts, depending on their domain model and business characteristics. In the process of combining traditional hierarchical architecture with domain-driven concepts, a variety of architectural styles emerged: six-sided architecture, clean architecture, microservices architecture, and so on. This article is based on the Chat reading and thinking of IT literary worker Zhang Yi.

preface

Rhombic Symmetric Architecture focuses on domain-level architectures. It draws on the knowledge of hexagonal Architecture, hierarchical Architecture, and neat Architecture, and combines domain-driven design metamodel to better apply it to Architecture design in bounded context.

Architecture evolution

Hexagonal architecture

Hexagonal architecture is characterized by: while satisfying the idea of clean architecture, it pays more attention to the nature of communication between inner and outer layers and external resources. There is an adapter for each type of external resources: The message adapter, REST adapter, SOAP adapter, and Persistence adapter clearly show the domain and technology boundary through the different boundaries of the inner and outer hexagon (blue is domain, gray is technology)

  • Ports: We can think of ports as concrete protocols
  • Adapters: Think of concrete communication verticals, such as Servlets, REST, JPA
  • Application: Business logic at the use case level, representing a business scenario
  • Domain model: System-level business logic with multiple services and aggregations constituting a single use case

In a six-sided architecture, ports are the key to decoupling: portals isolate external requests and technical implementation details; Export a business with a hexagon architecture that isolates data persistence and external access devices (booking airline tickets) Example:

  • ReservationResource: Booking requests are sent to resource services defined by RESTful contracts. As an entry adapter, it falls within the boundary between the application hexagon and domain hexagon. Upon receiving the front-end request delivered in JSON format, it converts (deserializes) it into the request object required by the entry port ReservationAppService
  • ReservationAppService: The entry port is the application service, located above the boundary of the domain hexagon. When it receives the transformed request object from the entry adapter, it invokes TicketReservation, the domain service located within the boundary of the domain hexagon
  • TicketReservation: Domain logic for booking tickets
  • ReservationRepository: The exit port is a resource repository located above the boundary of the domain hexagon and defined as an interface
  • ReservationRepositoryAdapter: true to access the database logic by hexagons between application and hexagon boundaries in the field of export adapter, the realization of access database, insert port forwards the request booking record into the database can receive messages, perform insert operations

Clean architecture

The model of clean architecture is an inner and outer layer structure similar to the kernel pattern, which has the following characteristics:

  • The closer 1 is to the center, the higher the level
  • 2 From the outside in: Framework and Drivers — Interface adapter — application-level business logic — system-level business logic
  • 3 dependencies in the source must point to the inner layer of concentric circles, from the outside to the inside

There are many memorable design ideas that can be glean from clean architecture:

  • Components of different levels vary in frequency and cause of change (in line with the design idea of high cohesion and low coupling)
  • The further down the hierarchy the less dependent the components are, the less dependent the core business entities are (domain model and technology perfectly decoupled)
  • The more internal components are, the closer they are to the business. It is difficult to form a common framework for domain-specific content (domain-built barriers).

In this architecture, the outer circle represents the mechanism and the inner circle represents the strategy; Mechanisms are related to the implementation of specific technologies and are vulnerable to changes in the external environment; Policies are business related, encapsulate the core domain model and are least susceptible to changes in the external environment

Hexagonal architecture only distinguishes the internal and external boundaries, abstracts the roles of ports and adapters, and does not plan the relationships between each level and each object within the bounded context. While clean architecture is too generic, it distills the basic rules and themes of enterprise system architecture design. Therefore, when we introduce the idea of hexagonal architecture and clean architecture into the context of domain-driven design, we also need to introduce hierarchical architecture to provide more detailed design guidance, that is, to determine the relationship between layers, modules, and role stereotypes.

Classical layered architecture

Layered architecture is the most widely used architectural pattern. Almost every software system needs to isolate different Concern points through layers, so as to cope with changes in different requirements and make such changes independent.

  • The user interface layer is responsible for presenting information to the user and interpreting user commands. Includes web UI, mobile UI, and third-party services.
  • Application layer: A thin layer that coordinates the activities of applications. It does not contain business logic. It does not retain the state of business objects. In domain design, it is a Facade that is typically invoked by controllers in other bounded context base Settings layers.
  • Domain layer: This layer contains domain information and is the core of the business software. The state of the business object is retained here, and persistence of the state of the business object is delegated to the underlying Settings layer. It includes aggregations, value objects, entities, services, resource interfaces, and so on from domain design.
  • Infrastructure layer: do not simply think of it as a physical layer, it exists as a support library for other layers. It provides inter-layer communication and persistence of business objects. Generally include: network communication, resource implementation (database persistence implementation), asynchronous message service, gateway, controller control, etc.

Rhombus symmetric architecture

Rhombus symmetric architecture combines the idea of hierarchical architecture and hexagon architecture.

Hierarchical mapping of hexagon types

  • Entry adapters: In response to requests from clients outside the boundary, they are required to implement interprocess communication and message serialization and deserialization, all of which are related to specific communication technologies and therefore mapped to the infrastructure layer
  • Entry port: coordinates the interaction between external client requests and internal applications, matches the coordination capabilities of the application layer, and maps to the application layer

Application: The domain logic that takes over the entire bounded context and contains the domain model of the current bounded context should, of course, be mapped to the domain layer

  • Exit port: As an abstract interface that encapsulates access to external devices and databases, it should also map to the domain layer because it is invoked by applications, following a clean architecture philosophy
  • Exit adapters: Real implementations of access to peripheral devices and databases, related to specific technology implementations, mapped to the infrastructure layer

Breaking through layers

The hierarchical architecture is simply a logical partition of the bounded context. The logical layer may be represented as a module in the implementation of the code, but it is also possible to treat the entire bounded context as a module, with each layer being nothing more than a namespace difference, defined as a package within the module. Whether physically separated modules or logically separated packages, the risk of architectural corrosion can be reduced by ensuring that the boundary context can maintain the clarity of the internal structure under the protection of hexagonal boundaries.

According to the stable dependency principle of clean architecture, the domain layer cannot depend on the outer layer. Therefore, exit ports can only be placed in the domain layer. In fact, domain-driven design also requires that a Repository be defined in the domain model to manage the life cycle of the aggregation and to act as an abstract exit port for accessing external databases.

There is an argument for placing the repository at the domain level. After all, once the implementation details of database technology are stripped away, the repository’s interface approach is the management of aggregated domain model objects, including query, modify, add, and delete behaviors that can also be considered part of the domain logic.

However, bounded contexts may not be limited to accessing databases, but may also access files, networks, and message queues that also belong to external devices. To isolate the domain model from the peripheral devices, you also need to define abstract exit ports for them. Where do you put these exit ports? If it is still placed at the domain level, it is hard to justify itself. For example, the exit port EventPublisher supports publishing event messages to message queues, which would be a nonentity to place at the domain level. If it is not placed in the domain layer, which is in the inner core, it is placed outside the domain layer, which again violates the idea of clean architecture.

Since the exit ports are in such an awkward position, and it is obvious that the exit and the incoming are not symmetrical, we simply combine the port and the adapter into a “gateway”.

The above symmetric architecture is derived from hexagonal architecture and domain-driven design hierarchy, but is different from both. The symmetric northbound gateway defines the remote gateway and the local gateway as both ports and adapters, which actually changes the port-to-adapter style of the hexagonal architecture. The internal and external hierarchies of the domain layer and the north-south gateway layer, as well as the separation of ports and adapters required by the southern gateway, are further removed from the hierarchical architecture of the domain-driven design.

Now that it has changed, reabstract the architecture diagram based on the idea

The rhombus symmetry architecture is obtained, which mainly reflects the symmetric relationship between the north and south gateways

Composition of rhombus symmetric architecture

The composition of the context of the diamond structure:

  • Northbound gateway Remote gateway: interprocess communication
  • Nbi local gateway: intra-process communication
  • Domain model at the domain level: domain logic
  • Port abstraction of the southbound gateway: Abstract borrowing interfaces for various repository operations that can be relied on by the domain layer,
  • Adapter implementation of the southbound gateway: Implementation of the gateway port, which the runtime injects into the domain layer through dependency injection

Taking the six-sided service example as an example, if the rhombus architecture is changed, its architecture is as follows:

Introducing context mapping

Northbound Gateway Evolution

The NORTHbound gateway is similar to open hosting services, but more functional as the open hosting layer, containing both remote and local services

  • Remote services: cross-process communication; It contains Resource service, Provider service, Controller service, and Event Subscriber service
  • Local service: in-process communication; Application services corresponding to the application layer

When an external request comes in from a remote service, if the domain logic of the domain layer needs to be invoked, the request to the domain layer must be initiated via the local service. In this case, the local service acts as a port and the remote service can be considered as the client of the local service.

Southward gateway evolution

The southward gateway introduces abstract ports to isolate the internal domain model from the external environment. This value is similar to that of the anti-corrosion layer in context mapping, and it also extends the function of the anti-corrosion layer. The ports of the southward gateway are divided into:

  • Repository port: Isolates access to external databases, and the corresponding adapter provides aggregated persistence
  • Client port: Isolates access to upstream bounded contexts or third-party services, and the corresponding adapter provides the ability to invoke the service
  • Event Publisher port: Isolates access to external message queues, corresponding to adapters that provide the ability to publish event messages

Improved rhombus symmetry architecture

The rhombus symmetric architecture removes the concepts of application layer and infrastructure layer, and is summarized as a unified gateway layer, with north and south reflecting requests from different directions. The resulting symmetrical structure highlights the core role of the domain model and more clearly reflects the boundaries between business logic, technical functions, and the external environment.

Repositories serve as ports and adapters of the preservative layer, and as role constructs for domain modeling, which are better combined with scenario-driven design to enhance the stability of domain models. After the application layer is removed, it is weakened to the local service of the open host service layer, which is equivalent to returning to the essence of the service appearance from the design level, and also helps to solve the conceptual dispute between application service and domain service.

Code model examples:

Ohs is the abbreviation of open Host Service mode, ACL is the abbreviation of anti-corrosion layer mode, PL stands for release language. You can also use Northbound and sourthbound instead of OHS and ACL for package names, and messages contracts instead of PL for package names.


DDD Domain Driven Strategy (6) Rhombus symmetric architecture