This is the fourth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

Why stratify

Domain Driven Design Patterns, Principles and Practices

To avoid turning the code base into a big ball of mud (BBoM) that weakens the integrity of the domain model and, ultimately, usability, the system architecture supports the separation of technical complexity from domain complexity. The reasons for technological implementation changes are obviously different from the reasons for domain logic changes, resulting in infrastructure and domain logic problems changing at different rates

Each layer has its own responsibilities, which are obviously SRP compliant

layered

Standard form of DDD

  1. The User Interface is used to process Restful requests sent by users, parse configuration files entered by users, and pass information to the Application layer
  2. Application layer is the Application layer, responsible for multi-process management and scheduling, multi-thread management and scheduling, multi-coroutine scheduling and maintenance of business instance state model. When the scheduling layer receives the request from the user interface layer, it delegates the Context layer to process the Context related to this business
  3. The Domain layer is the Domain layer that defines the Domain model, including not only the modeling of Domain objects and their relationships, but also the explicit modeling of the roles of the objects
  4. The Infrastructure layer is the basic implementation layer that provides common technical capabilities to other layers: business platforms, programming frameworks, persistence mechanisms, messaging mechanisms, encapsulation of third-party libraries, common algorithms, and so on

Refine the business logic layer according to DDD

The module

Combined with Maven’s modules, the project is now divided into eight modules

<modules> <module>generator-assist-dao</module> <! --> <module> Generator -assist-client-api</module> <! -- swagger api yaml --> <module>assist-client-api</module> <! -- generated API --> <module>assist-controller</module> <! -- controller --> <module>assist-service</module> <! -- domain --> <module>assist-infrastructure</module> <! -- infrastructure --> <module>assist-common</module> <! -- basic common --> <module>start</module> <! --> </modules>Copy the code

start

Entrance to the module

Package structure:

  • Start has only one startup class
  • Test Unit test

In addition to startup classes, there are unit tests

generator-assist-dao

The GENERATED DAO class

Package structure:

  • repository
    • Model The entity class corresponding to the database
  • repository
    • Mapper mybatis mapper

Now when the practice is implemented, this module is an empty module. Why?

DDD defines repository and belongs to domain layer, but DAO is the encapsulation of the underlying database, so it is more reasonable to put concrete implementation classes in Infrastructure layer

In COLA, the authors reversed the dependencies for domain level purity, with the Repository interface defined at the domain level and implemented at the infra level

However, when landing, domains and infra appear to have circular dependencies, COLA puts the implementation in the App layer, which is a bit of an alternative, so temporarily put Repository entirely in the Service layer

Myth:

1, based on myBatis implementation, Mapper itself is an interface, repository implementation class in the domain layer, do not interface, so satisfy DDD layerization rules, but a step away from the DIP

2. Mentioned in The Entropy of DDD

DDD introduces Repository at the domain level, which corresponds to the concept of the aggregate root and abstracts database access, but the DDD bounded context may not be limited to access to the database, but also to access files, networks, and message queues that also belong to external devices. To isolate the domain model from external devices, you also need to define abstract exit ports for them. Where should these exit ports be placed? If still in the domain level, it is difficult to justify. For example, the exit port EventPublisher supports publishing event messages to message queues, and putting such an interface in the domain layer would be a bit of a nuisance. If you don’t put it in the domain layer that is inside the core, you have to put it outside the domain layer, which again goes against the idea of clean architecture

3. Is there any other theoretical support to solve problem 2

generator-assist-client-api

To generate the API swagger YAML file

Package structure:

  • swagger-spec
    • All Swagger Integrated file of all YAML files
    • Apis Swagger defines the API
    • Models Swagger defines the MODEL in the API
  • Swagger-templates Templates file

assist-client-api

Swagger generated API interface and model in API

Package structure:

  • client
    • API Swagger Generated API interface
    • Model Swagger Generated request, Response object

assist-controller

In the Controller layer, place the controller

Package structure:

  • Controller All controllers
  • Xxljob XXLJob Compensation job

According to THE DDD hierarchy specification, controller belongs to the UI layer and handles restful requests

  • Accept requests — capabilities provided by Spring
  • Request format Validation and transformation – Format Validation follows the Java Validation specification
  • Permission verification — handled by the gateway
  • Routing requests — handled by the gateway
  • Record requests — exclusively handled by Accessfilter
  • Reply response – provided by Spring

Why is there an XXLJob package? According to the ability, XXLJob should be placed in the infra layer. This reason is similar to the generator- Assist – DAO module. The handler of the XXlJob needs to call the Application service and relies on the Service Module

