分布式系统中出现了一系列独特但又相关的问题。分布式系统的状态在任何时候都不能保持完全一致。我们始终保持聚合内部一致,而异步的进行其他更改。当更改在网络的节点间传播时,可能很难解决无序或来自不同来源的多个更新。
因此:
将关于领域中活动的模型信息视为一系列离散事件。将每个事件表示为一个领域对象。这些不同于系统事件,它们反映了软件本身的活动,虽然通常系统事件与领域事件相关联或者作为领域事件的响应的一部分,或者作为将领域事件的信息携带到系统中的一种方式。
领域事件是领域模型的一个完整的部分,是领域中发生的事情的表示形式。忽略不相关的领域活动,同时明确领域专家想要跟踪或者被通知的事件,或者与其他模型对象中的状态改变相关联的事件。
在分布式系统中,实体的状态可以从特定节点的当前已知的领域事件中推断出来,从而在没有关于整个系统的完整信息的情况下得到相关的模型。
领域事件通常是不可变的,因为它们是过去的某种事物的记录。除了对事件的描述之外,领域事件通常包含事件发生时间的时间戳以及事件涉及的实体的身份标识。此外,领域事件通常具有单独的时间戳,指示事件何时进入系统以及使其进入系统的人的身份标识。如果有用,领域事件的身份标识可以基于这些属性的一些集合。所以,例如,如果同一个事件的两个实例到达一个节点,则它们可以被识别为相同的。
服务
有时候,这不是一回事。领域的一些概念由模型作为对象是不自然的。强制所需的领域功能成为实体或者值对象的职责,要么篡改基于模型的对象的定义,要么添加无意义的虚拟对象。
因此:
当领域中的重要流程或转换不是实体或值对象的自然职责时,添加一个操作到模型中作为一个单独的接口同时声明为一个服务。定义一个服务契约,一组关于与服务交互的声明。用一个特定限界上下文的通用语言来陈述这些声明。给服务一个名字,这也成为通用语言的一部分。
模块
每个人都使用模块,但很少将它们视为模型的完整部分。代码被分解成各种类别,从技术架构的各个方面到开发人员的工作任务。即使是做了很多重构的开发人员也倾向于使用项目早期构思的模块。
耦合和凝聚力的解释倾向于使它们听起来像是技术指标,根据关联和相互作用的分布进行机械的判断。然而,这不仅仅是将代码划分为模块,还包括概念。一个人一次可以思考多少事情是有限的(因此耦合度低),不连贯的想法片段很难被理解为一个无差别的想法(因此具有很高的内聚性)。
因此:
选择能够讲述系统故事的模块,并包含一系列内聚的概念。让模块名称成为通用语言的一部分。模块是模型的一部分,它们的名称应反映对领域的洞察。
这通常会导致模块之间的低耦合,但是如果它不寻找一种方法来改变模型来分解概念,或者是一个被忽视的概念,它可能是一个能够以有意义的方式将元素组合在一起的模块的基础。在可以被独立地理解和推理的概念上寻求低耦合。根据高层领域概念对模型进行细化直到它被划分,并将相应的代码解耦。
聚合
要保证复杂关联模型中对象变化的一致性是很困难的。他们能够被是概念上的构成部分的其它对象的变化所掩盖。在多个服务器之间分发对象或设计异步事务时会出现类似的问题。
因此:
将实体和值对象集中到聚合中并在周围定义边界。选择一个实体作为每个聚合的根,并允许外部对象仅保留对根的引用(对内部成员的引用仅在一个操作中返回出去才能使用)。定义聚合的属性和不变量作为一个整体,并将这个约束的责任赋予根【这里指的是聚合根】或某种指定的框架机制。
使用相同的聚合边界来管理事务和分配。
在一个聚合边界内,同步地应用一致性规则。 跨越边界,异步地处理更新。
在一台服务器上共同维护一个聚合。允许不同的聚合在节点间分配。