通过语言的暗喻发现隐藏的DDD模型 - verraes


这是DDD专家 Mathias VerraesRebecca Wirfs-Brock最新文章,他们擅长从人类通用语言中发现模型,这是不同于事件建模和事件风暴的第三种DDD建模方法,也是比较更接近DDD原著的含义。
该文讲述了如何在一堆数据分析技术人员当中引入建模会议,通过参会者的语言沟通,发现了复杂系统中涌现的一种现象,人们不知道它叫什么情况下,一般是使用类比和比喻修辞语句形容它,这是一种隐喻用法,是发挥想象力以后的直觉判断,如水花这个词语就是用来比喻河流在流动过程终涌现的一种现象,如同植物花朵一样。两位专家在支付版权费领域中发现了他们忽视的Trust信任模型,这个模型并不存在实际领域中,但是人们在构建这个领域的软件系统时,却涌现出一种渴望,需要一种模型来作为索赔的前提条件,客户信任度为零与满分决定了后面不同的支付流程。
原文大意如下,点击标题见原文:

Mathias 为一位客户提供咨询,客户是一位经纪人:负责向版权所有者支付使用他们内容的费用。为此,他们弄清楚了作品的版权所有者是谁。然后他们跟踪使用索赔或支付,计算欠款,收款并付款。了解谁拥有什么是他们业务中最棘手的部分之一。
——“这只是一个技术问题。”
——“但没有人真正了解它是如何运作的!”
— “我们中的一些人了解其中的大部分内容。这恰好是一个复杂的问题。”
——“无论如何,让我们做一点建模吧。”
几周后,我们有了一个丰富的模型。它有清晰的概念,并且更容易理解。业务利益相关者开始关注,并积极参与建模。他们看到了伟大的想法,可以描述他们想要什么。事情是这样发生的。
 

案例分析
确定版权所有权(DDD核心子域)是一个复杂的数据匹配过程,它从多个数据源中提取数据:

  • 公司自行研究
  • 离岸数据清洗
  • 来自维基风格来源的公开数据
  • 公开可用的精选数据
  • 私人资源,公司为此支付了许可费
  • 个人直接提交
  • 代表版权持有人的机构

该公司存在数据质量问题。由于数据来源的多样性,任何说法都没有单一的真实来源。数据往往不完整和不一致。最重要的是,存在欺诈的可能性:不良行为者声称拥有作者作品的所有权。
大多数人都是出于善意行事。即便如此,数据总是会很乱,整理起来也需要付出相当大的努力。数据在不断变化:即使作品的所有权很少发生变化,数据却发生了变化。
 
数据匹配
工程师们一直在改进“数据匹配”。这就是他们所说的协调不一致的过程,并就谁拥有什么以及谁必须向谁支付费用提供清晰的决策。
他们使用了 EventSourcing,并且可以轻松地根据历史数据重放新的匹配算法。数据匹配算法在不同数据源中匹配相同作品的相似声明。当多个数据源同时发生时,匹配成功。
但是有时匹配很差:一个作品的50% 的来源指向一个所有者,50% 指向另一个所有者,仅根据这些信息,无法确定这个作品的版权所有者是谁。但是通过使用历史数据,该算法可以更频繁地找出哪些来源是成功匹配的一部分。他们可以赋予这些来源更多的权重,并朝一个方向或另一个方向倾斜。这样,即使 50% 的消息来源声称 A 为所有者,50% 的消息来源声称为 B,也可以找到答案。
 
领域建模
代码的职责是:提取数据、过滤、重新格式化、解释和应用匹配规则。
所有的情况和规则使数据匹配变得非常复杂。只有少数工程师知道它是如何工作的。Mathias 注意到工程师无法解释它是如何工作得很好。与他交谈的业务人员根本无法解释有关该系统如何工作的任何内容。他们简单地将其称为“数据匹配”。团队并不关心这个。在他们眼里,复杂不过是他们不得不面对的事情。
Mathias 提议召开白板建模会议。最初,工程师们反对。毕竟,他们不觉得这是一个业务领域,只是一个纯粹的技术问题。然而,Mathias 认为,结果的质量决定了谁得到了多少报酬,而错误意味着客户最终会转向竞争对手。因此,即使数据匹配是技术性的,它在核心域中也发挥了重要作用。关于它的知识是粗略的,工程无法解释它,商业不理解它。正因为如此,他们很少讨论它,当他们讨论的时候,它是纯粹的技术术语。如果沟通很困难,如果对话很麻烦,你就缺乏一个好的共享模式。
通过建模,匹配过程对工程师来说变得不那么不透明了。我们对提取数据、处理数据、识别匹配和做出决定的不同步骤进行了更清晰的区分。该模型包括来源、索赔、对账、例外情况。我们也在白板上绘制了匹配规则,使这些规则在模型中明确一流的概念。随着匹配过程变得更加清晰,导致系统设计的基本思想开始浮出水面。从“是什么”,我们转向“为什么”。这使我们处于开始发现抽象的有利位置。
 
Trust模型
渐渐地,他们构建算法的假设前提(根据)在对话中浮出水面:
我们陈述了这些假设,将它们写在便签上,然后放在白板上。一个公认的假设是,当数据源经常与其他来源一致时,将来出错的可能性较小。如果一个来源更可靠,它应该更受信任,因此来自该来源的声明在谁拥有所有权的决定中具有更大的权重。
在进行领域发现和建模时,观察并倾听语言中的微妙之处是很好的。在这些对话中,非正式地使用了诸如“可靠”、“信任”、“增加权重”和“决策”等词。
在这些情况下有效的是对语言有一种健康的痴迷。
将此语言添加到白板。
提问:这个词是什么意思?
通过这些讨论,“信任Truts”的概念变得越来越重要。它在白板模型中变得明确。
它是有形的:你可以看到它,指向它,移动它。你可以开始讲关于信任的故事。为什么一个来源会更受信任?什么会破坏这种信任?我们可以发现哪些边缘情况会以不同的方式影响信任?
  
Trust为对象
在下一次建模会议中,我们谈论了很多信任。从人们随意加入谈话的一个词,它已经演变成一个有意义的词。Mathias 提出了一个小小的思想实验:如果Trust是代码中的一个实际对象会怎样?那会是什么样子?很快,一个简单的信任模型出现了。
信任是一个值对象,它的值代表我们对数据源的信任“数量”,或者我们对作品或使用的主张的信任,或者我们对提出主张的人的信任:
信任度的衡量标准为 -5 到 5。
该数字决定了版权支付是否获得批准,是否需要其他来源来确认,或者公司是否需要做进一步的研究。
这是一个重大的思想转变。
Trust模型为我们提供了一种新的方式来思考我们的核心领域:根据谁最能赢得我们的信任就向其付款。
从白板概念转变为使信任Trust成为设计中的基本元素。该团队清理了分布在数据匹配代码中的临时逻辑,并将其替换为单个信任概念。
旧代码也会动态计算相似的值以确定“匹配”。这些计算在代码中传播和复制,隐藏在许多分支中。团队没有看到所有这些值和计算实际上是同一基本概念内几个方面。他们没有看到可以共享计算,无论您是匹配来源、人员还是声明,没有共享的抽象。
  
信任Trust是一个流程
团队正在设计一个 EventSourced 系统,因此很自然地,话题转向了哪些事件会影响 Trust。
信任如何随时间演变?
过去在旧模型中匹配的索赔或支付,现在变成了对我们的索赔信任产生的积极或消极影响的事件。
赢得信任(或失去信任)现在被认为是一个流程。
新的索赔动作是该流程中的一个事件。信任Trust现在被视为信任收益流程的快照。
如果索赔被拒绝,但出现了新的证据,则信任度增加并且索赔获得批准。
某些来源,例如公司为其购买许可证的私人数据库,高度可信且稳定。对于其他人,比如人们可以提交声明的 wiki 风格的资源,信任更加不稳定。
 
业务参与
在讨论新的信任Trust模型和信任建立概念期间,团队定期返回业务以确保这些概念有效。他们询问了他们应该如何分配信任以及他们应该使用什么标准的见解。我们看到了一个有趣的效果:企业中的人们投入到这些对话中并加入了建模会议:
数据匹配从对话中消失了,Trust 接管了。
人们普遍对能够分配和发展信任感到兴奋。工程师的新模型成为企业内部的共享模型。
 
信任是一种算术
版权经纪领域专家开始向团队抛出场景:如果信任度为 0 的来源 A 提出的声明得到信任度为 5 的来源 B 的证实,该怎么办?
声明本身现在受到高度信任,但对来源 A 的影响是什么?一只燕子不会造就春天,因此源 A 肯定不应该被授予与源 B 相同级别的信任。
另一方面,确认信任的重复模式应该反映对源 A 的更高信任。
在这些持续探索中,业务和工程人员列出了不同事件如何影响信任的规则,并对其进行了编码。
通过查看代码中的规则,出现了一个新想法:
信任Trust可以有自己的算法:一组定义信任如何积累的规则。
例如,信托为 3 的索赔现在被信托为 5 的索赔所证实,现在将被分配一个新的信托 4。更大的算术集解决了证实索赔的索赔的各种排列,来源证实了来源,以及随着时间的推移证实的模式。
Trust 对象封装了这个算法,并为其管理属性和行为。
从一个贫血的 Trust 对象,我们现在得到了负责所有这些操作的更丰富的 充血Trust 模型。该团队提出了多态 Strategy 对象。这些使他们能够更换不同的机制来分配和发展信任。旧的数据匹配代码将获取和存储信息与庞大的逻辑混合在一起。现在,该团队发现很容易将其分成一个处理管道的层,与干净的 Trust 模型分开。
 
模型的演变
总之,下面这是演变:
  1. 计算匹配值的临时代码。
  2. 在解释当前系统如何工作的对话中使用信任。
  3. 信任作为代码中的值对象。
  4. 将信任作为一个流程,通过分配新信任值的事件(例如找到匹配的声明)来发展信任。
  5. 信任作为业务和工程之间的共享术语,取代了技术数据匹配的旧语言。
  6. 探索如何使用更真实的场景分配信任。
  7. 构建控制信任计算的算法。
  8. 分配信任的多态策略。

当您找到更好、更有意义的抽象时,它就会成为催化剂:它支持其他建模结构,允许围绕该概念形成其他想法。这需要探索、编码、对话、尝试场景,......没有实现这一目标的黄金秘诀。你需要对可能性持开放态度,并花时间去做。
 
好的比喻
我们从原始代码转变为基于信任新概念的模型。但是这个Trust这个概念是什么东西呢?信任是一个比喻。1真正的信任是人类的情感,部分是非理性的。您本能地信任某人,并且出于可能会改变的完全主观的原因。机器没有这些情绪。我们的系统中有一个人工指标,有算法来操纵它,我们将其命名为 Trust。这是一个代理词。
这个比喻可以实现更紧凑的对话,工程师和领域专家都可以讨论信任,而不会在技术细节上彼此迷失,这一事实证明了这一点。“这个消息来源的说法得到了其他消息来源的反复证实”这样的句子被替换为“这个消息来源已经建立了信任”,所有人都知道这意味着什么。
这个比喻允许我们处理相同程度的复杂性,但我们可以推理确定信任,而无需了解使用它的每个细节。对于我们这些没有爱因斯坦大脑的人来说,现在处理代码要容易得多,它降低了认知负担。
在正确的上下文中使用一个好的比喻,例如信任,使我们能够实现以前无法轻松完成的事情。该团队重新考虑了一项功能,该功能允许他们更换不同的策略来匹配声明。最初他们驳回了这个想法,因为在旧代码中,构建成本高得令人望而却步。这将导致巨大的条件树和对共享状态的庞大依赖。他们必须非常小心,而且很难测试这种逻辑。使用新模型,换出多态 Strategy 对象是微不足道的。新模型允许测试像信任对象这样的低级单元、像信任建立过程这样的高级逻辑和单独的索赔策略,每个测试都保留在一个抽象级别。
我们的 Trust 模型不仅可以更好地组织细节,而且还简洁明了。我们可以转到代码中的一个点,并知道某些事情是如何确定的。Trust 对象在代码的一个地方计算它自己的值。我们不必查看代码中的 20 个不同条件来理解行为;相反,我们可以查看单一策略。发现错误要容易得多,这反过来又有助于我们使代码更正确。
一个好的模型可以帮助您推理系统的行为。一个好的比喻可以帮助您推理系统所需的行为。
信任的比喻开启了解决复杂性的途径。我们通过仔细聆听用于描述解决方案的语言,在示例中使用该语言并尝试思想实验来发现它。我们不再匹配数据,我们正在确定信任并使用它来解决索赔。我们现在没有对规则进行编码,而是对它们进行编码。因此,我们是更好的版权经纪人。
 
不好的比喻
警惕糟糕的、不恰当的比喻。想象一下,该团队以“星级”作为隐喻。当然,它也可以用作量化,但它基于流行度,并计算平均值。我们仍然可以构建与 Trust 模型相同的所有行为,但有很多奇怪的规则,例如“我们自己的来源获得 20 个五星级评级”。当您注意到必须将问题空间的元素强加到隐喻中,并且您想说的内容与该隐喻允许您说的内容之间存在摩擦时,您需要摆脱它。没有一个比喻能完美契合,但一个糟糕的比喻会让你陷入尴尬的对话,而不会让你明白。
更棘手的是,每当你引入一个新的比喻时,一开始可能会很尴尬。在我们的案例研究中,信任并没有立即成为一个被充分探索和接受的比喻。在采用一个新的好比喻的早期斗争和一个根本不好的比喻之间有一条微妙的界限。继续尝试,努力使用你的新比喻,看看它是否能给你带来解释力,如果没有,不要害怕放弃它。
有时,根本没有任何好的比喻,甚至找不到更简单的模型。在这些情况下,你只需要处理它。没有找到任何简化。您只需要制定所有规则,列出所有案例,并按原样处理复杂性。
 
结论
为了找到好的隐喻,把自己放在一个你会在谈话中注意到它们的位置。邀请不同的角色参与您的设计讨论。对语言有一种健康的痴迷:这是什么意思?这是最好的表达方式吗?观察这种语言,听听人们随意说的术语。捕捉人们使用的任何隐喻。在谈话中加强他们,但如果你觉得你有足够的力量,准备好放弃他们。隐喻能带来清晰吗?它是否有助于您更好地表达问题?尝试场景和边缘情况,即使它们极不可能。他们会教你隐喻的局限性。然后提炼比喻,同意一个确切的意思。在您的模型中使用它,然后将其转换为您的代码和测试。隐喻是语言的运作方式,我们的大脑如何赋予意义,
Mathias VerraesRebecca Wirfs-Brock撰写。原文击标题