DDD as code: how to interpret Domain Driven Design with code?

Time:2021-7-27

Introduction:Compared with the conventional MVC architecture, DDD is more abstract and difficult to understand, and various developers have different interpretations of DDD. So which design method is better? How do you know which DDD is more orthodox and not distorted by others? This paper attempts to use the concept of “DDD as code“, that is, use DSL code to describe DDD, unify the design idea of DDD, introduce in detail how to complete the expression of a project based on DDD DSL based on contextmapper through a case, and share the relationship between DDD design process and micro service in reality.

DDD as code: how to interpret Domain Driven Design with code?

There are many articles about DDD on the Internet, which is of course a good thing. Everyone wants to master good design methods to solve problems in software development. But there are also some problems. If you open several DDD articles on the Internet, although each author says he carries out architecture design according to DDD ideas, careful students will find that the structure description and architecture drawings in each author’s DDD articles are very different. You will be very surprised. Are these DDD designs? In fact, there is a problem here, that is, when describing some abstract concepts through words and diagrams, there will be great differences. We should not use the concept of blind people touching elephants for analogy. This is not appropriate. Even if the two students know DDD very well and have practiced several projects for several years, they still write different things. I started java a little earlier. Of course, you said I was old and conservative. I remember that there were not so many middleware at the beginning. It was based on the MVC framework struts 1. X. The design documents written by different students were also very different. Such a simple MVC architecture can have different architecture design documents, and DDD is relatively more abstract and difficult to understand, so the length of architecture design documents is different, which is understandable.

So do we want to accept the fact that “each author’s interpretation of DDD may not be the same” and “DDD design documents may be presented in different forms”? If so, students who want to learn DDD will have a great burden. Which design expression is better and easier to understand. At the same time, how do I know that my DDD is relatively orthodox and has not been misguided by others. I’m not saying that giving play to sexual thinking is not allowed, but from the perspective of preaching, it is still necessary to respect theory and facts.

We all know that the code can reflect the real situation when expressing some business or logic. Even if it is written by different developers, considering the compliance with design pattern, naming conventions, development language constraints, etc., the code is basically the same and easy to understand. It would be better if there were unit test and code review. This is also when some documents are imperfect. Many students choose to read the code. More students say, “look at the code directly, don’t look at their PPT and documents, you will be misled, otherwise you won’t know how to die”. In addition, we all know that now a very good practice is everything as code, such as the terrain of infrastructure as code, kubernetes yaml of platform as code, plantuml of diagram as code, etc. can we use the concept of DDD as code to make our design more unified, easier to express design ideas and easier to be understood.

DDD DSL

Using DSL means expressing DDD in code. This has existed for a long time, but it is more inclined to the tactical design and code level of DDD, such as sculptor [1] and fuin.org DDD DSL [2]. It is generally believed that it is a DDD code generator based on xtext. We have to study so much just to generate some code, and it’s just java code, so the general attention is not very high.

Can we make DDD DSL more strategic design than code generation and highlight the idea of design, so DDD as code is more comprehensive. Let’s introduce this framework.

Explain the term: many students have some doubts about the distinction between strategic design and tactical design of DDD. DDD has a special introduction, as follows:

  • Tactical DDD: entity, value object; Aggregate, Root Entity, Service, Domain Event; Factory, Repository。
  • Strategic DDD: bounded context, context map; Published Language, Shared Kernel, Open Host Service, Customer-Supplier, Conformist, Anti Corruption Layer (context relationship types)。

In fact, it is also relatively simple. The strategic design is larger and macro. You can understand the business and technical direction discussed by the top management of the company, and the division of labor and cooperation of various teams or products; The tactical design is relatively small, mainly concentrated in a boundedcontext, such as how to design the entity, service, repository, etc. of DDD, as well as the possible technical selection of application development, which can be said to pay more attention to the technical level.

Introduction to contextmapper framework

Contextmapper is an open source project [3], which mainly provides DSL support for DDD design, such as DDD strategic design, context mapping, boundedcontext modeling and service decoupling. Let’s see how to complete the expression of a project based on DDD DSL based on contextmapper.

When introducing contextmapper, let’s explain the project background first. Ruhua is an architect who is also very familiar with DDD and has practiced DDD in several projects. Recently, he joined the member line to complete the transformation of the member system and better cooperate with the company’s microservice design idea. There are three applications before the membership line: a large number of rest API services provided by the membership center; Member registration and login application; The member center handles the modification of personal password, basic information, SNS third-party binding and payment method binding after member login.

