实现业务一致的领域模型

本文以电商领域为例,介绍如何通过状态机模型来完善领域模型缺失的业务方法,同时在实现上保持领域模型的业务一致性。

初始建模

为电子商城建模,得到领域模型Order、OrderStatus、OrderItem、Product,每个模型都具备相应的业务属性:

领域对象的生命周期会经历不同的阶段,我们常常会使用UML状态图来对领域模型生命周期进行建模:

可以看到,订单Order初始由用户初始创建后,状态为待付款,用户支付订单后,Order状态变更为待发货,商家发货后,Order状态变更为待签收,用户确认收货后,Order状态变更为已完成

完善模型

识别状态模型上的Action并将其转化为领域模型方法:

  • 创建订单: Order(items)构造器,状态变更为待付款Paying,更新statusTimestamp为当前时间
  • 支付:pay(),状态变更为待发货Shipping,更新statusTimestamp为当前时间
  • 发货:ship(),状态变更为待收货Deliverying,更新statusTimestamp为当前时间
  • 收货:delivery(),状态变更为已完成Completed,更新statusTimestamp为当前时间

实现模型

在设计阶段,我们得到了领域模型上应该有哪些业务方法,现在我们只需要将方法填充到Order对象,并在方法内部实现即可:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@Getter
public class Order {
	private String number;
	private OrderStatus status;
	private Long statusTimestamp;
	private List<OrderItem> items;

	// 创建订单
	public Order(List<OrderItem> items) {
		this.number = UUID.randomUUID().toString();
		this.items = items;
		this.status = OrderStatus.Paying; // 这里初始化领域对象的状态为待付款
		this.statusTimestamp = System.currentTimeMillis();
	}

	// 支付
	public void pay() {
		if (this.status != OrderStatus.Paying) {
			throw new IllegalStateException();
		}
		this.status = OrderStatus.Shipping;
		this.statusTimestamp = System.currentTimeMillis();
	}

	// 发货
	public void ship() {
		if (this.status != OrderStatus.Shipping) {
			throw new IllegalStateException();
		}
		this.status = OrderStatus.Deliverying;
		this.statusTimestamp = System.currentTimeMillis();
	}

	// 收货
	public void delivery() {
		if (this.status != OrderStatus.Deliverying) {
			throw new IllegalStateException();
		}
		this.status = OrderStatus.Completed;
		this.statusTimestamp = System.currentTimeMillis();
	}
}

public enum OrderStatus {
	Paying,
	Shipping,
	Delivering,
	Completed
}

@Getter
@RequiredArgsConstructor
public class OrderItem {
	private final Product product;
	private final int quantity;
}

@Getter
@RequiredArgsConstructor
public class Product {
	private final String code;
	private final String name;
}

可以看到,Order模型的类、属性、方法定义均和建模设计阶段保持了一致性。

接下来是什么?

在编码实现以后,我们将会进行测试。下一篇将介绍如何为领域模型编写单元测试。