About Aggregate Root and Domain Events---Understanding DDD in a Simple Way | JD Cloud Technical Team

Author: JD Logistics Zhao Yongping

foreword

Recently, I have time to discuss with my colleagues the implementation of the DDD architecture, but the real situation is that, in practice, for the implementation of tactics such as entities, value objects, aggregate roots, and domain events in domain-driven design, everyone’s understanding is still due to It varies from person to person, most likely because these concepts are still somewhat abstract, and at the same time they are different from traditional MVC architecture development.

Here, through a small demo, I would like to share with you my understanding of the tactical level in DDD. This understanding is just an introduction. This understanding only represents my personal understanding at this stage. It may also be different in the future with the deepening of business experience. understanding.

Since it is a small demo, we still have to start from the business scenario, which is the e-commerce business scenario I am most familiar with. But in this article, I will simplify the complexity of some actual business scenarios, and reflect the basic problems in the practice process through the demo of the smallest granularity.

A simple demo business scenario

Not much to say, let me first throw out a business scenario I hypothesized, which is the scenario of placing an order on an e-commerce website that we are familiar with. The details are as follows:

1. Entities:

• Goods: have attributes such as unique identifier, name, price, inventory, etc.

• Order: It has attributes such as unique identifier, order time, and status. Order contains multiple line items.

2. Value object:

• Address: It has attributes such as province, city, district, and detailed address.

3. Field events:

• Order creation event: This event is triggered when the user places an order, including order information, commodity information and other data.

• Order payment event: This event is triggered when the user completes the payment, including order information, payment amount and other data.

• Order delivery event: This event is triggered when the merchant delivers the goods, including order information, courier company, courier number and other data.

4. Aggregate root:

• Commodity aggregate root: Contains commodity entities and related value objects, responsible for operations such as creation, modification, and query of commodities.

• Order aggregate root: contains the order entity and related value objects, responsible for order creation, modification, query and other operations.

5. External interface service:

• Create order interface: After the user submits the purchase request, the system creates the corresponding order and triggers the order creation event.

• Payment order interface: After the user completes the payment, the system updates the order status and triggers an order payment event.

• Delivery interface: After the merchant delivers the goods, the system updates the order status and triggers the order delivery event.

• Query order interface: users can query their own order information according to order number and other conditions.

In this demo, commodity and order are two core domain concepts, which are respectively managed by the corresponding aggregate root. At the same time, by defining domain events, data updates and notifications in different business scenarios are realized. Finally, a set of simple interface services are provided to facilitate the use and expansion of the system.

Java code implementation of demo

Well, with the above full analysis of our business scenarios and the determination of the subdomain, we should write our code next.

  1. Commodity entity class:
// 省略getter/setter方法
public class Product {
    private Long id;
    private String name;
    private BigDecimal price;
    private Integer stock;
}

2. Order entity class

// 省略getter/setter方法
public class Order {
    private Long id;
    private LocalDateTime createTime;
    private Integer status;
    private List orderItems;
}

3. Line item entity class

// 省略getter/setter方法
public class OrderItem {
    private Long id;
    private Product product;
    private Integer quantity;
    private BigDecimal price;
}

4. Address value object

// 省略getter/setter方法 
public class Address {
    private String province;
    private String city;
    private String district;
    private String detail;
}

5. Domain event class

//订单创建领域事件
public class OrderCreatedEvent {
    private Order order;
    private List orderItems;

    public OrderCreatedEvent(Order order, List orderItems) {
        this.order = order;
        this.orderItems = orderItems;
    }
}


//订单支付领域事件
public class OrderPaidEvent {
    private Order order;
    private BigDecimal amount;

    public OrderPaidEvent(Order order, BigDecimal amount) {
        this.order = order;
        this.amount = amount;
    }
}

//订单
public class OrderShippedEvent {
    private Order order;
    private String expressCompany;
    private String expressNo;

    public OrderShippedEvent(Order order, String expressCompany, String expressNo) {
        this.order = order;
        this.expressCompany = expressCompany;
        this.expressNo = expressNo;
    }
}

6. Commodity aggregate root

public class ProductAggregate {
    private ProductService productService;

    public void createProduct(Product product) {
        productService.create(product);
    }

    public void updateProduct(Product product) {
        productService.update(product);
    }

    public void deleteProduct(Long productId) {
        productService.delete(productId);
    }

    public Product getProductById(Long productId) {
        return productService.getById(productId);
    }
}

7. Order aggregate root

public class OrderAggregate {
    private OrderService orderService;

    public void createOrder(Order order, List orderItems) {
        orderService.create(order);
        // 触发订单创建事件 
        DomainEventPublisher.publish(new OrderCreatedEvent(order, orderItems));
    }

    public void payOrder(Long orderId, BigDecimal amount) {
        orderService.pay(orderId, amount);
        // 触发订单支付事件
        DomainEventPublisher.publish(new OrderPaidEvent(orderService.getById(orderId), amount));
    }

    public void shipOrder(Long orderId, String expressCompany, String expressNo) {
        orderService.ship(orderId, expressCompany, expressNo);
        // 触发订单发货事件 
        DomainEventPublisher.publish(new OrderShippedEvent(orderService.getById(orderId), expressCompany, expressNo));
    }

    public Order getOrderById(Long orderId) {
        return orderService.getById(orderId);
    }
}

Summarize

Through the above demo, you can understand entities and value objects well and be intuitive. However, I also want to focus on explaining the concepts of aggregate roots and domain events

1. Aggregate root

As can be seen from the demo above, in the combined root class, we define operations such as adding, deleting, and checking items and orders, and define business logic codes for creating orders, paying orders, and shipping for orders.

An aggregate root is an object that represents the whole of a set of related objects. Inside the aggregate root, multiple entity objects and value objects can be contained. Aggregate roots can usually be identified and accessed by a unique identifier. It is the manager of the entire aggregate, responsible for maintaining consistency within the aggregate, and coordinating the relationship between various entity objects. Aggregate roots usually have rich behaviors and operations, and complex operations can be performed on objects inside the aggregate.

Therefore, the method in the real aggregate root is encapsulated based on the congestion model, not just the data encapsulation of the object. In the aggregate root, the object not only encapsulates the data, but also contains the corresponding behavior and business logic. This means that within an aggregate root, objects can handle their own business logic without external control. As written in the demo, the order object may contain some methods about order processing and delivery, such as confirm order, cancel order, ship, etc.

2. Field events

Domain events are one of the most important concepts in DDD. They are an important means to solve the coupling between subdomains, because they provide a way to transform domain concepts and business language into code. When a domain event occurs, it triggers actions that may change the state of the system or cause other domain events to occur. By modeling domain events, we can better understand business processes and design systems that are more in line with actual needs.

In DDD, domain events usually consist of three parts:

  1. Event name: This name should be able to concisely and clearly describe the business significance represented by the event.

  2. Related data: These data contain all the information related to the event at the time of the event. For example, in an e-commerce system, if an order is submitted, the order information as well as buyer and seller information should be included in the event.

  3. Sender and receiver: The sender is usually the object that triggers the event, and the receiver is the object that the event is processed.

Domain events have many uses in DDD. For example, they can be used to trigger other business processes, update databases, or notify other subsystems. They can also be used to solve some complex business logic problems, such as concurrency, data synchronization and error handling, and so on.

In short, domain events are a very important concept in DDD architecture, which can help us better understand business processes, design systems that are more in line with actual needs, and improve system maintainability and scalability.

Guess you like

Origin blog.csdn.net/jdcdev_/article/details/130400265