反映业务规则的代码是整个软件的核心,但是它一般只占很小的一部分,在传统的基于贫血模型的分层软件架构中,业务规则可能分散到各个层、各个代码段,从而使得通过代码来还原业务规则或者保证代码与业务规则一致将变得非常困难。DDD分层架构的核心思想就是将所有业务规则的代码抽取到领域层,保证领域层的编码与领域模型是完全一致的。

下图是DDD的分层架构。
解构领域驱动设计(二):领域驱动设计的核心之分层架构-LMLPHP
我将通过代码来演示这个新的分层架构。

1 应用层

应用层在这里非常的简单清晰,它仅仅是将基础设施层、领域层提供的功能装配起来完成任务,这一层的代码逻辑非常简单。

下面是创建设计师订单的装配任务。

 1 @Service
 2 @Transactional(rollbackFor = Exception.class)
 3 public class DesignerOrderServiceImpl implements DesignerOrderService {
 4     @Autowired
 5     private DesignerOrderRepository designerOrderRepository;
 6     @Autowired
 7     private RefundOrderRepository refundOrderRepository;
 8
 9     @Override
10     public DesignerOrder createOrder(int customerId, int designerId) {
11         DesignerOrder order = DesignerOrderFactory.createOrder(customerId, designerId);
12
13         designerOrderRepository.create(order);
14
15         return designerOrderRepository.selectByKey(order.getId());
16     }
17
18     @Override
19     public void pay(int orderId, float amount) {
20         DesignerOrder order = designerOrderRepository.selectByKey(orderId);
21         if (order == null) {
22             AppException.throwAppException(AppExceptionMessage.DESIGNER_ORDER_NOT_EXIST_CODE, AppExceptionMessage.DESIGNER_ORDER_NOT_EXIST, orderId);
23         }
24
25         order.pay(amount);
26         designerOrderRepository.update(order);
27     }
28
29     @Override
30     public RefundOrder refund(int orderId, String cause) {
31         DesignerOrder order = designerOrderRepository.selectByKey(orderId);
32         if (order == null) {
33             AppException.throwAppException(AppExceptionMessage.DESIGNER_ORDER_NOT_EXIST_CODE, AppExceptionMessage.DESIGNER_ORDER_NOT_EXIST, orderId);
34         }
35
36         RefundOrder refundOrder = order.refund(cause);
37
38         designerOrderRepository.update(order);
39
40         refundOrderRepository.create(refundOrder);
41
42         return refundOrderRepository.selectByKey(refundOrder.getId());
43     }
44 }

这里例举了创建订单、付款、退款的应用层代码。

这里,订单创建有2个步骤:

(1)使用Factory创建新的业务对象;

(2)使用Repository将业务对象持久化到数据库。

付款3个步骤:

(1)使用Repository加载订单业务对象到内存;

(2)调用订单业务对象的付款方法更改业务对象状态;

(3)使用Repository将业务对象持久化到数据库。

退款有3个步骤:

(1)使用Repository加载订单业务对象到内存;

(2)调用设计师订单业务对象的退款方法改变业务对象的状态,然后生成一个退款订单业务对象;

(3)使用Repository持久化设计师订单和退款订单业务对象。

此外,应用层还额外处理了数据持久化的事务。

2 领域层

