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
- 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
- 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
- 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
- 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
- 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
- The controller to the UI
- 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
- There will be many entries, such as Controller, xxlJob, mq, and so on
- 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
- 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
- External wechat name
- 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