前面我们已经分析设计出了实体对象和对应的限界上下文,这两个应该是可以同时设计出来的,如果只有领域和实体而没有对应的限界上下文去管理,那么可以说这个设计就是未完成的。
当然对于实体对象,在限界上下文中就有内部和外部之分,比如:订单还和报表,库存,物流(电商系统),对账,结算(支付系统)等等相关,按照界限上下文的划分,这些肯定不是一个上下文,但他们需要用到订单对象,那么订单这个实体对象对于上面这些限界上下文中就是外部实体对象。
针对外部实体对象,我们可以将其称之为:值对象。可以看出来,值对象是一个相对的定义,即这个对象在其中一个上下文中,可能是领域对象,那么在其他上下文中则是值对象。而且对于被定义为值对象的限界上下文,值对象的属性信息都基本上是不可变的(比如订单对象,在做为值对象的时候,它的信息已经基本生成,而且基本可以不变)。而且上面每一个限界上下文中,订单这个值对象的属性的构成也是不一样的,比如在对账微服务中,我们只需关心这个订单数据的金额以及其他订单费用;而在结算系统中,我们更加关心的是订单的状态等信息,所以每一个限界上下文对于订单的属性构成也是不一样的,不光不一样,而且就订单信息而言,这里的每一个限界上下文都只是需要订单的信息,而不会去更改订单的信息。所以在这些限界上下文中,这个订单的数据就是值对象。值对象在这个限界上下文中是不变的。
在限界上下文中,针对这种值对象的获取方式,可以是调用远程服务获取,也可以通过缓存,事件传递,或则调用时的参数等等方式获取。甚至对于值对象可以进行冗余似的存储。
还有一种值对象,它天生就是不变的,即便是在它自己的限界上下文中,他也是值对象,因为他一旦产生就不会变了,比如一个日志信息,即便是日志系统产生了日志,但也是一产生就不再变化,所以日志对象在日志系统上下文中也是一个值对象。
总结一下:在某一个限界上下文中,我们会有这个上下文需要维护实体对象的信息,而不属于这个限界上下文维护的实体对象则是值对象。在不同限界上下文中,会依赖的值对象的构成也是不同的,但不能超出这个值对象在做为领域对象的属性。
所以值对象,一般是相对限界上下文来说的。但最重要的是请记住他的特点:不可变。
那么到了这一步,我们可以对整个系统通过设计分析,逐条划分出哪些限界上下文有哪些是领域实体,依赖哪些值对象,可以慢慢的得出。
那么这么划分领域实体和值对象有什么好处?
第一实体对象的逻辑处理是内聚的,比如上面说的订单,那么订单的一切变更逻辑都会聚合在订单中心里(或交易中心里),那么对于维护这种逻辑,我们在工程上就会减少对应出错的概率。
第二对于值对象,在一些场景下,微服务可以针对一些值对象进行缓存处理,这样减少了服务依赖,增加了服务的健壮性,而且通过区分实体对象和值对象,我们也可以进一步发现流程梳理的漏洞(比如这个值对象从哪里来等等)。
第三,减少了对于数据库依赖,特别是锁的依赖。因为随着实体的划分清晰,大部分的事务处理集中在同一个微服务中,而服务间的依赖关系,更多的是通过不可变的值对象,不可变对象在多线程中是线程安全的,这样减轻了部分事务锁等待出现的概率。
最后通过上面的详细梳理,大概能得到下面这个图
这个图中:圆圈代表实体,方块代表值对象,箭头代表创建关系。
可以看到,这个时候,我们的对象比之前表格中要更多,这是因为我们基于前一章表格的内容做了更多的细化。