《领域驱动设计:软件核心复杂性应对之道(修订版)》—第2章 2.1节模式:Ubiquitous Language

  1. 云栖社区>
  2. 博客>
  3. 正文

《领域驱动设计:软件核心复杂性应对之道(修订版)》—第2章 2.1节模式:Ubiquitous Language

异步社区 2017-05-02 10:39:00 浏览1639
展开阅读全文

本节书摘来自异步社区《领域驱动设计:软件核心复杂性应对之道(修订版)》一书中的第2章,第2.1节模式:Ubiquitous Language,作者【美】埃里克•埃文斯(Eric Evans), 马利伟 , 万龙,更多章节内容可以访问云栖社区“异步社区”公众号查看。

第2章 交流与语言的使用
领域驱动设计:软件核心复杂性应对之道(修订版)
领域模型可成为软件项目通用语言的核心。该模型是一组得自于项目人员头脑中的概念,以及反映了领域深层含义的术语和关系。这些术语和相互关系提供了模型语言的语义,虽然语言是为领域量身定制的,但就技术开发而言,其依然足够精确。正是这条至关重要的纽带,将模型与开发活动结合在一起,并使模型与代码紧密绑定。

这种基于模型的交流并不局限于UML(统一建模语言)图。为了最有效地使用模型,需要充分利用各种交流手段。基于模型的交流提高了书面文档的效用,也提高了敏捷过程中再度强调的非正式图表和交谈的效用。它还通过代码本身及对应的测试促进了交流。

在项目中,语言的使用很微妙,但却至关重要……

23
2.1 模式:Ubiquitous Language
首先写下一个句子,

然后将它分成小段,

再将它们打乱并重新排序。

仿佛是巧合一样,

短语的顺序对意思完全没有影响。
——Lewis Carroll, “Poeta Fit, Non Nascitur”

要想创建一种灵活的、蕴含丰富知识的设计,需要一种通用的、共享的团队语言,以及对语言不断的试验——然而,软件项目上很少出现这样的试验。

未标题-1

虽然领域专家对软件开发的技术术语所知有限,但他们能熟练使用自己领域的术语——可能还具有各种不同的风格。另一方面,开发人员可能会用一些描述性的、功能性的术语来理解和讨论系统,而这些术语并不具备领域专家的语言所要传达的意思。或者,开发人员可能会创建一些用于支持设计的抽象,但领域专家无法理解这些抽象。负责处理问题不同部分的开发人员可能会开发出各自不同的设计概念以及描述领域的方式。

由于语言上存在鸿沟,领域专家们只能模糊地描述他们想要的东西。开发人员虽然努力去理解一个自己不熟悉的领域,但也只能形成模糊的认识。虽然少数团队成员会设法掌握这两种语言,但他们会变成信息流的瓶颈,并且他们的翻译也不准确。

在一个没有公共语言的项目上,开发人员不得不为领域专家做翻译。而领域专家需要充当开发人员与其他领域专家之间的翻译。甚至开发人员之间还需要互相翻译。这些翻译使模型概念变得混淆,而这会导致有害的代码重构。这种间接的沟通掩盖了分裂的形成——不同的团队成员使用不同的术语而尚不自知。由于软件的各个部分不能够浑然一体,因此这就导致无法开发出可靠的软件(参见第14章)。翻译工作导致各类促进深入理解模型的知识和想法无法结合到一起。

24
如果语言支离破碎,项目必将遭遇严重问题。领域专家使用他们自己的术语,而技术团队所使用的语言则经过调整,以便从设计角度讨论领域。

日常讨论所使用的术语与代码(软件项目的最重要产品)中使用的术语不一致。甚至同一个人在讲话和写东西时使用的语言也不一致,这导致的后果是,对领域的深刻表述常常稍纵即逝,根本无法记录到代码或文档中。

翻译使得沟通不畅,并削弱了知识消化。

然而任何一方的语言都不能成为公共语言,因为它们无法满足所有的需求。

所有翻译的开销,连带着误解的风险,成本实在太高了。项目需要一种公共语言,这种语言要比所有语言的最小公分母健壮得多。通过团队的一致努力,领域模型可以成为这种公共语言的核心,同时将团队沟通与软件实现紧密联系到一起。该语言将存在于团队工作中的方方面面。

Ubiquitous Language(通用语言)的词汇包括类和主要操作的名称。语言中的术语,有些用来讨论模型中已经明确的规则,还有一些则来自施加于模型上的高级组织原则(如第14章和第16章要讨论的Context Map和大型结构)。最后,团队常常应用于领域模型的模式名称也使这种语言更为丰富。

模型之间的关系成为所有语言都具有的组合规则。词和短语的意义反映了模型的语义。

开发人员应该使用基于模型的语言来描述系统中的工件、任务和功能。这个模型应该为开发人员和领域专家提供一种用于相互交流的语言,而且领域专家还应该使用这种语言来讨论需求、开发计划和特性。语言使用得越普遍,理解进行得就越顺畅。

至少,我们应该将它作为目标。但最初,模型可能不太好,因此无法很好地履行这些职责。它可能不会像领域的专业术语那样具有丰富的语义。但我们又不能直接使用那些术语,因为它们有歧义和矛盾。模型可能缺乏开发人员在代码中所创建的更为微妙和灵活的特性,这要么是因为开发人员认为模型不必具备这些特性,要么是因为编码风格是过程式的,只能隐含地表达领域概念。

25
尽管模型和基于模型的语言之间的次序像是循环论证,但是,能够产生更有用模型的知识消化过程依赖于团队投身于基于模型的语言。持续使用Ubiquitous Language可以暴露模型中存在的缺点,这样团队就可以尝试并替换不恰当的术语或组合。当在语言中发现缺失时,新的词语将被引入到讨论中。这些语言上的更改也会在领域模型中引起相应的更改,并促使团队更新类图并重命名代码中的类和方法,当术语的意义改变时,甚至会导致行为也发生改变。

