当前位置:网站首页>Practice of landing DDD code

Practice of landing DDD code

2022-01-15 02:43:34 loup

Preface

There are so many on the Internet DDD The article , But there is no good example of code Engineering , This article will be written with you hand in hand DDD Code , Study DDD The benefits of combining ideas with code .

One 、 Start with the hexagonal Architecture

 Hexagon structure

Hexagonal architecture is also called port and adapter . For each external type , There is an adapter corresponding to it . The outside world passes through the application layer API Interact with internal .

The hexagon architecture advocates a new perspective on the whole system . There are two areas in the architecture , Namely “ The outer area ” and “ Internal area ”. In the outer area , Different customers can submit input ; The internal system is used to obtain persistent data , And store the program output ( Like databases ), Or forward the output to another place halfway ( Like news , Forward to message queue ).

Each type of customer has its own adapter , The adapter is used to convert customer input into program internal data API Understood input . Each different side of a hexagon represents a different type of port , The port either handles input , Either process the output . The picture shows 3 All customer requests arrive at the same input port ( Adapter A、B and C), Another customer requested the use of an adapter D. Maybe before 3 A request used HTTP agreement ( browser 、REST and SOAP etc. ), The latter request uses MSMQ The agreement . Ports are not clearly defined , It's a very flexible concept . No matter which method is used to divide the ports , When a customer request arrives , There should be corresponding adapters to convert the input , The adapter will then invoke an operation of the application or send an event to the application , Control is thus handed over to the internal area .

Two 、 Dependency inversion

Rely on the principle of inversion (DIP) from Robert C. Martin Put forward , The core is defined as :

 High level modules should not depend on the underlying modules , Both should depend on abstraction 
 Abstraction should not depend on implementation details , Implementation details should depend on the interface 

according to DIP Principles , The domain layer can no longer rely on the infrastructure layer , The infrastructure layer decouples the domain layer by injecting persistence , The new layered architecture model using the dependency injection principle becomes as follows :
DIP

After using dependency injection , In fact, it can be found that there is no concept of layering . Whether high or low , Reality depends only on abstraction , The whole layer seems to be flattened .

3、 ... and 、DDD Code layering

DDD  Code layering

Overall code structure

- com.${company}.${system}.${appname}
|- ui( User interface layer )
  |service
    |- impl
  |- web
    |- controller
    |- filter
|- application( application layer )
  |- service
    |- impl
  |- command
  |- query
  |- dto
  |- mq
|- domain( Domain layer )
  |- service
  |- facade
  |- model
  |- event
  |- repository
|- infrastructure( Infrastructure layer )
  |- dal
    |-dos
    |-dao
  |- mapper
  |- factory

3.1 User interface layer

The user interface layer acts as an external portal , Decouple network protocol from business logic . Can include authentication 、Session management 、 Current limiting 、 exception handling 、 Log and other functions .

The return value is generally {"code":0,"msg":"success","data":{}} Format to return .
It usually encapsulates some public Response object , Refer to the following :

public class Response implements Serializable {
    private boolean success;
    private String code;
    private String msg;
    private Map<String, Object> errData;
}

public class SingleResponse<T> extends Response {
    private T data;   
}    

public class ListResponse<T> extends Response {
    private int count = 0;
    private int pageSize = 20;
    private int pageNo = 1;
    private List<T> data;
}

Interface of user interface layer , There is no need to maintain one-to-one correspondence with the application interface , It should be ensured that different interfaces are used for different scenarios , Ensure the compatibility and maintainability of subsequent services .

3.2 application layer

The application layer connects the user interface layer and the domain layer , Main coordination area layer , Oriented to use cases and business processes , Coordinate multiple aggregations to complete the composition and orchestration of services , No business logic is implemented at this layer , It's just a very thin layer .