领域层是实现所有业务规则的领域对象,它是整个软件的核心,并且与领域模型保持一致。

 1 @Data
 2 @EqualsAndHashCode(of = {"id"})
 3 public class DesignerOrder implements Entity<DesignerOrder> {
 4     private int id;
 5     private DesignerOrderState state;
 6     private int customerId;
 7     private int designerId;
 8     private float area;
 9
10     private float expectedAmount;
11     private int estimatedDays;
12     private DesigningProgressReport progressReport;
13
14     private String abortCause;
15
16     private float actualPaidAmount;
17
18     private int feedbackStar;
19     private String feedbackDescription;
20
21     private Date createdTime;
22     private Date updatedTime;
23
24     public void pay(float amount) {
25         Assert.isTrue(amount > 0, "The amount must be bigger than 0.");
26
27         if (!DesignerOrderWorkflowService.canChangeState(state, DesignerOrderState.PAID)) {
28             DomainException.throwDomainException(DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE_CODE, DomainExceptionMessage.PAYMENT_NOT_IN_READY_STATE, this.id, this.state);
29         }
30
31         if (Math.abs(amount - this.expectedAmount) > 0.01) {
32             DomainException.throwDomainException(DomainExceptionMessage.PAYMENT_NOT_MATCHED_CODE, DomainExceptionMessage.PAYMENT_NOT_MATCHED, this.id, this.expectedAmount, amount);
33         }
34
35         this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.PAID);
36         this.actualPaidAmount = amount;
37
38         // 付款完成后,自动启动进度跟踪
39         this.progressReport.startup();
40     }
41
42     public RefundOrder refund(String cause) {
43         this.assertCanRefund();
44
45         this.state = DesignerOrderWorkflowService.changeState(this.id, state, DesignerOrderState.REFUND);
46
47         return RefundOrderFactory.newRefundOrder(this, cause);
48     }
49
50     private void assertCanRefund() {
51         DesigningProgressNode constructionDrawingDesignNode = this.progressReport.getNode(DesigningProgressNodeType.CONSTRUCTION_DRAWING_DESIGN);
52         if (constructionDrawingDesignNode.getState() == DesigningProgressNodeState.REQUEST_COMPLETION ||
53                 constructionDrawingDesignNode.getState() == DesigningProgressNodeState.CONFIRM_COMPLETION) {
54             DomainException.throwDomainException(DomainExceptionMessage.FAILED_TO_REFUND_FOR_PROGRESS_CODE, DomainExceptionMessage.FAILED_TO_REFUND_FOR_PROGRESS, this.id);
55         }
56     }
57
58     @Override
59     public boolean sameIdentityAs(DesignerOrder other) {
60         return this.equals(other);
61     }
62 }

你可以发现业务对象的代码有:

  • 业务对象内部状态,即它包含的属性(字段)。
  • 业务对象方法,即业务规则的实现,业务对象方法一般完成业务对象状态变更。
  • 业务对象关联,包含关联业务对象的属性或者字段。

关于领域层的编码模式,在下文会详细介绍。

3 基础设施层

基础设施层为上层提供通用的技术能力,包括消息传递、缓存、远程调用、分布式事务、持久化、UI绘制等。以下是持久化实现的一段代码。它以整个实体作为存储单元(注意:准确的说,是以聚合根为存储单元,后续详细介绍)。

 1 @Repository
 2 public class DesignerOrderRepositoryImpl implements DesignerOrderRepository {
 3     private static final String DESIGNER_ORDER_TABLE = "designer_order";
 4
 5     @Autowired
 6     private DesignerOrderMapper designerOrderMapper;
 7
 8     @Override
 9     public void create(DesignerOrder order) {
10         if (designerOrderMapper.create(order) == 0) {
11             TableException.throwTableException(DESIGNER_ORDER_TABLE, TableOperation.CREATE);
12         }
13     }
14
15     @Override
16     public DesignerOrder selectByKey(int id) {
17         DesignerOrder order = designerOrderMapper.selectByKey(id);
18         buildConnection(order);
19         return order;
20     }
21
22     @Override
23     public DesignerOrder selectOneBySpecification(DesignerOrder example) {
24         DesignerOrder designerOrder = designerOrderMapper.selectOneBySpecification(example);
25         buildConnection(designerOrder);
26         return designerOrder;
27     }
28
29     @Override
30     public List<DesignerOrder> selectBySpecification(DesignerOrder example) {
31         List<DesignerOrder> designerOrders = designerOrderMapper.selectBySpecification(example);
32         buildConnection(designerOrders);
33         return designerOrders;
34     }
35
36     @Override
37     public void update(DesignerOrder order) {
38         if (designerOrderMapper.update(order) == 0) {
39             TableException.throwTableException(DESIGNER_ORDER_TABLE, TableOperation.UPDATE);
40         }
41     }
42 }

4 结论

通过以上的分层示例,我们可以总结出来领域驱动设计的代码基本模式:

  1. 上层应用层通过Factory、Repository、领域对象协同来完成用户任务。
  2. 通过Factory、Repository来处理领域对象生命周期管理,包括领域对象创建、加载、持久化。
  3. 领域对象由状态和对状态变更的操作组成,它是业务规则的实现。在这里,字段代表状态,方法代表状态变更。领域对象状态变更后,由Repository进行持久化。如何涉及多个领域对象状态变更的一致性,则这几个领域对象的状态变更将组合在一起,由Repository进行一致性变更。
  4. 基本模式:新建——通过Factory创建领域对象,调用领域对象方法更改状态,使用Repository将领域对象持久化;变更——通过Repository加载领域对象,调用领域对象方法更改状态,使用Repository将领域对象持久化。

解构领域驱动设计(二):领域驱动设计的核心之分层架构-LMLPHP

01-13 03:47