After joining the member team, Ruhua communicated with everyone the architecture idea based on DDD + microservices, and everyone agreed, but it was difficult to implement it in the specific architecture design and documents. Let’s take a look at the most typical DDD design:

DDD as code: how to interpret Domain Driven Design with code?

There are no problems with the concepts, such as subdomain, boundedcontext, entity, valueobject, service, repository, domain event, and context mapping, but how to express this idea to others? You can’t paste DDD design drawings and layered drawings every time, and then say that I designed according to DDD.

Start with subdomain

Ruhua starts the first step of DDD, that is, the division of subdomain. Of course, DDD includes three types of subdomains: generic, supporting and core. Here is a brief explanation of the differences between them:

  • Generic domain: general domain is generally considered to have been solved by the industry, such as observability logging, metrics and tracing in architecture design, various cloud services, etc. These have better implementation schemes and can be connected. Of course, there are also business solutions, such as mature industry solutions, such as ERP, CRM, mature hardware systems, etc. you can buy them.
  • Supporting domain: similar to general domain, but the system is more internal or needs some customized development on the basis of general domain. Such as an e-commerce system, membership, commodity, order, logistics and other business systems, of course, there are some internally developed technology type support systems.
  • Core domain: that is what we often call the business core. Of course, if it is a technical product, it is the technical core, which is what you should pay most attention to.

The overall relationship between the three is as follows: the core is the most distinctive and takes a lot of energy. In the complexity y dimension, we should avoid high complexity generic and supporting domains, which will distract your attention and invest a lot of energy. If it is really necessary, the way to buy services may be the best.

DDD as code: how to interpret Domain Driven Design with code?

Source:
https://github.com/ddd-crew/d…

Ruhua first divides members into several sub domains, such as accounts related to accounts, usertags marked by members, paymentprofile for payment methods, snsprofile for social platform integration, and other profiles. Here we do not involve the planning of generic and supporting Doman, but mainly start from the core business domain. One student explained the division structure and starting point with PPT, as follows:

DDD as code: how to interpret Domain Driven Design with code?

However, some students said whether the component diagram of UML is better, which is convenient to be unified with the later UML diagram, as follows:

DDD as code: how to interpret Domain Driven Design with code?

Of course, there are many other graphical tools such as Visio to show the structure diagram. The first step of DDD: the division and presentation of subdomain have different ways of understanding. There are many differences on how to describe and graphically present.

Returning to the starting point of the problem, we want to divide subdomains. Can the following DSL codes also be used:

Domain User {
    domainVisionStatement = "User domain to manage account, tags, profiles and payment profile."
    Subdomain AccountDomain {
       type = CORE_DOMAIN
       domainVisionStatement = "Account domain to save sensitive data and authentication"
    }
    Subdomain UserTagDomain {
       type = GENERIC_SUBDOMAIN
       domainVisionStatement = "UserTag domain manage user's KV and Boolean tag"
    }
    Subdomain PaymentProfileDomain {
        type = CORE_DOMAIN
        domainVisionStatement = "User payment profile domain to manage credit/debit card, Alipay payment information"
    }
    Subdomain SnsProfileDomain {
        type = CORE_DOMAIN
        domainVisionStatement = "User Sns profile domain to manage user Sns profile for Weibo, Wechat, Facebook and Twitter."
    }
    Subdomain ProfilesDomain {
        type = CORE_DOMAIN
        domainVisionStatement = "User profiles domain to manage user basic profile, interest profile etc"
    }
}

Although we don’t know the corresponding DSL code syntax at present, we already know the name, type and vision statement of the domain. As for how to present the system domain in the later stage, such as tables and graphics, we can consider presenting it based on the current data. The usertagdomain type is generic_ Subdomain, which means that marking is a universal domain. For example, we can cooperate with commodity, picture or video teams in the later stage, and we can build a marking system together.

Note: subdomain does not simply include type and domainvisionstatement. At the same time, you can add entity and service. Its purpose is to highlight the core features and facilitate your understanding of domain. For example, resetpassword and authbysmcode are added to account. I believe most people know what this means. However, be careful not to add other objects to the subdomain, such as VO, repository, domain event, etc. These are auxiliary development and should be used in the boundedcontext.

