6

DDD的哲学:核心域、统一语言

 1 year ago
source link: https://www.51cto.com/article/743794.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

DDD的哲学:核心域、统一语言

作者:Thoughtworks洞见 2023-01-03 10:06:39
我们用若干章节探讨了领域驱动设计的哲学内涵。两者之间的这种契合关系并非偶然。

作者 | 钟敬

阅读本系列文章:《​​DDD的哲学:模型的关联、演进和认知​​》

71b8ed0770201b81bbb8407845709eaf14e910.jpg

核心域与主要矛盾

大约公元前800年至前200年间,中国、希腊、印度和以色列的文明几乎在同一时期兴起,这被称为人类文明的轴心时代。不同文明展现出了不同的风貌。中国古代文化强调辩证逻辑,重视变化、联系和综合的思维方式,同时又有“子不语怪力乱神,六合之外存而不论”的唯物主义倾向。古希腊则重视严格的推理和分析,孕育了形式逻辑和公理化的数学体系。印度在婆罗门和佛教哲学中,从另一个方向将辩证法、逻辑和语言学推向极致。以色列的先知则创立了一神论的犹太教和政教合一的社会体制。

直到近代,西方才产生了系统的辩证法体系,集大成者是黑格尔。马克思和恩格斯吸收了黑格尔的辩证法,扬弃了其中的唯心主义成分,形成了辩证唯物主义,成为了风起云涌的无产阶级革命的理论基础。后来唯物辩证法传入中国,与中国传统文化中的辩证法和唯物主义思想一拍即合。为了将马克思主义中国化,毛老师写出了《实践论》和《矛盾论》。前文提到了《矛盾论》第一部分“两种宇宙观”,这一节看一下第四部分“主要的矛盾和主要的矛盾方面”。

事物中包含着多种矛盾,其发展变化成为事物发展的内在动力。在多种矛盾中,一般会有一种占主导地位,称为“主要矛盾”。要有效地处理问题,就要抓住主要矛盾。

例如某筹备开业的保险公司要开发一个保险核心系统。在开始时,“快速开展新单业务的需要与落后的新单处理能力”的矛盾就是主要矛盾,而“快速理赔的要求与落后的理赔能力”之间的矛盾则是次要矛盾。这是因为,新开业的保险公司首先面临的是新单业务的压力而不是理赔的压力。

在领域驱动设计中,领域模型中包含主要矛盾的部分,称为“核心域”(Core Domain)。在复杂的领域模型中,将核心域识别并有效处理的过程称为“精炼”(Distillation, 《领域驱动设计》第15章)。领域驱动设计中还提供了“领域愿景说明”(Domain Vision Statement),“强调核心” (Highlighted Core), “分离核心”(Segregated Core)等模式用于精炼过程的具体实践。

随着内因和外因的变化,主要矛盾和次要矛盾会发生转化。例如,当处理新单业务的系统成熟后,就成为了次要矛盾,而理赔则成为主要矛盾。当理赔也成熟后,灵活的产品和算法的定义可能变为主要矛盾。

总之,以矛盾的观点看问题,就可以知道识别和处理“核心域”的必要性,同时以发展的眼光看待核心域的转化。

这里还有一个微妙的问题:目前业界不同人所说的“核心域”的含义是有区别的。假如我们把业务需求作为“问题空间”,将软件系统的分析和设计作为“解空间”,那么《领域驱动设计》一书中所说的核心域实际上处于解空间,而另一本《实现领域驱动设计》中的核心域说的是问题空间。

《领域驱动设计》中,核心域在“精炼”一章中。其逻辑在于,先有领域模型,然后在领域模型发展到一定的复杂程度时,就需要从中“精炼”出核心域了。由于领域模型处于解空间,因此《领域驱动设计》中的“核心域”谈的是解空间的问题。

而《实现领域驱动设计》中的核心域则来自于对业务问题的分解,因此属于问题空间,先于领域模型的建立。

存在两个层面的“核心域”的现象具有一定的合理性。因为矛盾是普遍存在的,既存在于问题空间,也存在于解空间,两者都有“主要矛盾”,所以可以说两个层面都存在核心域。

目前多数人理解的核心域是《实现领域驱动设计》中的说法,而《领域驱动设计》原书中的模式和实践却没有得到重视。领域驱动设计本来要求“统一语言”,但“核心域”这一概念自身已经不统一了。这是业界需要解决的一个问题。

下一节就来看看与统一语言相关的问题吧。

统一语言与哲学的“语言转向”

