将领域模型最终转设计,可以落实到三种类型的对象设计,
- 服务,2. 实体,3. 值对象,
然后进行贫血模型与充血模型的设计思路。
除此之外还需要有聚合,仓库,工厂的设计。
聚合:是领域驱动设计中一项非常重要的设计与概念,它表达的是真实世界中那些整体与部分的关系。
比如订单与订单明细表单与表单明细、发票与发票明细。
以订单为例,在真实世界中,订单与订单明细本来是同一个事物,订单明细是订单中的一个属性。但是,由于在关系型数据库中,没有办法在一个字段中表达一对多的关系,因此必须将订单明细设。
在领域模型的设计中,我们又将其还原到真实世界,以聚合的形式进行设计。
在领域模型中,即将订单明细设计成订单中的一个属性。
有了这样的设计,订单的时候将不再单独,而是将订单明细创建在订单中。
订单的时候应当同时保存订单表与订单明细表,并放在同一事务中。
查询订单时,应当同时查询订单表与订单明细表,并将其装配成一个订单对象,这时候订单就作为一个整体进行操作,而不需要再单独去操作订单明细。
也就是说,对订单明细的操作是封装在订单对象内部的设计实现。
对于客户程序来说去使用订单对象就好了。
这就包括了作为属性去访问订单对象中的订单明细,而不再需要关注它内部是如何操作的。
按照以上思路进行的设计就是聚合。
当创建或更新订单时,在订单对象中填入或更新订单的明细就好了。
当保存订单时,只需要将订单对象作为整体去保存,而不需要关心订单数据是怎么保存,保存到哪几张表中,是不是有事物。保存数据库的所有细节都封装在了订单对象内部。
当删除订单时,删除订单对象就好了,至于如何删除订单名细是订单对象内部的,实现,外部的程序不需要关注。
当查询或装载订单时,客户程序只需要根据查询语句或id查询订单对象就好了。查询程序会在查询过程中自动的去补填订单对应的订单明细。
聚合体现的是一种整体与部分的关系。
正是因为有这样的关系,在操作整体的时候,整体就封装了对部分的操作,但并非所有对象间的关系都有整体与部分的关系。而那些不是整体与部分的关系是不能设计成聚合的。
因此,正确的识别聚合关系就变得尤为重要。
所谓的整体与部分的关系就是当整体不存在时,部分就变得没有了意义。
部分是整体的一个部分,与整体有相同的生命周期。
比如只有创建了这张订单,才能创建它的订单明细,如果没有了这张订单,那么它的订单明细就变得没有意义,就需要同时删除掉。
这样的关系才具备整体与部分的关系,才是聚合。
比如订单与用户之间的关系就不是聚合,因为用户不是创建订单时才存在的,而是在创建订单时早就存在了。
当删除订单时,用户不会随着订单的删除而删除,因为删除了订单,用户依然还是那个用户。
那么饭店和菜单的关系是不是聚合关系呢?
这就要看系统如何设计了。如果系统设计成每个饭店都有各不相同的菜单,每个菜单都是隶属于某个饭店,则饭店和菜单是聚合关系。
让各个饭店都有宫保鸡丁,但每个饭店都是各自不同的宫保鸡丁。
比如在描述图片或价格上的不同,甚至在数据库中也是有各不相同的记录。
这时要查询菜单就要先查询饭店,离开了饭店的菜单是没有意义的。
但是饭店和菜单还可以有另一种设计思路,就是所有的菜,菜单都是公用的。
去每个饭店只是选择有还是没有这个菜品,这时系统中有一个菜单对象,宫保鸡丁只是这个对象中的一条记录,其他各个饭店,如果他们的菜单上有宫保鸡丁,则去引用这个对象,否则不引用,这是菜单就不再是饭店的一个部分。没有这个饭店,这个菜品依然存在,就不再是聚合关系。
因此,判断聚合关系最有效的方法就是去探讨如果整体不存在时,部分是否存在,如果不存在就是聚合。反之,则不是。
有了聚合关系,部分就会被封装在整体里面。
这时就会形成一种约束,即外部程序不能跳过整体。
需要通过整体,整体就下一步唯一入口,被称为聚合根。
也就是说一旦将对象间的关系设计成了聚合,那么外部程序只能访问聚合根,而不能访问聚合中的其他对象。
这样带来的好处就是当聚合内部的业务逻辑发生变更时,只与聚合内部有关,只需要对聚合内部进行更新,与外部程序无关。
从而有效降低了变更的维护成本,提高了系统的设计质量。
需要注意的是,这样的设计并非任何情况下都有效。
比如,在管理订单中,对订单进行增删改时,聚合是有效的。
但是如果要统计销量、分析销售趋势、销售占比时,则需要对大量的订单明细进行汇总进行统计。如果每次对订单明细的汇总与统计都必须经过订单的查询,必然使得查询统计变得效率极低而无法使用。
因此领域驱动设计通常适用于增删改的业务操作,不适用于分析统计。
在一个系统中,增删改的业务可以采用领域驱动设计,但在非增删改的汇总,分析场景中则不必采用领域驱动的设计。直接sql查询就好了,也就不必再遵循聚合的约束。
网友评论