Subdomain AccountDomain {
       type = CORE_DOMAIN
       domainVisionStatement = "Account domain to save sensitive data and authentication"
       Entity Account {
         long id
         String nick
         String mobile
         String ^email
         String name
         String salt
         String passwd
         int status
         Date createdAt
         Date updatedAt
       }
      Service AccountService {
          void updatePassword(long accountId, String oldPassword, String newPassword);
          void resetPassword(long acountId);
          boolean authByEmail(String email, String password);
          boolean authBySmsCode(String mobile, String code);
      }
    } 

Context Map

Contextmap mainly describes the association relationship between boundedcontext in each domain. You can understand it as the topology map of boundedcontext. We won’t introduce boundedcontext in detail here. Now you only need to understand it as a carrier to realize domain, such as HSF service application you write, a web application or mobile app that processes customer requests, or an external SaaS system you rent. For example, there is a blog subdomain in your system. You can develop it yourself, set up a WordPress, or implement a blog with medium. Back to the microservice scenario, how to divide microservice applications? A subdomain corresponds to a business or virtual domain, while boundedcontext is a microservice application that specifically supports a subdomain. Of course, a subdomain may correspond to multiple microservice applications.

Since it describes various boundedcontext relationships, it will inevitably involve association relationships, such as partnership ([P] < – > [P]), shared kernel ([SK] < – > [SK]), customer / supplier ([C] < – [S]), conform (D, CF] < – [u, OHS, pl]), open host service, anticorrection layer ([D, ACL] < – [u, OHS, pl]), published language, etc. recommended by DDD. For detailed introduction, you can refer to DDD books. These corresponding relationships have corresponding abbreviations, which are the expression methods in parentheses. Here is an explanatory diagram of the association chart sheet:

DDD as code: how to interpret Domain Driven Design with code?

Source:
https://github.com/ddd-crew/c…

If you draw your own drawings to express these relationships, there must be a lot of work, including arrow types and notes, otherwise it will lead to misunderstanding. Here, we will go directly to the description of contextmap by contextmapper DSL. The code is as follows:

ContextMap UserContextMap {
   type = SYSTEM_LANDSCAPE
   state = TO_BE
   contains AccountContext
   contains UserTagContext
   contains PaymentProfileContext
   contains SnsProfileContext
   contains ProfilesContext
   contains UserLoginContext
   contains UserRegistrationContext
    UserLoginContext [D]<-[U] AccountContext {
        implementationTechnology = "RSocket"
        exposedAggregates = AccountFacadeAggregate
    }
    ProfilesContext [D]<-[U] UserTagContext {
        implementationTechnology = "RSocket"
        exposedAggregates = UserTags
    }
    UserRegistrationContext [D,C]<-[U,S] UserTagContext {
        implementationTechnology = "RSocket"
        exposedAggregates = UserTags
    }
    UserRegistrationContext [D,C]<-[U,S] SnsProfileContext {
        implementationTechnology = "RSocket"
    }
} 

You can see the boundedcontext names contained in the map, and then describe the relationship between them. In the association description, the corresponding description is involved. Previously, we explained that boundedcontext is the bearer of specific systems and applications of domain, so it involves the corresponding technical implementation. For example, HTTP rest API, RPC, pub / sub, etc. if the blog system is medium, then implementationtechnology = “rest API”. There are also exposedaggregates, which represent the exposed aggregation information, such as class objects and fields, service interfaces, etc., to facilitate the docking between communication parties. This will be introduced in boundedcontext.

BoundedContext

In the contextmap, we describe the relationship between them. Next, we will define the boundedcontext in detail. I believe most students know the contents contained in boundedcontext, such as entity, valueobject, aggregate, service, repository, domainevent, etc., which should be familiar to everyone. Here we give the code of a contextmapper for boundedcontext, as follows:

BoundedContext AccountContext implements AccountDomain {
    type = APPLICATION
    domainVisionStatement = "Managing account basic data"
    implementationTechnology = "Kotlin, Spring Boot, MySQL, Memcached"
        responsibilities = "Account", "Authentication"
    Aggregate AccountFacadeAggregate {
       ValueObject AccountDTO {
          long id
          String nick
          String name
          int status
          Date createdAt
          def toJson();
       }
       /* AccountFacade as Application Service */
       Service AccountFacade {
          @AccountDTO findById(Integer id);
       }
    }
    Aggregate Accounts {
         Entity Account {
            long id
            String nick
            String mobile
            String ^email
            String name
            String salt
            String passwd
            int status
            Date createdAt
            Date updatedAt
         }
   }
} 

