领域模型的范围

领域模型不同于数据模型,数据模型中没有方法,领域模型中有方法。方法可以隐藏数据值,避免数据带来的歧义。比如这么一个例子,数据项允许打印一次,打印完成后,就不再允许打印了,如果还需要打印,需要由管理员重置打印状态。这个功能可以通过设置一个属性来完成,比如:

1
2
3
4
5
6
7
setPrintState(state){
this.printState=state
}

getPrintState(){
return this.printState
}

这种情况下,领域模型与数据模型没什么区别,通过领域模型没有办法知道打印相关的业务要求。所有相关的业务约束在应用服务或者客户端完成。由于不同的应用服务或客户端对业务的解释不同,那么保存在领域模型中的数据就可能不同,通过领域模型,无法理解完整的业务。如果改为方法,领域模型的描述更加清晰:

1
2
3
4
5
6
7
8
9
10
11
12
printFinished(){
setPrintState("打印完成")
}

isPrintable(){
return getPrintState()!="打印完成"
}

resetPrintable(){
setPrintState("可以打印")
}

使用isPrintable()判断是否可以打印,打印完成后调用pringFinished(),这样可以禁止打印,管理员可以通过调用restePrintable()重置打印状态。

从上面的例子可以看出,使用方法,将领域模型的属性进行了封装,不对外暴露,方法的调用不会产生歧义。

然而,并不是所有的情况都需要在领域模型中进行描述,我们看下面一个例子。 我们为任务建模,每个任务(Task)包括若干个工序(TaskItem),任务是聚合根,任务中有一个数组保存工序。我们操作工序需要通过任务,对工序的操作包括增加工序、删除工序、修改工序的顺序等等,删除还包括软删除和硬删除,软删除是设置删除标记,删除后还可以恢复,硬删除是从数组永久删除。

这种情况下,领域模型最好只保存数据,因为具体的操作方法在应用层甚至客户端完成,因为这些操作方法与客户端的实现手段有关。有可能所有的工序在客户端一次编辑完成,一次发送的服务端进行保存,也有可能是每编辑一条记录,都需要访问一次服务端,领域模型必须为实现手段的不同留有余地。