laitimes

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

author:Yards are like clouds

This is a series of articles explaining DDD implementation, which takes a real and successfully launched software project as an example, systematically explains the typical practices of DDD in the implementation process, and the various trade-offs in the face of actual business scenarios.

This series contains the following articles:

  1. Introduction to DDD
  2. DDD concept vernacular
  3. Strategic design
  4. Code Engineering Structure (this article)
  5. Request processing flow
  6. Aggregate the root with the repository
  7. Entity and value objects
  8. Application services and domain services
  9. Realm events
  10. CQRS

Case project introduction

Since DDD is "domain" driven, we can't talk about technology without business, so let's first understand the case project throughout this article series from the business perspective - code like cloud (not Ma Yun, not code cloud). If you've already seen this case in other articles in this series, skip it.

Code is a SaaS software platform for the application of two-dimensional code scenarios, using the "one thing, one code" business model, can generate a two-dimensional code for each "item", and use the two-dimensional code as the entrance to carry out the relevant operations of the "item", typical application scenarios include fixed asset management, equipment inspection and item labeling.

When using code such as cloud, you first need to create an application, an application (App) contains multiple pages (also known as forms), and a page can contain multiple controls (such as radio box controls). After the application is created, multiple instances (QRs) can be created under the application to represent the managed objects (such as machines and equipment). Each instance corresponds to a QR code, and the mobile phone can scan the code to perform corresponding operations on the instance, such as viewing the relevant information of the instance or filling in the page form, etc., and the filling of the form is called Submission; For more concepts, see Code such as Cloud Terminology.

Technically, CodeRu Cloud is a no-code platform that includes multiple functional modules such as form engine, approval process, and data reports. The code cloud uses DDD to complete the development, and its back-end technology stack mainly includes Java, Spring Boot and MongoDB.

Code engineering structure

During the sharing period, the audience will generally ask a lot of questions, and the most asked is not how to divide the bounded context, how to design aggregation and other DDD key issues, but how to build the DDD engineering structure and how to divide the package.

In fact, DDD does not make requirements for engineering structure, in the code like cloud, we combined with the industry's common practices and our own understanding of DDD to build a set of engineering structure suitable for ourselves, we believe that it is also applicable to most projects, in this article, we will explain this in detail.

In the previous strategic design, we mentioned that Coderu Cloud is a monolithic project, which divides 3 bounded contexts, that is, 3 modules, through Java subcontracting. For readers who are engaged in microservices, don't be scared away by the word "monolith", most of the content explained in this article is suitable for both monolithic and microservices.

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

The above is the directory structure of the code like a cloud project, under the root subpackage src/main/java/com/mryqr, there are 3 module packages of core, integration and management, corresponding to "core context", "integration context" and "background management context", for microservice systems, these three subpackages do not exist, because each subpackage has its own separate microservice project. That is, there is a one-to-one correspondence between the bounded context of DDD and the microservice. The same level as these three module packages is also a common package, which is not a business module, but some infrastructure shared by all modules, such as Spring configuration, email sending mechanism, etc. Under the src directory, there are also three directories: test, apiTest and gatling, which correspond to unit tests, API tests, and performance test codes. In addition, the deploy directory is used to store deployment-related files, the doc directory is used to store project documents, and the gradle directory is used to store various Gradle configuration files.

Subcontracting principle: business first, technology later

Among the various module packages mentioned above, programmers are most concerned about how the core package should be further subcontracted, because core is the core business module of the entire project.

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

When doing subcontracting, one of the most common anti-patterns is to use technology subcontracting as an upper-level subcontract, and then divide the business package under each technology subcontract. The subcontracting method more respected by the DDD community is "business first, then technology", that is, the upper package is divided according to the business first, and then subcontracting according to the technology within each business package.

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

In the core module package of the code, the first is a business-based subcontract, including dozens of packages such as APP and Assignment, where the app corresponds to the application aggregation root, and the assignment corresponds to the task aggregation root, that is, each business subcontract corresponds to an aggregation root. Under each business subcontract, technical subcontracting is done, including the following sub-sub-subcontracts:

  • command: Used to store application services and command objects, please refer to Application Services and Domain Services for more details;
  • domain: used to store all domain models, for more related content, please refer to the aggregation root and resource library;
  • eventhandler: used to store domain event handlers, please refer to domain events for more related content;
  • infrastructure: used to store technical infrastructure, such as access to databases;
  • query: Used to store query logic, please refer to CQRS for more information.