Therefore, xxLJob can be used as an entry point for remote requests, which belongs to the UI layer like controller

So this brings up a little bit of thought, is controller really the UI layer? Can it be divided into other levels?

There are several design ideas

  1. The UI layer belongs entirely to the big front end, not in the back end, and therefore not in the DDD, which starts with the Application Service
  2. The controller to the UI
  3. Controllers belong to infra, controllers are after all adapters that depend on the capabilities of the specific underlying framework

Controller is a concrete implementation based on SpringBoot

From the above analysis, it can be seen that the controller is logically assigned to the infrA layer, but physically it cannot be placed in the Infra module. You can’t just think of Controller as the C in MVC, there are many entries like XXlJob

  1. There will be many entries, such as Controller, xxlJob, mq, and so on
  2. There are also in-process events, the application layer, the infrastructure layer, the domain layer all have events, how to distinguish events is a problem
  3. The distinction between Application servers and domain services is also troublesome

Can we borrow the port and adapter architecture from the Shape of DDD

The controller as a driving adapter, since the distinction is so complex, it can be simple, thickening the controller, integration of entry and application service

There are simply two parts: remote services and local services

  • Remote service: Defines cross-process services, including Resource service, Provider service, Controller service and Event Subscriber service
  • Local services: All remote services need to invoke domain services, which must be invoked through local services; Clear isolation between the outside world and the domain to prevent leakage of the domain model

assist-service

Domain layer, but it’s still a three-tier structure, you have app Service, Domain Service, DTO, Event and even infrastructure layer, right

Package structure

  • BO
  • builder
  • common
  • component
  • convertor
  • domain
  • dto
  • event
  • interceptor
  • listener
  • model
  • repository
  • service
  • thrid
  • valid

assist-infrastructure

Infrastructure layer

Package structure

  • Config Configuration information
  • Adapter external call encapsulation
    • Clients External invocation implementation
    • Pl service interface contract published Language
  • Dp domain primitive should this be in the domain layer
  • (InvoiceType vs. InvoiceTypeEnum)
  • event
    • Publish event publisher, this package is empty and relies directly on Spring without self-implementation
  • The exception exception class
  • Gateway Encapsulates the behavior of accessing an external system or resource
    • External wechat name
      • API Indicates an external interface
      • Dto Indicates the external INTERFACE DTO
  • local
    • pl
  • ports
    • Clients External invocation interface
  • repository
    • model
  • Resources resources
  • Service Depends on external services
  • Util utility class

The most common package is the Gateway, which interacts with the external world with acLs

U is an Upstream dependent and D is an Downstream dependent. The anticorrosive layer (ACL) is placed downstream to translate the upstream message into the downstream domain model

In combination with the issue of generator- Assist – DAO module, whether ACL can be expanded, not limited to the gateway, like the resource library, do not have to follow DDD completely only abstract repository, like access to third-party applications, cache, messages can be abstracted, to fit the responsibility of the port


transform

<modules> <module>generator-assist-dao</module> <! --> <module> Generator -assist-client-api</module> <! -- swagger api yaml --> <module>assist-client-api</module> <! -- generated API --> <module>assist-ohs</module> <! -- ohs --> <module>assist-service</module> <! -- domain --> <module>assist-acl</module> <! -- acl --> <module>start</module> <! --> </modules>Copy the code

assist-controller

Based on the analysis above, this layer could be thicker

Changed its name to assist – ohs

OHS,open host service, defines the protocol for exposing the service, including the mode of communication, the format of message delivery (protocol)

Package structure

  • remote
    • controller
    • openapi
    • xxljob
    • subscribe
  • local
    • appservices
    • pl (plush language) request,response
    • convertor

assist-service

Domain layer

Package structure

  • Domain Domain object
  • Service Domain service
  • Factory The domain object factory
  • Builder Domain object constructor

assist-acl

Expand the infrastructure layer, isolate the domain layer and external dependencies, all external environments are treated equally, do not need to be specialized for the repository, so also can ensure the simplicity of the architecture, client, CAhCE…

The domain layer depends on the port interface

Package structure

  • Config Configuration information
  • Port depends on the external interface
    • Repository interface
    • Client Third-party system interface
    • Publisher message interface
    • Cache Interface
  • Adapter port implementation
    • repository
    • pl
    • client

conclusion

Module division and package structure are just one opinion. First, there is sufficient theoretical system support, whether according to DDD standard or deformation, more reasonable, and reach an agreement with the team and myself; The second is the domain abstraction, everything is for the stability and extensibility of the domain model, the form is only the representation

Our project still pays too much attention to form, and the most important domain is still too weak