Here is another description of boundedcontext:

  • Needless to say, the name of boundedcontext is consistent with the name in contextmap.
  • Implements accountdomain: indicates which subdomain to implement. We all know that a subdomain may contain multiple boundedcontexts. These boundedcontexts work together to meet the business requirements of the subdomain. The contextmap also provides refines to indicate that the boundedcontext needs to implement some user cases. The official documents have corresponding instructions.
  • Attribute field of boundedcontext: type indicates type, such as application, system, etc. Domainvision statement describes the responsibilities of boundedcontext. Implementationtechnology refers to specific technologies. As we mentioned earlier, the boundedcontext has involved specific applications and systems, so to explain the implementation of the corresponding technical scheme, just describe the core part. Responsibilities refers to the responsibility list of boundedcontext. Only keywords are required here. For example, account is responsible for security verification.
  • Accountfacade aggregate: refers to the aggregation provided to external calls. Here, the object definition of dto, the definition of service interface, etc.
  • Aggregate accounts: This represents the aggregation within boundedcontext, such as entity, value, object, service, etc. Let’s explain that the aggregate in DDD is the aggregate object of entity and value object, while the aggregate in contextmapper is represented as a collection of resources, such as service collection.

For more information about boundedcontext, please refer to the sculptor document [4]. According to the actual situation, you can add corresponding parts, such as domainevent, repository, etc.

Personally, I don’t think the boundedcontext here involves the ubiquitous language, but the corresponding auxiliary design documents are needed, and the relevant project background and technical decisions need to be explained. Personally, I recommend using the visualise, document and explore your software architecture [5] written by the C4 architecture design author. It is very practical and has no problem as a DDD architecture design document.

At the beginning of the article, we said that the previous DDD DSL is more a code generator. If it is a code generator, the generated code must have corresponding specifications and structures, such as the directory saved by entity, value object, service and repository. The generated code may also include a certain annotation or interface, standard fields, etc. Of course, we won’t discuss the code generator here, but we hope that your DDD architecture design still adopts a certain standard directory structure. Here are several standards recommended to you:

  • ddd-4-java: Base classes for DDD with Java[6]
  • jDDD:Libraries to help developers express DDD building blocks in Java code[7]
  • ddd-base: DDD base package for java[8]

In fact, the starting point of the three is the same, that is to describe DDD at the code level. The core is some annotation, interface, base class, and of course, the recommended package structure.

Other features of contextmapper

In fact, DDD as a whole has been explained clearly: domain division, boundedcontext topology and association relationship of the overall domain, specific definition of boundedcontext and specification of architecture design documents. However, contextmapper also provides DSLs corresponding to userstory and usecase. Let’s take a look.

UserStory

Many students asked how to write userstory. With this DSL, students no longer have to worry about how to write userstory. This DSL is relatively clear and mainly consists of three elements: as “AAA”, I hope to “XXX”, I hope to “yyyy”, so as to “zzz”, which is also in line with the typical three elements of userstory: role, activity and business value.

UserStory Customers {
    As a "Login User"
        I want to update a "Avatar"
        I want to update an "Address"
    so that "I can manage the personal data."
} 

UseCase

Use case is a way to describe requirements. There is a corresponding usecase diagram in UML diagram. The core is actor, interactive action and business value. The corresponding DSL code is as follows:

UseCase UC1_Example {
  actor = "Insurance Employee"
  interactions = create a "Customer", update a "Customer", "offer" a "Contract"
  benefit = "I am able to manage the customers data and offer them insurance contracts."
}

In aggregate aggregation, you can set the usecases property to describe the corresponding usecase, as follows:

Aggregate Contract {
  useCases = UC1_Example, UC2_Example
}

Benefits of contextmapper

According to you, we use DSL code to describe DDD. What are the benefits of this?

Architecture design standardization

This code method is clear and very standardized. If you write the wrong code, there will be any problems. Of course, if the compilation fails, the IDE will help you correct it. Therefore, DDD DSL is the same, completely unambiguous. Currently, contextmapper DSL includes eclipse and vs Code plug-ins. In IntelliJ idea, you can customize file types and live templates to assist you in writing CML files.

Generators support

Earlier, we talked about DDD DSL supporting code generator, which can help you generate code. I believe everyone can understand this, because DDD DSL code is standard, and other forms of code can be generated based on this code model, of course.

In addition, contextmapper also supports other model generation, such as the graphical presentation of contextmap and the structure diagram of plantuml. The corresponding code is here [9]. Here are some screenshots:

DDD as code: how to interpret Domain Driven Design with code?

DDD as code: how to interpret Domain Driven Design with code?

DDD as code: how to interpret Domain Driven Design with code?

Of course, contextmapper also provides a general generator, that is, based on DDD DSL model and FreeMarker template, and then you can generate various outputs you want, such as jhipster domain language (JDL) for fast file creation. It’s not surprising. I believe many Java programmers are familiar with this. We use FreeMarker to generate HTML when developing web applications. Visit here for more details [10].

DDD design process in reality

If we have DDD DSL to describe our architecture design, is it comprehensive and sufficient, and we don’t have to worry about development. Not yet. We know that before software architecture design and coding, there are demand research, customer visits, communication with domain experts, demand analysis, discussion, etc. This is still indispensable in real life. Its purpose is to provide materials and pave the way for subsequent architecture design. So how to integrate DDD with these previous operations? In fact, DDD involves this aspect, such as the eventstoring card:

DDD as code: how to interpret Domain Driven Design with code?

Bounded context canvas card:

DDD as code: how to interpret Domain Driven Design with code?

If you pay attention to the use of these DDD cards in the requirements analysis stage, there will be better materials for subsequent DDD design, of course, userstory and use case.

Personal suggestion: if you have time, it is strongly recommended to pay attention to DDD crew [11], and have a very comprehensive latest and practical knowledge and practice related to DDD.

Relationship between DDD and microservices

It has nothing to do with DDD DSL, just a little mention. The design of microservice architecture is how to divide complex business systems into closely cooperative microservice applications, and the basis of division is very important. From the perspective of business, subdomain divides business boundaries, while boundedcontext focuses on the application bearer corresponding to the business field. The generic type boundedcontext can support multiple subdomains at the same time, and can achieve the application reuse of different business systems. In the cloud native scenario, we hope to use more boundedcontext of system type, that is, reuse the system on the cloud, so as to reduce our own development and maintenance costs. Back to the boundedcontext of the appplication type, this is the specific application you want to develop. You can decide which microservice framework you choose. In the whole process, DDD plays a theoretical basis for application division.

But there is another problem here, that is, the communication between microservices. You can repeatedly emphasize that we need to build powerful distributed applications, but what is the recommended technology stack? How to do it? And we need to do better. This is not clearly stated, so we choose rest API, grpc, RPC, pub / sub and other hybrid communication technology stacks.

DDD has given the relationship between boundedcontext (partner ship, C / s, share kernel, etc.), but it has not given a good theoretical basis for communication and cooperation. However, there is also some consensus in the DDD community that asynchronous message communication + event driven is a better scheme, So you can see that Vaughn Vernon, the chief preacher of DDD, repeatedly talked about DDD + reactive to solve the communication problem of contextmapping.

At this point, if you see that contextmapper supports the output of MDSL (micro -) service contracts generator, it’s not surprising. It’s also a matter of course.

For more information on the relationship between microservices and DDD, please refer to microservices love domain driven design, why and how[ 12]

summary

The DSL concept proposed by contextmapper is still very good. At least it makes people less ambiguous and standardized in the understanding of DDD. The threshold for DDD beginners is also reduced. Although it can not reach the level of architecture design, it is at least barrier free in reading and understanding. At the time of writing this article, contextmapper DSL version 5.15.0 has been released, all relevant features have been developed, and the use is very smooth. Of course, when it comes to the actual development, whether DDD as code is effective or not, I also hope that students who practice DDD will give valuable opinions.

Of course, I can’t explain the contextmapper very clearly in an article. There are very detailed documents and corresponding papers on contextmapper [13]. Of course, you can not use the idea of DSL, but these ideas and relevant materials are very helpful to DDD design.

In addition, personally, if you are a beginner of DDD, then contextmapper may be more appropriate. DDD is a methodology. Those books are boring to death. It is almost very difficult to read two chapters without getting sleepy. On the contrary, if you learn DDD DSL, it will be much simpler. No matter how complex the DSL is, it will not be more complex than the programming language you learn, right? On the contrary, this DSL is very simple. Through simple DDD DSL learning, you will quickly master the concepts, ideas and methods. If you can’t, just look at other people’s codes (DDD DSL examples), which will also help you learn and master these methodologies quickly. It’s also very good to use books and articles to consolidate them later.

Author: Chashi!
Original link
This article is the original content of Alibaba cloud and cannot be reproduced without permission