The core class of the application layer :

  • ApplicationService Application service : The core class , Be responsible for the arrangement of business processes , But it is not responsible for any business logic . Sometimes it is abbreviated as “AppService”. One ApplicationService Class is a complete business process , Each of these methods is responsible for handling one Use Case, For example, various use cases of orders ( Place an order 、 Successful payment 、 deliver goods 、 Receiving goods 、 Inquire about ).
  • DTO Assembler: Be responsible for transforming internal domain model into external domain model DTO.
  • Back to DTO: As ApplicationService Out of the ginseng .
  • Command Instructions : It refers to the instruction that the caller clearly wants the system to operate , The expectation is to have an impact on a system , That is, write operations . Generally speaking, an instruction needs to have an explicit return value ( Such as synchronous operation results , Or asynchronous instructions have been accepted ).
  • Query Inquire about : It refers to what the caller explicitly wants to query , Including query parameters 、 Filter 、 Paging and other conditions , It is expected to have no impact on the data of a system , Read only operations .
  • Event event : It refers to an existing fact that has happened , Need the system to change or respond to this fact , Usually, event processing will have certain write operations . The event handler will not have a return value . What we need to pay attention to here is ,Application Layer of Event Concepts and Domain Layer of DomainEvent It's a similar concept , But not necessarily the same thing , there Event It's just an external notification mechanism .

ApplicationService The interface input parameter of can only be one Command、Query or Event object ,CQE The object needs to represent the semantics of the current method . This has the advantage of improving the stability of the interface 、 Reduce low-level repetition , And make the interface input parameters more semantic .

Case code

public interface UserAppService {
    UserDTO add(@Valid AddUserCommand cmd);
    List<UserDTO> query(UserQuery query);
}

@Data
public class AddUserCommand {
    private Integer age;
    private String name;
    ...
}

@Data
public class OrderQuery {
    private Long userId;
    private int pageNo;
    private int pageSize;
}

@Data
public class UserDTO {
    private Long userId;
    private Integer age;
    private String name;
    ...
}

For instructions with different semantics , To avoid CQE Reuse of objects . Counter example : A common scenario is “Create establish ” and “Update to update ”, Generally speaking, the only difference between the two types of objects is ID, Created without ID, The updates are . Therefore, it is often seen that some students use the same object as the input parameter of two methods , The only difference is ID Assignment or not . This is the wrong usage , Because the semantics of these two operations are completely different , Their verification conditions may also be completely different , So you shouldn't reuse the same object . The correct approach is to produce two objects .

3.2 1 Response vs Exception

Interface Layer of HTTP and RPC Interface , The return value is Result, Catch all exceptions .
Application The return value of all interfaces of the layer is DTO, Not responsible for handling exceptions .

3.2.2 CQE vs DTO

On the surface , Both objects are simple POJO object , But there is a big difference :

  • CQE: yes ApplicationService The input of , There is a definite “ Intention ”, The inside of the object needs to ensure its correctness . every last CQE There are clear “ Intention ” Of , So try to avoid CQE Reuse of , Even if all parameters are the same , As long as the semantics are different , You shouldn't reuse .
  • DTO: Just a data container , Just to interact with the outside , So it doesn't contain any logic , Just anemia object .

because CQE Yes, there is “ Intention ” Of , therefore , Theoretically CQE The number of is infinite . but DTO As a data container , It corresponds to the model , So it's Limited .

3.2.3 Anti-Corruption Layer Anticorrosive coating

stay ApplicationService in , Often rely on external services , From the code level, there are dependencies on external systems . For example, when creating a user , May depend on the account service , At this time, we introduce anti-corrosion coating . The class name of anticorrosive coating is generally “Facade”.

ACL Implementation of anti-corrosion coating :

  • For dependent external objects , We extract the required fields , Generate an internal required VO or DTO class .
  • Building a new Facade, stay Facade Encapsulate the call link , Convert an external class to an internal class .
  • For external system calls , The same with Facade Method encapsulates the external call link .

3.3 Domain layer