前面介绍了哲学从本体论向方法论的转变。在方法论方面,洛克、休谟、帕斯卡等等大家宛如哲学天空中的星斗,而黑格尔和康德是其中最璀璨的两颗。

然而新的问题来了。这些哲学家的著作中往往充斥着晦涩的词汇,其中很多是自造的,在此基础上又进行了复杂的推演,然后产生了更加晦涩的语言。这些莫衷一是的概念又被后人按照自己的意思做了不同的解释。后果是,不仅普通人难以窥其门径,即便专业的哲学工作者,也会争论不休。尽管由此产生了大量论文和“学术成果”,但人们也逐渐意识到,很多争论仅仅是由于对同一名词的理解不同,而没有解决任何实际问题。这样的哲学除了在象牙塔中孤芳自赏,又有多少实际意义?

到了十九世纪末,人们逐渐意识到问题很可能出在“语言”上,很多哲学问题来源于对语言的误用。解决了语言问题,就解决了哲学问题。由此产生了“语言哲学”,并成为了二十世纪上半叶英美哲学的主流。如果说认识论是“对思考的思考”,那么语言哲学就是“对言说的言说”。尽管历史上有很多哲学家也很重视语言,但那时语言只是研究哲学的工具;而现在,语言成了哲学本身。这种哲学关注点的转移,称为“哲学的语言转向”(the linguistic turn in philosophy)。

信息技术和语言有着不解之缘。当您初次学习编程,知道了一条条指挥计算机工作的指令被称为计算机“语言”时,是否曾像我当初那样,有过一丝的惊讶?

在领域驱动设计中和语言直接相关的模式是“统一语言”(Ubiquitous Language)。该模式出现在《领域驱动设计》的第二章,属于该书第一部分“运用领域模型”,与“消化知识”和“模型驱动设计”共同作为全书的总纲。

“统一语言”要求业务和开发人员要用一致的语言来沟通。例如,同一个概念应该用同一个词汇,同一词汇也仅表达唯一的概念。这一道理是如此“直白”,为什么领域驱动设计还要专门强调呢?因为这一实践看似不难理解,但要在实践中真正做好则不太容易。下面我们从语言哲学的角度看一看软件开发中有关语言的问题。

首先,语言哲学认为,语言的意义是在使用的过程中体现出来的。

前面提到的早期哲学家,他们的语言是为了表达自己的观念自造的,并没有真正被用来解决实际问题,因而没有“恰当”地使用语言,从而造成了新的哲学问题。

回到软件开发,如果对软件的理解只是写在文档中,而这些文档很少有机会被阅读,没有真正起到交流的作用,那么也可以看作对语言的误用。这也正是敏捷软件开发所反对的。而统一语言强调不同干系人,尤其是业务人员和开发人员间的沟通。只有在协作中,统一语言才有意义。

近年流行的“事件风暴”方法,也可看作是统一语言的应用。事件风暴和经典的用例分析方法所解决的问题其实是类似的,都是为了捕获行为需求,为进一步的领域建模奠定基础。事件风暴中的“风暴”来源于头脑风暴,是常用的沟通协作方法。因此,沟通协作是事件风暴的内在组成部分,这一点是事件风暴优于传统的用例分析的地方。

其次,语言哲学强调语言的表达能力是有的限度的。

如维特根斯坦所说,“凡可说的,皆可说清;凡不可说的,应保持缄默”。前面提到的早期哲学家的另一个问题,就是强行用语言表达无法表达的东西。

领域驱动设计认为,软件开发的过程可以看作知识的学习、构建和运用的过程。而有些知识是语言难以表达的。不过,难以表达不代表不可知。比如说,我们难以向没有吃过梨子的人描述梨子的味道,但这并不代表梨子的味道是不可知的,只要尝一下就可以了。软件开发中的不可言说知识同样不是玄学,而完全可以通过持续的交流和实践来掌握。因此,不要期望通过文档可以表达所有知识,而要回归到干系人之间的沟通和协作。

再次,语言哲学认为,语言只有在特定的语境(Context)中才有意义。

前文已经提到,领域驱动设计中利用限界上下文(Bounded Context)来保证概念的一致性。而统一语言是针对限界上下文而言的,只有在其所处的上下文中才有意义。英文Context在中文中有两种译法:“语境”和“上下文”。目前《领域驱动设计》一书中采用了“上下文”的译法。其实译作“语境”可能更恰当,在“语境”中运用“语言”,意思会更协调一些。

最后,语言哲学又可分成两大流派:较早的“理想语言学派”和晚一些的“日常语言学派”。