Under these subcontracts, further subcontracting can be based on the actual situation.

This "business first, technology later" approach to subcontracting has the following benefits:

  • Business Intuitive: All business modules are grouped together and at a subcontracting level, giving a panoramic view of all businesses in a software project at a glance. In fact, Robert C. Martin (Uncle Bob) came up with a concept called Screaming Architecture, and that's what it means. Screaming means "wow", for example, when you see a house, you say "Wow, what a beautiful house!" That is, you can recognize at a glance that this is a house.
  • Easy to navigate: When you want to find a feature, the first thing you think of must be which business segment the function belongs to, not which Controller it belongs to, so you can find the business subcontract first, and then find the corresponding function code along the way.
  • Easy migration: Each business package contains all the code from business to technology, so you only need to move the business package as a whole during migration, for example, if you want to migrate to the microservice architecture in the future, you only need to copy the business package that needs to be migrated out into the new project.

Among the above sub-subcontracts, the domain subcontracting should be the largest one, because it contains all the domain models and business logic. Under the app service package of a cloud project, the amount of code contained in each sub-subpackage is as follows:

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

As you can see, the amount of code contained in the domain package is far greater than all the other subpackages combined. Of course, we are not saying that all DDD projects need to meet this, but emphasize that in DDD the domain model should be the body of the code.

Next, let's take a look at what's included in each subpackage, starting with the domain subpackage:

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

In domain subcontracting, the most important is the App aggregation root, which also includes the domain service AppDomainService, the factory AppFactory and the resource library AppRepository. AppRepository here is an interface, which is actually in the infrastructure subpackage. Based on the principle of cohesion, some closely related classes are placed in the next level of subpackages, such as attribute and page subpackages. It is worth mentioning that the event package used to store domain events is also placed under the domain, because domain events are also part of the domain model, but the handler class of domain events is placed in the eventhandler package at the same level as domain, which we will explain in detail in domain events.

The command package is used to place the application service and request data class, where "command" is the "C" in CQRS, which represents a command initiated by the outside world to the software system.

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

In the command package, AppCommandService is used to receive external business requests (commands). The input parameter received by AppCommandService is the Command object (suffixed with "Command"), and the Command object expresses the business intent by its name, such as CopyAppCommand is used to copy the application (here "application" represents the application aggregation root on the business), and CreateAppCommand is used to create a new application.

Eventhandler is used to store the processor classes of domain events, which are equivalent to application services, they are not part of the domain model, but similar to application services to orchestrate and orchestration.

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

Infrastructure is used to store infrastructure classes, mainly including the implementation classes of the resource library:

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

Query is used to store classes related to data queries, and the "query" here is also the "Q" in CQRS, which we will explain in detail in this series of CQRS.

The product code is shown to you, but don't say that you can't DDD series (four): code engineering structure

Automated testing

Automated tests include unit tests, API tests, and performance tests. In API testing, infrastructure such as databases and message queues are built through local Docker, and the entire Spring process is started during testing, and then the simulated front-end sends real business requests to each API, and finally verifies the return results, and if there is a need to access third-party systems, it is replaced by the Stub class. Code Ruyun adopts the "API testing mainly, unit testing supplemented" testing strategy, its API test coverage reaches 90%, all business use cases and important branches have API test coverage, unit testing is mainly used to test domain models, for structural facilities such as application services, Controllers, and event handlers do not do unit testing requirements, because these classes do not contain much logic, the tests of these classes can be digested in API tests.

summary

This article mainly explains the typical directory structure of DDD code engineering, and we recommend subcontracting through the method of "business first, technology later", so that the business reflected in the project is more intuitive. In addition, in a DDD project, the domain model should be the main body of the entire project, and all domain objects and business logic should be included under the domain package. In the following request processing process, we will explain the whole process of request processing in DDD projects in detail.