The domain layer is the core of the domain model , It mainly implements the core business logic of the domain model , Reflect the business capabilities of the domain model . The domain layer focuses on implementing the congestion model of domain objects and the atomic business logic of aggregation itself , As for user operations and business processes , It is left to the application layer to arrange . This design can ensure that the domain model is not vulnerable to changes in external requirements , Ensure the stability of domain model .

The core class of the domain layer :

  • Entity class (Entity): majority DDD The core of architecture is entity classes , Entity classes contain states in a domain 、 And direct operation of the State .Entity The most important design principle is to ensure the invariance of entities (Invariants), That is to ensure that no matter how the external operation , No attributes within an entity can conflict with each other , Inconsistent status .
  • The value object (VO): It is usually used to measure and describe things . We can create it very easily , test , Use , Optimization and maintenance , So when modeling , We try to use value objects to model .
  • Aggregate root (Aggr): Aggregation is a combination of entities and value objects closely related to business and logic . Aggregation is the basic unit of data modification and persistence . Each aggregation has a root entity , It's called aggregate roots , The outside world can only communicate with the aggregation through the aggregation root . The main purpose of aggregation root is to avoid the lack of uniform business rule control due to complex data model , And it leads to aggregation 、 Data inconsistency between entities .
  • Field service (DomainService): When an operation is not suitable for placing on aggregate and value objects , The best way is to use domain services . Where domain services can be used , Overuse of domain services will lead to anemia domain models . Perform a significant business operation process ; Transform domain objects ; Multiple domain objects have been calculated as input , The result is a value object .
  • Storage layer interface (Repository): Put the data we want as a collection in the warehouse , Get it directly when you want it . Warehousing serves as a connecting component between the domain layer and the infrastructure layer , So that the domain layer does not have to pay too much attention to storage details . In design , Put the warehousing interface at the domain layer , The specific implementation of warehousing is placed on the infrastructure layer , The domain layer accesses the data store through the interface , Instead of paying too much attention to the details of warehouse data , This makes the domain layer pay more attention to the domain logic .
  • factory (Factory): It is troublesome to construct objects such as entities , It can be constructed with the help of factories .

Usually , For entities 、 The value object 、 Aggregate root , We can't do without a class suffix , This can better reflect the meaning of the domain object itself .

public class Order {
    // OrderId It is the implicit concept of dominance , Instead of using a String,String It can only represent one value 
    private OrderId orderId;
    private BuyerId buyerId;
    private OrderStatus status;
    private Long amount;
    private List<OrderItem> orderItems;
    
    public static Order create(...) {
        //  If there are more parameters , The structure is troublesome , You can move to  Factory
        ...
    }
    
    public void pay(...) {
        
    }
    
    public void deliver(...) {
        
    }
    
    ...
}
public class OrderItem {
    private Long goodsId;
    private Integer count;
    
    public static OrderItem create(Long goodsId, Integer count) {
        ...
    }
}
//  Domain services generally do not require interface definitions 
public class OrderDomainService {
   
    @Resource
    private OrderRepository orderRepository;

    public Order create(Order order) {
        ...
        orderRepository.create(order);
        return order;
    }
}
public interface OrderRepository {
    void add(Order order);
    Order getByOrderId(OrderId orderId);
}

3.4 Infrastructure layer

Mainly responsible for technical details , Like databases CRUD、 cache 、 Message service, etc .

public class OrderDO {
    
}

public class OrderItemDO {
    
}
public class OrderDao implements OrderRepository {
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderItemMapper orderItemMapper;

    @Override
    public void add(Order order) {
        OrderDO orderDO = OrderFactory.build(order);
        List<OrderItemDO> orderItemDOList = OrderFactory.build(order);

        orderMapper.insert(orderDO);
        orderItemMapper.batchInsert(orderItemDOList);
    }
}

Reference material

版权声明
本文为[loup ]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/12/202112122250388399.html

随机推荐