作者:京东物流 赵勇萍

前言

最近有空会跟同事讨论DDD架构的实践落地的状况,但真实状况是,实践中对于范畴驱动规划中的实体,值目标,聚合根,范畴事情这些战术类的实践落地,每个人了解仍然因人而异,大概率是由于这些概念仍是有一些抽象,一起有有别于传统的MVC架构开发。

在此,经过小demo的方式跟咱们分享一下我对DDD中战术层级的了解,算是抛砖引玉,该了解仅代表我个人在现阶段的一个了解,也或许未来随着事务经历深化,还会有不同的了解。

既然说是小demo,仍是要从事务场景出发,也便是我最熟知的电商事务场景说起。可是该篇文章里, 我会简化一些实践事务场景中的杂乱度,经过最小颗粒度的demo,来反映实践进程中的基本问题。

一个简略的demo事务场景

话不多说,我先抛出我自己假设的一个事务场景,便是咱们熟知的电商网站下单购物的场景。具体细节如下:

1. 实体:

• 产品:具有仅有标识、称号、价格、库存等属性。

• 订单:具有仅有标识、下单时刻、状况等属性。订单包含多个订单项。

2. 值目标:

地址:具有省、市、区、具体地址等属性。

3. 范畴事情:

• 订单创立事情:当用户下单时触发该事情,包含订单信息、产品信息等数据。

• 订单付出事情:当用户完结付出时触发该事情,包含订单信息、付出金额等数据。

• 订单发货事情:当商家发货时触发该事情,包含订单信息、快递公司、快递单号等数据。

4. 聚合根:

• 产品聚合根:包含产品实体和相关的值目标,担任产品的创立、修改、查询等操作。

• 订单聚合根:包含订单实体和相关的值目标,担任订单的创立、修改、查询等操作。

5. 对外接口服务:

• 创立订单接口:用户提交购买恳求后,体系创立相应的订单,并触发订单创立事情。

• 付出订单接口:用户完结付出后,体系更新订单状况,并触发订单付出事情。

• 发货接口:商家发货后,体系更新订单状况,并触发订单发货事情。

• 查询订单接口:用户可以依据订单号等条件查询自己的订单信息。

该demo中,产品和订单是两个中心范畴概念,分别由对应的聚合根担任管理。一起,经过界说范畴事情,完成了不同事务场景下的数据更新和告诉。最后,对外供给了一组简略的接口服务,方便体系的使用和扩展。

demo的java代码完成

好了,有了以上咱们对事务场景的充分剖析,确定了子域,接下来咱们该写咱们的代码。

1.产品实体类:

// 省掉getter/setter办法
public class Product {
    private Long id;
    private String name;
    private BigDecimal price;
    private Integer stock;
}
  1. 订单实体类
// 省掉getter/setter办法
public class Order {
    private Long id;
    private LocalDateTime createTime;
    private Integer status;
    private List orderItems;
}
  1. 订单项实体类
// 省掉getter/setter办法
public class OrderItem {
    private Long id;
    private Product product;
    private Integer quantity;
    private BigDecimal price;
}
  1. 地址值目标
// 省掉getter/setter办法 
public class Address {
    private String province;
    private String city;
    private String district;
    private String detail;
}
  1. 范畴事情类
//订单创立范畴事情
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;
    }
}
  1. 产品聚合根
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);
    }
}
  1. 订单聚合根
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);
    }
}

总结

经过以上demo,对于实体和值目标,咱们会很好了解,并且很直观。可是, 我额外想要点解释一下聚合根和范畴事情的概念

  1. 聚合根

从上面的demo可以看出,在合根类中,咱们界说了产品和订单的增、删、查等操作,并且为订单界说了创立订单、付出订单、发货等事务逻辑代码。

聚合根是一个目标,它代表一组相关联的目标的整体。在聚合根内部,可以包含多个实体目标和值目标。聚合根一般可以经过仅有标识符来进行辨认和访问。它是整个聚合的管理者,担任维护聚合之内的一致性,并协调各个实体目标之间的关系。聚合根一般具有丰厚的行为和操作,可以对聚合内部的目标进行杂乱的操作。

所以说,真正的聚合根内的办法是根据充血模型封装的,而不是仅仅是对目标的数据封装。在聚合根中,目标不仅封装了数据,还包含了相应的行为和事务逻辑。这意味着在一个聚合根中,目标可以自己处理自己的事务逻辑,而不需要外部的控制。就好像demo中所写的那样,订单目标或许包含一些关于订单处理和交给的办法,如确认订单、撤销订单、发货等。

  1. 范畴事情

范畴事情是DDD中最重要的概念之一,他是处理子域之间耦合的重要手法,由于它们供给了一种将范畴概念和事务语言转化为代码的办法。当一个范畴事情发生时,它会触发一些操作,这些操作或许会更改体系的状况,也或许会导致其他范畴事情的发生。经过对范畴事情进行建模,咱们可以更好地了解事务进程并规划出愈加符合实践需求的体系。

在DDD中,范畴事情一般由三个部分组成:

1.事情称号:这个称号应该可以简洁明了地描述事情所代表的事务意义。

2.相关数据:这些数据包含了事情发生时与事情相关的所有信息。例如,在一个电子商务体系中,如果订单被提交,则订单信息以及买家和卖家的信息都应该包含在该事情中。

3.发送者和接收者:发送者一般是触发事情的目标,接收者则是事情处理的目标。

范畴事情在DDD中有很多用处。例如,它们可以用来触发其他事务流程、更新数据库或告诉其他子体系。它们还可以用于处理一些杂乱的事务逻辑问题,例如并发、数据同步和错误处理等等。

总归,范畴事情是DDD架构中非常重要的概念,它可以协助咱们更好地了解事务进程,规划出愈加符合实践需求的体系,并进步体系的可维护性和可扩展性。