通过在实现的过程中使用这种语言,开发人员能够指出不准确和矛盾之处,并和领域专家一起找到有效的替代方案。

当然,为了解释和给出更广泛的上下文,领域专家的语言会超出Ubiquitous Language的范围。但在模型应对的范围内,他们应该使用Ubiquitous Language,并在发现不合适、不完整或错误之处后要引起注意。通过大量使用基于模型的语言,并且不达流畅绝不罢休,我们可以逐步得到一个完整的、易于理解的模型,它由简单元素组成,并通过组合这些简单元素表达复杂的概念。

因此:

将模型作为语言的支柱。确保团队在内部的所有交流中以及代码中坚持使用这种语言。在画图、写东西,特别是讲话时也要使用这种语言。

通过尝试不同的表示方法(它们反映了备选模型)来消除难点。然后重构代码,重新命名类、方法和模块,以便与新模型保持一致。解决交谈中的术语混淆问题,就像我们对普通词汇形成一致的理解一样。

26
要认识到,Ubiquitous Language的更改就是对模型的更改。

领域专家应该抵制不合适或无法充分表达领域理解的术语或结构,开发人员应该密切关注那些将会妨碍设计的有歧义和不一致的地方。

有了Ubiquitous Language,模型就不仅仅是一个设计工件了。它成为开发人员和领域专家共同完成的每项工作中不可或缺的部分。语言以动态形式传递知识。使用这种语言进行讨论能够呈现图和代码背后的真实含义。

未标题-1

我们在这里讨论的Ubiquitous Language假设只有一个模型在起作用。第14章将讨论不同模型(和语言)的共存,以及如何防止模型分裂。

Ubiquitous Language是那些以非代码形式呈现的设计的主要载体,这些包括把整个系统组织在一起的大尺度结构(参见第16章)、定义了不同系统和模型之间关系的限界上下文(参见第14章),以及在模型和设计中使用的其他模式。

示例 制定货运路线

下面这两段对话有着微妙但重要的差别。在每个对话场景中,注意观察讲话者有多少内容是谈论软件的业务功能,有多少内容是从技术上谈论软件的工作机理的。用户和开发人员用的是同一种语言吗?它的表达是否丰富,足以应对应用程序功能的讨论?

■场景1:最小化的领域抽象■


ed8875b2bc60f2d842fb5d382dd79908a3bf7c47

用户:那么,当更改清关(customs clearance)地点时①,需要重新制定整个路线计划啰。

开发人员:是的。我们将从货运表(shipment table)中删除所有与该货物id相关联的行,然后将出发地、目的地和新的清关地点传递给Routing Service,它会重新填充货运表。Cargo中必须设立一个布尔值,用于指示货运表中是否有数据。

用户:删除行?好,就按你说的做。但是,如果先前根本没有指定清关地点,也需要这么做吗?

开发人员:是的,无论何时更改了出发地、目的地或清关地点(或是第一次输入),都将检查是否已经有货运数据,如果有,则删除它们,然后由Routing Service重新生成数据。

用户:当然,如果原有的清关数据碰巧是正确的,我们就不需要这样做了。

开发人员:哦,没问题。但让Routing Service每次重新加载或卸载数据会更容易些。

用户:是的,但为新航线制定所有支持计划的工作量很大,因此,除非非改不可,我们一般不想更改航线。
28
开发人员:哦,好的,当第一次输入清关地点时,我们需要查询表格,找到以前的清关地点,然后与新的清关地点进行比较,从而判断是否需要重做。

用户:这个处理不必考虑出发地和目的地,因为航线在此总要变更。

开发人员:好的,我明白了。
■场景2:用领域模型进行讨论 ■


1fc9527530a1c6af7e3ec2b89e67c3245e0ebd3f

用户:那么,当更改清关地点时,需要重新制定整个路线计划啰。

开发人员:是的。当更改Route Specification(路线说明)的任意属性时,都将删除原有的Itinerary(航线),并要求Routing Service(路线服务)基于新的Route Specification生成一个新的Itinerary。

用户:如果先前根本没有指定清关地点,也需要这么做吗?

开发人员:是的,无论何时更改了Route Spec的任何属性,都将重新生成Itinerary。这也包括第一次输入某些属性。
29
用户:当然,如果原有的清关数据碰巧是正确的,我们就不需要这样做了。

开发人员:哦,没问题。但让Routing Service每次重新生成一个Itinerary会更容易些。

用户:是的,但为新航线制定所有支持计划的工作量很大,因此,除非非改不可,我们一般不想更改路线。

开发人员:哦。那么需要在Route Specification添加一些功能。这样,当更改Route Specification中的属性时,查看Itinerary是否仍满足Specification。如果不满足,则需要由Routing Service重新生成Itinerary。

用户:这一点不必考虑出发地和目的地,因为Itinerary在此总是要变更的。

开发人员:好的,但每次只做比较就简单多了。只有当不满足Route Specification时,才重新生成Itinerary。

第二段对话表达了领域专家的更多意图。在这两段对话中,用户都使用了“itinerary”这个词,但在第二段中它是一个对象,这使得双方可以更准确、具体地进行讨论。他们明确讨论了“route specification”,而不是每次都通过属性和过程来描述它。

这两段对话有意使用了相似的结构。实际上,第一段对话显得更啰嗦,对话双方需要不断对应用程序的特性和表达不清的地方进行解释。第二段对话使用了基于领域模型的术语,因此讨论更简洁。

网友评论

登录后评论
0/500
评论