理想语言学派认为自然语言是模糊的,哲学的出路在于利用数理逻辑创造一种形式化的语言。这种语言不仅表意明确,而且可以跨越各种自然语言。这在后来导致了计算机科学的基础理论“形式语言与自动机”的产生。

这一过程可以追溯到十七至十八世纪,莱布尼茨发明的二进制以及提出的“通用文字”(characteristica universalis)的构想。到了十九世纪,布尔利用二进制使逻辑成为了数学演算。布尔代数至今仍然是计算机科学的基础。稍后,现代逻辑学之父弗雷格提出了一阶谓词演算。这一理论也导致了像Prolog这样的逻辑编程语言的诞生。二十世纪,罗素、哥德尔等人进一步将数理逻辑发扬光大。后来,图灵提出的“图灵机”,丘奇提出的lambda演算,成为了算法和编程语言的数学基础。我们程序员所使用的各种编程语言,也可以算作形式语言。

较晚出现的一派则认为自然语言本身就是精确的,之所以会让人觉得模糊,是因为没有进入特定的语境。因此,应通过对自然语言本身的研究处理哲学问题,而无需借助形式语言。这就是“日常语言学派”。

对于软件开发者来说,没有必要陷入哲学家的争论。我们反而可以看到两者各自的合理性,以及互相衔接的可能。

一方面,计算机中运行的是二进制的字节流,而这又是由编程语言编译得来的。机器语言和编程语言都是形式语言。另一方面,计算机所要解决的业务问题,却开始于日常语言或者说自然语言的描述。因此我们必须有一种自然语言和形式语言的转换机制。领域驱动设计中的领域建模正是这一机制的重要环节。领域模型在自然语言和编程语言之间建立起了桥梁,帮助跨越两者之间的巨大鸿沟,成为开发复杂软件的重要手段。

领域驱动设计中,常用UML(统一建模语言)进行领域建模。严格地说,UML也是一种形式语言。不过比起编程语言,UML更贴近业务概念,更容易与自然语言转换。同时,用UML建立的领域模型,又可以方便地映射到编程语言和数据库设计。UML常用于软件系统的分析和设计,而编程语言用于编码。在分析、设计、编码过程中保持语言的统一,是统一语言的另一层意思。

我们用若干章节探讨了领域驱动设计的哲学内涵。两者之间的这种契合关系并非偶然。

哲学的主要目的是解决对世界进行认识(认识论)和诠释(语言哲学)问题。前面说的本体论,从现在的观点来看也可以理解为一种对世界的诠释。

计算机系统本质上是用软硬件对现实中的事物以及逻辑进行模拟,进而解决现实中的问题。计算机软件可以看作现实世界的模型。在计算机中最终运行的模型是二进制流,这在形式上与现实世界有巨大的差距,这是软件开发的本质复杂性之一。

为了跨越现实世界和二进制模型之间的鸿沟,软件开发方法学采取了渐进的方式。首先将现实世界映射为分析模型,然后由分析模型映射为设计模型,再映射为编程语言模型,最后编译成二进制模型。通过这种逐层递进的方式,来化解软件开发的困难。

领域驱动设计中所说的领域模型,主要指上面说的分析模型,目的是为了反应业务概念,也兼顾了一定的开发视角。建立领域模型的过程,就是认识现实世界中的概念及其发展变化逻辑的过程,而这正是认识论和语言哲学的应用场景。这就是哲学对领域建模以及整个软件开发过程具有指导作用的原因。

对领域建模的强调,并非始自领域驱动设计,而是从软件开发方法学出现的时候就已经开始了。早期的结构化方法学,采用数据流图和ER图等方式进行建模。后来发展为面向对象方法学,采用对象建模的方式。领域驱动设计则是对面向对象方法学的归纳和发展,为面向对象方法学建立了一套模式语言,使之更容易学习和运用。

领域驱动设计主要面向的还是传统意义上的软件开发,尤其是企业应用的开发。经过几十年的发展,至少在理论上已经研究得比较透彻了,难点在于工程上的推广和应用。而近年来,人工智能、大数据以及下一代互联网的发展,带来了一系列新的问题:隐私如何保护?谁拥有个人行为数据的所有权?人工智能是否会剥夺人的自由意志?怎样判定人工智能作恶的法律责任?机器人可以用于战场吗?个人怎样面对高科技所带来的焦虑?等等。这些则是伦理学、心灵哲学所要讨论的问题。这些话题超出了领域驱动设计的范围,我们的讨论,也可告一段落了。

责任编辑:赵宁宁 来源: Thoughtworks洞见

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK