领域模型(Domain Model)

news2025/4/7 21:18:37

前言

软件的核心是其为用户解决领域相关的问题的能力。所有其他特性,不管有多么重要,都要服务于这个基本目的。当领域很复杂时,这是一项艰巨的任务,要求高水平技术人员的共同努力。开发人员必须钻研领域以获取业务知识。他们必须磨砺其建模技巧,并精通领域设计。
然而,在大多数软件项目中,这些问题并未引起足够的重视。大部分有才能的开发人员对学习与他们的工作领域有关的知识不感兴趣,更不会下力气去扩展自己的领域建模技巧。技术人员喜欢那些能够提高其技能的可量化问题。领域工作很繁杂,而且要求掌握很多复杂的新知识,而这些新知识看似对提高计算机科学家的能力并无裨益。
相反,技术人才更愿意从事精细的框架工作,试图用技术来解决领域问题。他们把学习领域知识和领域建模的工作留给别人去做。软件核心的复杂性需要我们直接去面对和解决,如果不这样做,则可能导致工作重点的偏离。
在一个团队中,对领域深层次理解的模型开发有时也会在混乱中迷失方向,此时,理解领域核心的领导者能够将软件项目带回到正确的轨道上来。

领域模型定义

为了创建真正能为用户活动所用的软件,开发团队必须运用一整套与这些活动有关的知识体系。所需知识的广度可能令人望而生畏,庞大而复杂的信息也可能超乎想象。模型正是解决此类信息超载问题的工具。模型这种知识形式对知识进行了选择性的简化和有意的结构化。适当的模型可以使人理解信息的意义,并专注于问题。
领域模型并非某种特殊的图,而是这种图所要传达的思想。它绝不单单是领域专家头脑中的知识,而是对这类知识严格的组织且有选择的抽象。图可以表示和传达一种模型,同样,精心书写的代码或文字也能达到同样的目的。
领域建模并不是要尽可能建立一个符合"现实"的模型。即使是对具体、真实世界中的事物进行建模,所得到的模型也不过是对事物的一种模拟。领域建模也不单单是为了实现某种目的而构造出来的软件机制。领域建模更像是制作电影——出于某种目的而概括地反映现实。即使是一部纪录片也不会原封不动地展现真实生活。就如同电影制片人讲述故事或阐明观点时,他们会选择素材,并以一种特殊方式将它们呈现给观众,领域建模人员也会依据模型的作用来选择具体的模型。

领域模型的作用

在领域驱动的设计中,3个基本用途决定了模型的选择。
(1) 模型和设计的核心互相影响。正是模型与实现之间的紧密联系才使模型变得有用,并确保我们在模型中所进行的分析能够转化为最终产品(即一个可运行的程序)。模型与实现之间的这种紧密结合在维护和后续开发期间也会很有用,因为我们可以基于对模型的理解来解释代码。
(2) 模型是团队所有成员使用的通用语言的中枢。由于模型与实现之间的关联,开发人员可以使用该语言来讨论程序。他们可以在无需翻译的情况下与领域专家进行沟通。而且,由于该语言是基于模型的,因此我们可借助自然语言对模型本身进行精化。
(3) 模型是浓缩的知识。模型是团队一致认同的领域知识的组织方式和重要元素的区分方式。透过我们如何选择术语、分解概念以及将概念联系起来,模型记录了我们看待领域的方式。当开发人员和领域专家在将信息组织为模型时,这一共同的语言(模型)能够促使他们高效地协作。模型与实现之间的紧密结合使来自软件早期版本的经验可以作为反馈应用到建模过程中。

将领域知识转换成领域模型

将领域知识转化成领域模型是一个系统而复杂的过程,它涉及对领域知识的深入理解和结构化表示。

知识消化

高效的领域建模人员是知识的消化者。他们在大量信息中探寻有用的部分。他们不断尝试各种信息组织方式,努力寻找对大量信息有意义的简单视图。很多模型在尝试后被放弃或改造。只有找到一组适用于所有细节的抽象概念后,工作才算成功。这一精华严谨地表示了所发现的最为相关的知识。
知识消化并非一项孤立的活动,它一般是在开发人员的领导下,由开发人员与领域专家组成的团队来共同协作。他们共同收集信息,并通过消化而将它组织为有用的形式。信息的原始资料来自领域专家头脑中的知识、现有系统的用户,以及技术团队以前在相关遗留系统或同领域的其他项目中积累的经验。信息的形式也多种多样,有可能是为项目编写的文档,有可能是业务中使用的文件,也有可能来自大量的讨论。早期版本或原型将经验反馈给团队,然后团队对一些解释做出修改。
在传统的瀑布方法中,业务专家与分析员进行讨论,分析员消化理解这些知识后,对其进行抽象并将结果传递给程序员,再由程序员编写软件代码。由于这种方法完全没有反馈,因此总是失败。分析员全权负责创建模型,但他们创建的模型只是基于业务专家的意见。他们既没有向程序员学习的机会,也得不到早期软件版本的经验。知识只是朝一个方向流动,而且不会累积。
有些项目使用了迭代过程,但由于没有对知识进行抽象而无法建立起知识体系。开发人员听专家们描述某项所需的特性,然后开始构建它。他们将结果展示给专家,并询问接下来做什么。如果程序员愿意进行重构,则能够保持软件足够整洁,以便继续扩展它;但如果程序员对领域不感兴趣,则他们只会了解程序应该执行的功能,而不去了解它背后的原理。虽然这样也能开发出可用的软件,但项目永远也不会从原有特性中自然地扩展出强大的新特性。
好的程序员会自然而然地抽象并开发出一个可以完成更多工作的模型。但如果在建模时只是技术人员唱独角戏,而没有领域专家的协作,那么得到的概念将是很幼稚的。使用这些肤浅知识开发出来的软件只能做基本工作,而无法充分反映出领域专家的思考方式。
在团队所有成员一起消化理解模型的过程中,他们之间的交互也会发生变化。领域模型的不断精化迫使开发人员学习重要的业务原理,而不是机械地进行功能开发。领域专家被迫提炼自己已知道的重要知识的过程往往也是完善其自身理解的过程,而且他们会渐渐理解软件项目所必需的概念严谨性。
所有这些因素都促使团队成员成为更合格的知识消化者。他们对知识去粗取精。他们将模型重塑为更有用的形式。由于分析员和程序员将自己的知识输入到了模型中,因此模型的组织更严密,抽象也更为整洁,从而为实现提供了更大支持。同时,由于领域专家也将他们的知识输入到了模型中,因此模型反映了业务的深层次知识,而且真正的业务原则得以抽象。
模型在不断改进的同时,也成为组织项目信息流的工具。模型聚焦于需求分析。它与编程和设计紧密交互。它通过良性循环加深团队成员对领域的理解,使他们更透彻地理解模型,并对其进一步精化。模型永远都不会是完美的,因为它是一个不断演化完善的过程。模型对理解领域必须是切实可用的。它们必须非常精确,以便使应用程序易于实现和理解。

持续学习

当开始编写软件时,其实我们所知甚少。项目知识零散地分散在很多人和文档中,其中夹杂着其他一些无关信息,因此我们甚至不知道哪些知识是真正需要的知识。看起来没什么技术难度的领域很可能是一种错觉——我们并没意识到不知道的东西究竟有多少。这种无知往往会导致我们做出错误的假设。
同时,所有项目都会丢失知识。已经学到了一些知识的人可能干别的事去了。团队可能由于重组而被拆散,这导致知识又重新分散开。被外包出去的关键子系统可能只交回了代码,而不会将知识传递回来。而且当使用典型的设计方法时,代码和文档不会以一种有用的形式表示出这些来之不易的知识,因此一旦由于某种原因人们没有口头传递知识,那么知识就丢失了。
高效率的团队需要有意识地积累知识,并持续学习。对于开发人员来说,这意味着既要完善技术知识,也要培养一般的领域建模技巧。但这也包括认真学习他们正在从事的特定领域的知识。
需要说明的是,早期工作还是非常重要的。关键的模型元素被保留下来,而更重要的是,早期工作启动了知识消化的过程,这使得所有后续工作更加高效:团队成员、开发人员和领域专家等都学到了知识,他们开始使用一种公共的语言,而且形成了贯穿整个实现过程的反馈闭环。这样,一个发现之旅悄然开始了。

知识丰富的设计

业务活动和规则如同所涉及的实体一样,都是领域的核心,任何领域都有各种类别的概念。知识消化所产生的模型能够反映出对知识的深层理解。在模型发生改变的同时,开发人员对实现进行重构,以便反映出模型的变化,这样,新知识就被合并到应用程序中了。
当我们的建模不再局限于寻找实体和值对象时,我们才能充分吸取知识,因为业务规则之间可能会存在不一致。领域专家在反复研究所有规则、解决规则之间的矛盾以及以常识来弥补规则的不足等一系列工作中,往往不会意识到他们的思考过程有多么复杂。软件是无法完成这一工作的。正是通过与软件专家紧密协作来消化知识的过程才使得规则得以澄清和充实,并消除规则之间的矛盾以及删除一些无用规则。
将重要的业务规则隐藏在过程代码中,有存在以下的问题:
(1) 不可能有业务专家会通过阅读上述代码来检验规则,即使在开发人员的帮助下也无法完成。
(2) 非业务的技术人员很难将需求文本与代码联系起来。
如果规则更复杂,情况将更糟。可以考虑使用LAYERED ARCHITECTURE,它会帮助我们将业务规则转移到领域对象中。
领域模型和相应的设计可用来保护和共享知识。更明确的设计具有以下优点:
(1) 为了实现更明确的设计,程序员和其他各位相关人员都必须理解有业务含义的专有名词的本质,明白它是一个明确且重要的业务规则,而不只是一个不起眼的代码。
(2) 程序员可以向业务专家展示技术工件,甚至是代码,但应该是领域专家(在程序员指导下)可以理解的,以便形成反馈闭环。

深层模型

有用的模型很少停留在表面。随着对领域和应用程序需求的理解逐步加深,我们往往会丢弃那些最初看起来很重要的表面元素,或者切换它们的角度。这时,一些开始时不可能发现的巧妙抽象就会渐渐浮出水面,而它们恰恰切中问题的要害。
要想建立实用且清晰的模型则要求团队成员既精通领域知识,也要精通建模技术。
知识消化是一种探索,它永无止境。

领域模型通用语言(UBIQUITOUS LANGUAGE)

领域模型可成为软件项目通用语言的核心。该模型是一组得自于项目人员头脑中的概念,以及反映了领域深层含义的术语和关系。这些术语和相互关系提供了模型语言的语义,虽然语言是为领域量身定制的,但就技术开发而言,其依然足够精确。正是这条至关重要的纽带,将模型与开发活动结合在一起,并使模型与代码紧密绑定。
这种基于模型的交流并不局限于UML(统一建模语言)图。为了最有效地使用模型,需要充分利用各种交流手段。基于模型的交流提高了书面文档的效用,也提高了敏捷过程中再度强调的非正式图表和交谈的效用。它还通过代码本身及对应的测试促进了交流。
UBIQUITOUS LANGUAGE(通用语言)——围绕领域模型建立的一种语言,团队所有成员都使用这种语言把团队的所有活动与软件联系起来。要想创建一种灵活的、蕴含丰富知识的设计,需要一种通用的、共享的团队语言,以及对语言不断的试验——然而,软件项目上很少出现这样的试验。
虽然领域专家对软件开发的技术术语所知有限,但他们能熟练使用自己领域的术语——可能还具有各种不同的风格。另一方面,开发人员可能会用一些描述性的、功能性的术语来理解和讨论系统,而这些术语并不具备领域专家的语言所要传达的意思。或者,开发人员可能会创建一些用于支持设计的抽象,但领域专家无法理解这些抽象。负责处理问题不同部分的开发人员可能会开发出各自不同的设计概念以及描述领域的方式。
由于语言上存在鸿沟,领域专家们只能模糊地描述他们想要的东西。开发人员虽然努力去理解一个自己不熟悉的领域,但也只能形成模糊的认识。虽然少数团队成员会设法掌握这两种语言,但他们会变成信息流的瓶颈,并且他们的翻译也不准确。
在一个没有公共语言的项目上,开发人员不得不为领域专家做翻译。而领域专家需要充当开发人员与其他领域专家之间的翻译。甚至开发人员之间还需要互相翻译。这些翻译使模型概念变得混淆,而这会导致有害的代码重构。这种间接的沟通掩盖了分裂的形成——同一个团队的不同成员使用不同的术语而尚不自知。由于软件的各个部分不能够浑然一体,因此这就导致无法开发出可靠的软件。翻译工作导致各类促进深入理解模型的知识和想法无法结合到一起。
(1) 如果语言支离破碎,项目必将遭遇严重问题。领域专家使用他们自己的术语,而技术团队所使用的语言则经过调整,以便从设计角度讨论领域。
(2) 日常讨论所使用的术语与代码(软件项目的最重要产品)中使用的术语不一致。甚至同一个人在讲话和写东西时使用的语言也不一致,这导致的后果是,对领域的深刻表述常常稍纵即逝,根本无法记录到代码或文档中。
(3) 翻译使得沟通不畅,并削弱了知识消化。
(4) 然而任何一方的语言都不能成为公共语言,因为它们无法满足所有的需求。
所有翻译的开销,连带着误解的风险,成本实在太高了。项目需要一种公共语言,这种语言要比所有语言的最小公分母健壮得多。通过团队的一致努力,领域模型可以成为这种公共语言的核心,同时将团队沟通与软件实现紧密联系到一起。该语言将存在于团队工作中的方方面面。
UBIQUITOUS LANGUAGE(领域模型通用语言)的词汇包括类和主要操作的名称。语言中的术语,有些用来讨论模型中已经明确的规则,还有一些则来自施加于模型上的高级组织原则。最后,团队常常应用于领域模型的模式名称也使这种语言更为丰富。
模型之间的关系成为所有语言都具有的组合规则。词和短语的意义反映了模型的语义。
开发人员应该使用基于模型的语言来描述系统中的工件、任务和功能。这个模型应该为开发人员和领域专家提供一种用于相互交流的语言,而且领域专家还应该使用这种语言来讨论需求、开发计划和特性。领域模型通用语言使用得越普遍,理解进行得就越顺畅。
至少,我们应该将它作为目标。但最初,模型可能不太好,因此无法很好地履行这些职责。它可能不会像领域的专业术语那样具有丰富的语义。但我们又不能直接使用那些术语,因为它们有歧义和矛盾。模型可能缺乏开发人员在代码中所创建的更为微妙和灵活的特性,这要么是因为开发人员认为模型不必具备这些特性,要么是因为编码风格是过程式的,只能隐含地表达领域概念。
尽管模型和基于模型的语言之间的次序像是循环论证,但是,能够产生更有用模型的知识消化过程依赖于团队投身于基于模型的语言。持续使用UBIQUITOUS LANGUAGE可以暴露模型中存在的缺点,这样团队就可以尝试并替换不恰当的术语或组合。当在语言中发现缺失时,新的词语将被引入到讨论中。这些语言上的更改也会在领域模型中引起相应的更改,并促使团队更新类图并重命名代码中的类和方法,当术语的意义改变时,甚至会导致行为也发生改变。
通过在实现的过程中使用这种语言,开发人员能够指出不准确和矛盾之处,并和领域专家一起找到有效的替代方案。
当然,为了解释和给出更广泛的上下文,领域专家的语言会超出UBIQUITOUS LANGUAGE的范围。但在模型应对的范围内,他们应该使用UBIQUITOUS LANGUAGE,并在发现不合适、不完整或错误之处后要引起注意。通过大量使用基于模型的语言,并且不达流畅绝不罢休,我们可以逐步得到一个完整的、易于理解的模型,它由简单元素组成,并通过组合这些简单元素表达 复杂的概念。因此:
(1) 将模型作为语言的支柱。确保团队在内部的所有交流中以及代码中坚持使用这种语言。在画图、写东西,特别是讲话时也要使用这种语言。
(2) 通过尝试不同的表示方法(它们反映了备选模型)来消除难点。然后重构代码,重新命名类、 方法和模块,以便与新模型保持一致。解决交谈中的术语混淆问题,就像我们对普通词汇形成一致的理解一样。
(3) 要认识到,UBIQUITOUS LANGUAGE的更改就是对模型的更改。
(4) 领域专家应该抵制不合适或无法充分表达领域理解的术语或结构,开发人员应该密切关注那些将会妨碍设计的有歧义和不一致的地方。
有了UBIQUITOUS LANGUAGE,模型就不仅仅是一个设计工件了。它成为开发人员和领域专家共同完成的每项工作中不可或缺的部分。语言以动态形式传递知识。使用这种语言进行讨论能够呈现图和代码背后的真实含义。
我们在这里讨论的UBIQUITOUS LANGUAGE假设只有一个模型在起作用。后面还会讨论不同模型(和语言)的共存,以及如何防止模型分裂。
UBIQUITOUS LANGUAGE是那些以非代码形式呈现的设计的主要载体,这些包括把整个系统组织在一起的大尺度结构、定义了不同系统和模型之间关系的限界上下文,以及在模型和设计中使用的其他模式。

将领域模型语言用于业务交谈中

改善模型的最佳方式之一就是通过对话来研究,试着大声说出可能的模型变化中的各种结构。这样不完善的地方很容易被听出来。
使用单词和短语是极为重要的——其将我们的语言能力用于建模工作,这就如同素描对于表现视觉和空间推理十分重要一样。我们即要利用系统性分析和设计方面的分析能力,也要利用对代码的神秘"感觉"。这些思考方式互为补充,要充分利用它们来找到有用的模型和设计。在所有这些方式中,语言上的试验常常是最容易被忽视的。
事实上,我们的大脑似乎很擅长处理口语的复杂性。例如,当具有不同语言背景的人凑在一起做生意时,如果没有公共语言,他们就会创造一种称为"混杂语"(pidgin)的公共语言。混杂语虽然不像讲话者的母语那样详尽,但它适合当前任务。当人们交谈时,自然会发现词语解释和意义上的差别,而后会自然而然地解决这些差别。他们会发现这种语言中的简陋晦涩之处并把它们搞顺畅。
当我们在讨论中使用领域模型的UBIQUITOUS LANGUAGE时,特别是在开发人员和领域专家一起推敲场景和需求时,通用语言的使用会越来越流利,而且我们还可以互相指点一些细微的差别。我们自然而然地共享了我们所说的语言,而这种方式是图和文档无法做到的。
想要在软件项目上产生一种UBIQUITOUS LANGUAGE,说起来容易,做起来却难,我们必须充分利用自然赋予我们的才能来实现这个目标。正如人类的视觉和空间思维能力使我们能够快速传达和处理图形概述中的信息一样,我们也可以利用自己在基于语法的、有意义的语言方面的天赋来推动模型的开发。因此,下面这段话可作为UBIQUITOUS LANGUAGE模式的补充:
讨论系统时要结合模型。使用模型元素及其交互来大声描述场景,并且按照模型允许的方式将各种概念结合到一起。找到更简单的表达方式来讲出你要讲的话,然后将这些新的想法应用到图和代码中。

一个团队,一种语言

技术人员通常认为业务专家最好不要接触领域模型,他们可能认为"领域模型对业务专家来说太抽象了"或者"业务专家不熟悉面向对象设计",等等。当然,设计中有一些技术组件与领域专家无关,但模型的核心最好让他们参与。过于抽象?那你怎么知道抽象是否合理?你是否像他们一样深入理解领域?有时,某些特定需求是从底层用户那里收集的,他们在描述这些需求时可能会用到一小部分更具体的术语,但领域专家应该能够更深入地思考他们所从事的领域。如果连经验丰富的领域专家都不能理解模型,那么模型一定出了什么问题。
最初,当用户讨论系统尚未建模的未来功能时,他们没有模型可供使用。但当他们开始与开发人员一起仔细讨论这些新想法时,探索共享模型的过程就开始了。最初的模型可能很笨拙且不完整,但会逐渐精化。随着新语言的演进,领域专家必须付出更多努力来适应它,并更新那些仍然很重要的旧文档。
当领域专家使用这种语言互相讨论,或者与开发人员进行讨论时,很快就会发现模型中哪些地方不符合他们的需要,甚至是错误的。另一方面,模型语言的精确性也会促使领域专家(在开发人员的帮助下)发现他们想法中的矛盾和含糊之处。
开发人员和领域专家可以通过一步一步地使用模型对象来走查场景,从而对模型进行非正式的测试。每次讨论都是开发人员和专家一起使用模型的机会,在这个过程中,他们可以加深彼此的理解,并对概念进行精化。
领域专家可以使用模型语言来编写用例,甚至可以直接利用模型来具体说明验收测试。
有时,有人会反对使用模型语言来收集需求。毕竟,难道需求不应该独立于实现它们的设计吗?这种观点忽视了所有语言都要基于某种模型这一事实。词的意义是不明确。领域模型通常是从领域专家自己的术语中推导出来的,但已经经过了"清理",以便具有更明确、更严密的定义。当然,如果这些定义与领域公认的意义有较大差别,领域专家应该反对。在敏捷过程中,需求是随着项目的前进而演变的,因为几乎不存在现成的知识可以充分说明一个应用程序。用精化后的UBIQUITOUS LANGUAGE来重新组织需求应该是这种演变过程的一部分。
语言的多样性通常是必要的,但领域专家与开发人员之间不应该有语言上的分歧。
当然,开发人员的确会使用领域专家无法理解的技术术语。开发人员有其所需的大量术语来讨论系统技术。几乎可以肯定的是,用户也会用开发人员无法理解的、超出应用程序范畴的专用术语。这些都是对语言的扩展。但在这些语言扩展中,同一领域的相同词汇不应该反映不同的模型。

请添加图片描述
有了UBIQUITOUS LANGUAGE之后,开发人员之间的对话、领域专家之间的讨论以及代码本身所表达的内容都基于同一种语言,都来自于一个共享的领域模型。

文档和图

每当我参加讨论软件设计的会议时,如果不在白板或画板上画图,我就很难讨论下去。我画的大部分是UML图,主要以类图和对象交互图为主。
有些人天生是视觉动物,图可以帮助人们掌握某些类型的信息。UML图在传达对象之间的关系上真是游刃有余,而且也很擅长表现交互。但它们却无法给出这些对象的概念定义。在会议中,我会一边画图一边用语言来丰富它们的意义,或者在与其他参与者讨论时进行解释。
简单、非正式的UML图能够维系整个讨论。绘制一幅包含当前问题最关键的3~5个对象的图,这样每个人都可以集中注意力。所有人就对象关系会达成一致的认识,更重要的是,他们将使用相同的对象名称。如此,口头讨论会更加高效。当人们尝试不同的想法时,图也随之改变,草图在某种程度上可以反映讨论的变化,这是讨论中真正重要的部分。毕竟,UML就是统一建模语言
当人们必须通过UML图表示整个模型或设计时,麻烦也随之而来。很多对象模型图在某些方面过于细致,同时在某些方面又有很多遗漏。说它们过于细致是因为人们认为必须将所有要编码的对象都放到建模工具中。而细节过多的结果是"只见树木,不见森林"。
尽管存在所有这些细节,但属性和关系只是对象模型的一部分。这些对象的行为以及这些对象上的约束就不那么容易表示了。对象交互图可以阐明设计中的一些复杂之处,但却无法用这种方式来展示大量的交互,就是工作量太大了,既要制作图,还要学习这些图。而且交互图也只能暗示出模型的目的。要想把约束和断言包括进来,需要在UML图中使用文本,这些文本用括号括起来,插入到图中。
操作名称可能会暗示出对象的行为职责,对象交互图(或序列图)中也会隐含地展示出这些职责,但无法直接表述。因此,这项任务就要靠补充文本或对话来完成。换言之,UML图无法传达模型的两个最重要的方面,一个方面是模型所表示的概念的意义,另一方面是对象应该做哪些事情。但是,这并不是大问题,因为通过仔细地使用语言(英语、西班牙语或其他任何一种语言)就可以很好地完成这项任务。
UML也不是一种十分令人满意的编程语言。我从未见过有人使用建模工具的代码生成功能达到了预期目的。如果UML的能力无法满足需要,通常人们就不得不忽略模型最关键的部分,因为有些规则并不适合用线框图来表示。当然,代码生成器也无法使用上面所说的那些文本注释。如果确实能使用UML这样的绘图语言来编写可执行程序,那么UML图就会退化为程序本身的另一种视图,这样,"模型"的真正含义就丢失了。如果使用UML作为实现语言,则仍然需要利用其他手段来表达模型的确切含义。
图是一种沟通和解释手段,它们可以促进头脑风暴。简洁的小图能够很好地实现这些目标,而涵盖整个对象模型的综合性大图反而失去了沟通或解释能力,因为它们将读者淹没在大量细节之中,加之这些图也缺乏目的性。鉴于此,我们应避免使用包罗万象的对象模型图,甚至不能使用包含所有细节的UML数据存储库。相反,应使用简化的图,图中只包含对象模型的重要概念——这些部分对于理解设计至关重要。本书中的图都是我在项目中使用过比较典型的图。它们很简单,而且具有很强的解释能力,在澄清一些要点时,还使用了一些非标准的符号。它们显示了设计约束,但它们不是面面俱到的设计规范。它们只体现了思想纲要。
设计的重要细节应该在代码中体现出来。良好的实现应该是透明的,清楚地展示其背后的模型(下一章及本书其他许多章节的主题就是阐述如何做到这一点)。互为补充的图和文档能够引导人们将注意力放在核心要点上。自然语言的讨论可以填补含义上的细微差别。这就是为什么我喜欢把典型的UML使用方法颠倒过来的原因。通常的用法是以图为主,辅以文本注释;而我更愿意以文本为主,用精心挑选的简化图作为说明。
务必要记住模型不是图。图的目的是帮助表达和解释模型。代码可以充当设计细节的存储库。书写良好的代码与UML具有同样的表达能力。经过仔细选择和构造的图可以帮助人们集中注意力,并起到指导作用,当然前提条件是不能强制用图来表示全部模型或设计,因为这样会削弱图的清晰表达的能力。

书面设计文档

口头交流可以解释代码的含义,因此可作为代码精确性和细节的补充。虽然交谈对于将人们与 模型联系起来是至关重要的,但书面文档也是必不可少的,任何规模的团队都需要它来提供稳定和 共享的交流。但要想编写出能够帮助团队开发出好软件的书面文档却是一个不小的挑战。
一旦文档的形式变得一成不变,往往会从项目进展流程中脱离出来。它会跟不上代码或项目语言的演变。
书面文档有很多编写方法。本书第四部分将介绍几种满足特定需要的具体文档,但不会列出 项目需要使用的所有文档,而是给出两条用于评估文档的总体原则。
文档应作为代码和口头交流的补充
每种敏捷过程在编写文档方面都有自己的理念。极限编程主张完全不使用(多余的)设计文档,而让代码解释自己。实际运行的代码不会说谎,而其他文档则不然。运行代码所产生的行为是明确的。
极限编程只关注对程序及可执行测试起作用的因素。由于为代码添加的注释并不影响程序的行为,因此它们往往无法与当前代码及其模型保持同步。外部文档和图也不会影响程序的行为,因此它们也无法保持同步。另一方面,口头交流和临时在白板上画的图不会长久保留而产生混淆。依赖代码作为交流媒介可以促使开发人员保持代码的整洁和透明。
然而,将代码作为设计文档也有局限性。它可能会把读代码的人淹没在细节中。尽管代码的行为是非常明确的,但这并不意味着其行为是显而易见的。而且行为背后的意义可能难以表达。换言之,只用代码做文档与使用大而全的UML图面临着差不多相同的基本问题。当然,团队进行大量的口头交流能够为代码提供上下文和指导,但是,口头交流很短暂,而且范围很小。此外,开发人员并不是唯一需要理解模型的人。
文档不应再重复表示代码已经明确表达出的内容。代码已经含有各个细节,它本身就是一种精确的程序行为说明。
其他文档应该着重说明含义,以便使人们能够深入理解大尺度结构,并将注意力集中在核心元素上。当编程语言无法直接明了地实现概念时,文档可以澄清设计意图。我们应该把书面文档作为代码和口头讨论的补充。
文档应当鲜活并保持最新
我在为模型编写书面文档时,会仔细选择一个小的模型子集来画图,然后让文字放置在这些图周围。我用文字定义类及其职责,并且像自然语言那样把它们限定在一个语义上下文中。而图显示了在将概念形式化和简化为对象模型的过程中所做的一些选择。这些图可以随意一些,甚至是手绘的。手绘图除了节省工作量,也让人们一看就知道它们是不正式、临时的。这些优点都非常有利于交流,因为它们适用于我们的模型思想。
设计文档的最大价值在于解释模型的概念,帮助在代码的细节中指引方向,或许还可以帮助人们深入了解模型预期的使用风格。根据不同的团队理念,整个设计文档可能会十分简单,如只是贴在墙上的一组草图,也可能会非常详尽。
文档必须深入到各种项目活动中去。判断是否做到这一点的最简单方法,是观察文档与UBIQUITOUS LANGUAGE之间的交互。文档是用人们(当前)在项目上讲的语言编写的吗?它是用嵌入到代码中的语言编写的吗?
注意听UBIQUITOUS LANGUAGE,观察它是如何变化的。如果设计文档中使用的术语不再出现在讨论和代码中,那么文档就没有起到它的作用。或许是文档太大或太复杂了,或许是它没有关注足够重要的主题。人们要么不阅读文档,要么觉得它索然无味。如果文档对UBIQUITOUS LANGUAGE没有影响,那么一定是出问题了。
相反,我们会注意到UBIQUITOUS LANGUAGE随着文档渐渐过时而自然地改变。显然,要么人们不再关心文档,要么认为它不重要而不再去更新它。这时可以将它作为历史文件安全地归档,如果继续使用这样的文档可能会产生混淆并损害项目。如果文档不再担负重要的作用,那么纯粹靠意志和纪律保持其更新就是浪费精力。
UBIQUITOUS LANGUAGE可以使其他文档(如需求规格说明)更简洁和明确。当领域模型反映了与业务最相关的知识时,应用程序的需求成为该模型内部的场景,而UBIQUITOUS LANGUAGE可直接用MODEL-DRIVEN DESIGN(模型驱动设计)的方式描述此类场景。结果就是规格说明书的编写更简单,因为它们不必传达模型背后隐含的业务知识。通过将文档减至最少,并且主要用它来补充代码和口头交流,就可以避免文档与项目脱节。
根据UBIQUITOUS LANGUAGE及其演变来选择那些需要保持更新并与项目活动紧密交互的文档。

完全依赖可执行代码的情况

良好的代码具有很强的表达能力,但它所传递的信息不能确保是准确的。一段代码所产生的实际行为是不会改变的。但是,方法名称可能会有歧义、会产生误导或者因为已经过时而无法表示方法的本质含义。测试中的断言是严格的,但变量和代码组织方式所表达出来的意思未必严格。好的编程风格会尽力使这种联系直接化,但其仍然主要靠开发人员的自律。编码时需要一丝不苟的态度,只有这样才能编写出"言行全部正确"的代码。
消除这些差异是诸如声明式设计这样的方法的最大优点,在这类方法中,程序元素用途的陈述决定了它在程序中的实际行为。从UML生成程序的部分动机就来源于此,虽然目前看来这通常不会得到好的结果。
尽管代码可能会产生误导,但它仍然比其他文档更基础。要想利用当前的标准技术使代码所传达的消息与它的行为和意图保持一致,需要纪律和思考设计的特定方式。要有效地交流,代码必须基于在编写需求时所使用的同一种语言,也就是开发人员之间、开发人员与领域专家之间进行讨论时所使用的语言。

解释性模型

模型在帮助领域学习方面也具有很大价值。对设计起到推动作用的模型是领域的一个视图,但为了学习领域,还可以引入其他视图,这些视图只用作传递一般领域知识的教学工具。出于此目的,人们可以使用与软件设计无关的其他种类模型的图片或文字。
使用其他模型的一个特殊原因是范围。驱动软件开发过程的技术模型必须经过严格的精简,以便用最小化的模型来实现其功能。而解释性模型则可以包含那些提供上下文的领域方面——这些上下文用于澄清范围更窄的模型。
解释性模型提供了一定的自由度,可以专门为某个特殊主题定制一些表达力更强的风格。领域专家在一个领域中所使用的视觉隐喻通常呈现了更清晰的解释,这可以教给开发人员领域知识,同时使领域专家们的意见更一致。解释性模型还可以以一种不同的方式来呈现领域,并且各种不同角度的解释有助于人们更好地学习。
解释性模型不必是对象模型,而且最好不是。实际上在这些模型中不使用UML是有好处的,这样可以避免人们错误地认为这些模型与软件设计是一致的。尽管解释性模型与驱动设计的模型往往有对应关系,但它们并不完全类似。为了避免混淆,每个人都必须知道它们之间的区别。

绑定领域模型和实现

当我走进办公室,首先映入眼帘的是打印在数张大纸上的完整类图,它铺满了一整面墙。这是我进入某个项目的第一天,在此之前,聪明的项目组成员花费了几个月的时间进行仔细的研究并且开发出了上面这幅详尽的领域模型。该模型中的对象一般都与3~4个其他对象有着复杂的关联,而这张关联网几乎没有边界。在这方面,分析人员忠实地反映了领域自身的性质。
尽管这张墙面大小的图让人吃不消,但是它所表现的模型确实捕获了一些知识。经过一段时间的研究,我确实从中学到了不少知识(但是这种学习很难找到头绪,更像是在随意地浏览网页)。然而对类图的研究并不能让我深入地了解该应用程序的代码和设计,这让我备感困扰。
当开发人员开始实现应用程序时,他们很快就发现,尽管分析人员说得头头是道,他们依然无法将这种错综复杂的关系转换成可存储、可检索的且具有事务完整性的单元。请注意,该项目使用的是对象数据库,也就是说开发人员根本不用考虑对象—关系表映射这种难题。从根本上说,该模型无法为应用程序的实现提供帮助。
由于模型是"正确的",这是经过技术分析人员和业务专家大量协作才得到的结果,因此开发人员得出这样的结论:无法把基于概念的对象作为设计的基础。于是他们开始进行专门针对程序开发的设计。他们的设计确实用了一些原有模型中类和属性的名称进行数据存储,但这种设计并不是建立在任何已有模型的基础上的。
这个项目虽然建立了领域模型,但是如果模型不能直接帮助开发可运行的软件,那么这种纸上谈兵的模型又有什么意义呢?
几年后,我在一个完全不同的项目中又看到了完全相同的结果。该项目要用Java实现新设计,并用新设计替换现存的C++应用程序。老版本的程序根本没有进行对象建模,仅仅是把功能堆积在一起。老版本的设计(如果有的话)就是在已有代码的基础上一个一个地堆积新功能,完全没有任何泛化或抽象的迹象。
奇怪的是,这两种开发流程所完成的最终产品却非常相似!它们都充斥了大量功能,难于理解,难以维护。尽管有些程序实现是比较直观的,但是仅通过阅读代码依然无法深入了解该系统的目的所在。除了精心设计的数据结构之外,这两种开发流程都没有利用其开发环境中的面向对象的设计范式。
模型种类繁多,作用各有不同,即使是那些仅用于软件开发项目的模型也是如此。领域驱动设计要求模型不仅能够指导早期的分析工作,还应该成为设计的基础。这种设计方法对于代码的编写有着重要的意义。不太明显的一点就是:领域驱动设计要求一种不同的建模方法。

MODEL-DRIVEN DESIGN

MODEL-DRIVEN DESIGN(模型驱动的设计)——软件元素的某个子集严格对应于模型的元素。也代表一种合作开发模型和实现以便互相保持一致的过程。
严格按照基础模型来编写代码,能够使代码更好地表达设计含义,并且使模型与实际的系统相契合。那些压根儿就没有领域模型的项目,仅仅通过编写代码来实现一个又一个的功能,它们无法利用知识消化沟通所带来的好处。如果涉及复杂的领域就会使项目举步维艰。
另一方面,许多复杂项目确实在尝试使用某种形式的领域模型,但是并没有把代码的编写与模型紧密联系起来。这些项目所设计的模型,在项目初期还可能用来做一些探索工作,但是随着项目的进展,这些模型与项目渐行渐远,甚至还会起误导作用。所有在模型上花费的精力都无法保证程序设计的正确性,因为模型和设计是不同的。
模型和程序设计之间的联系可能在很多情况下被破坏,但是二者的这种分离往往是有意而为之的。很多设计方法都提倡使用完全脱离于程序设计的分析模型,并且通常这二者是由不同的人员开发的。之所以称其为分析模型,是因为它是对业务领域进行分析的结果,它在组织业务领域中的概念时,完全不去考虑自己在软件系统中将会起到的作用。分析模型仅仅是理解工具,人们认为把它与程序实现联系在一起无异于搅浑一池清水。随后的程序设计与分析模型之间可能仅仅保持一种松散的对应关系。在创建分析模型时并没有考虑程序设计的问题,因此分析模型很有可能无法满足程序设计的需求。
这种分析中会有一些知识消化的过程,但是在编码开始后,如果开发人员不得不重新对设计进行抽象,那么大部分的领域知识就会被丢弃。如此一来,就不能保证在新的程序设计中还能保留或者重现分析人员所获得的并且嵌入在模型中的领域知识。到了这一步,要维护程序设计和松散连接的模型之间的对应关系就很不合算了。
纯粹的分析模型甚至在实现理解领域这一主要目的方面也捉襟见肘,因为在程序设计和实现过程中总是会发现一些关键的知识点,而细节问题则会出人意料地层出不穷。前期模型可能会深入研究一些不相关的问题,反而忽略了一些重要的方面。而且它对于其他问题的描述也可能对应用程序没有任何帮助。最后的结果就是:编码工作一开始,纯粹的分析模型就被抛到一边,大部分的模型都需要重新设计。即便是重新设计,如果开发人员认为分析与程序开发毫不相关,那么建模过程就不会那么规范。而如果项目经理也这么认为,那么开发团队可能没有足够的机会与领域专家进行交流。
无论是什么原因,软件的设计如果缺乏概念,那么软件充其量不过是一种机械化的产品——只实现有用的功能却无法解释操作的原因。
如果整个程序设计或者其核心部分没有与领域模型相对应,那么这个模型就是没有价值的,软件的正确性也值得怀疑。同时,模型和设计功能之间过于复杂的对应关系也是难于理解的,在实际项目中,当设计改变时也无法维护这种关系。若分析与和设计之间产生严重分歧,那么在分析和设计活动中所获得的知识就无法彼此共享
分析工作一定要抓住领域内的基础概念,并且用易于理解和易于表达的方式描述出来。设计工作则需要指定一套可以由项目中使用的编程工具创建的组件,使项目可以在目标部署环境中高效运行,并且能够正确解决应用程序所遇到的问题。
MODEL-DRIVEN DESIGN(模型驱动设计)不再将分析模型和程序设计分离开,而是寻求一种能够满足这两方面需求的单一模型。不考虑纯粹的技术问题,程序设计中的每个对象都反映了模型中所描述的相应概念。这就要求我们以更高的标准来选择模型,因为它必须同时满足两种完全不同的目标。
有很多方法可以对领域进行抽象,也有很多种设计可以解决应用程序的问题。因此,绑定模型和程序设计是切实可行的。但是这种绑定不能够因为技术考虑而削弱分析的功能,我们也不能接受那些只反映了领域概念却舍弃了软件设计原则的拙劣设计。模型和设计的绑定需要的是在分析和程序设计阶段都能发挥良好作用的模型。如果模型对于程序的实现来说显得不太实用时,我们必须重新设计它。而如果模型无法忠实地描述领域的关键概念,也必须重新设计它。这样,建模和程序设计就结合为一个统一的迭代开发过程。
将领域模型和程序设计紧密联系在一起绝对是必要的,这也使得在众多可选模型中选择最适用的模型时,又多了一条选择标准。它要求我们认真思考,并且通常会经过多次反复修改和重新构建的过程,但是通过这样的过程可以得到与设计关联的模型。因此:
软件系统各个部分的设计应该忠实地反映领域模型,以便体现出这二者之间的明确对应关系。我们应该反复检查并修改模型,以便软件可以更加自然地实现模型,即使想让模型反映出更深层次的领域概念时也应如此。我们需要的模型不但应该满足这两种需求,还应该能够支持健壮的UBIQUITOUS LANGUAGE(通用语言)。
从模型中获取用于程序设计和基本职责分配的术语。让程序代码成为模型的表达,代码的改变可能会是模型的改变。而其影响势必要波及接下来相应的项目活动。
完全依赖模型的实现通常需要支持建模范式的软件开发工具和语言,比如面向对象的编程。
有时,不同的子系统会有不同的模型,但是从需求分析到代码编写的整个开发过程中,软件系统的每一部分只能对应一个模型。
单一模型能够减少出错的概率,因为程序设计直接来源于经过仔细考虑而创建的模型。程序设计,甚至是代码本身,都与模型密不可分。
要想创建出能够抓住主要问题并且帮助程序设计的单一模型并没有说的那么容易。我们不可能随手抓个模型就把它转化成可使用的设计。只有经过精心设计的模型才能促成切实可行的实现。想要使代码有效地描述模型就需要用到程序设计和实现的技巧。知识消化人员需要研究模型的各个选项,并将它们细化为实用的软件元素。软件开发于是就成了一个不断精化模型、设计和代码的统一的迭代过程。

建模范式和工具支持

建模范式(modeling paradigm)——一种从领域中提取概念的特殊方式,与工具结合起来使用,为这些概念创建软件类比。(例如,面向对象编程和逻辑编程)
为了使MODEL-DRIVEN DESIGN发挥作用,一定要在可控范围内严格保证模型与设计之间的一致性。要实现这种严格的一致性,必须要运用由软件工具支持的建模范式,它可以在程序中直接创建模型中的对应概念。
请添加图片描述
面向对象编程之所以功能强大,是因为它基于建模范式,并且为模型构造提供了实现方式。从程序员的角度来看,对象真实存在于内存中,它们与其他对象相互联系,它们被组织成类,并且通过消息传递来完成相应的行为。许多开发人员只是得益于对象的技术能力——用其组织程序代码,只有用代码表达模型概念时,对象设计的真正突破之处才彰显出来。Java和许多其他工具都允许创建直接反映概念对象模型的对象和关系。在MODEL-DRIVEN DESIGN中,建模范式是逻辑的,而模型则是一组逻辑规则以及这些规则所操作的事实。
像C这样的语言并不适用于MODEL-DRIVEN DESIGN,因为没有适用于纯粹过程语言的建模范式。对过程语言而言,程序员要告诉电脑一系列要执行的操作步骤。尽管程序员也会考虑到领域中的概念,但是程序本身仅仅是一组对数据进行的技术操作。最终程序可能是实用的,但是它并没有包含太多的意义。过程语言通常支持复杂的数据类型,这些数据类型一开始就能很自然地对应到领域中的概念上,但是它们也只是被组织在一起的数据,并不能描述领域的活跃方面。因此,用过程语言编写的软件具有复杂的函数,这些函数基于预先制定的执行路径连接在一起,而不是通过领域模型中的概念联系进行连接的。
面向对象设计是目前大多数项目所使用的建模范式,也是本书中使用的主要方法。

为什么模型对用户至关重要

从理论上讲,也许你可以向用户展示任何一种系统视图,而不管底层如何实现。但实际上,系统上下层结构的不匹配轻则导致误解,重则产生bug。
MODEL-DRIVEN DESIGN要求只使用一个模型(在任何一个上下文中都是如此)。大部分的设计建议和例子都只针对将分析模型和设计模型分离的问题,但是这里的问题涉及了另外一对不同的模型:用户模型设计/实现模型
当然,大多数情况下,没有经过处理的领域模型视图肯定不便于用户使用。但是在用户界面中出现与领域模型不同的"影像"将会使用户产生迷惑,除非这个"影像"完美无缺。如果网站收藏夹实际上只是快捷方式文件的集合,那么应该将这一事实告诉用户,还应该删除之前那个起误导作用的模型。这样不但能使收藏夹的功能更加清晰,用户还可以利用自己所知道的文件系统的知识来对网站收藏夹进行操作。比如,用户可以用资源管理器来重新组织已收藏的文件,而不是用浏览器内置的拙劣工具。而电脑高手还能够灵活地在文件系统的任何位置存储网页快捷方式。仅仅通过删除起误导作用的多余模型就可以让应用程序的功能更加强大且清晰。如果程序员认为原有模型足够好,那么为什么还要让用户学习新模型呢?此外,如果以不同的方式来存储收藏夹,比如将其存储在一个数据文件中,这样收藏文件就可以有自己的规则了。这些规则很可能是应用于网页的命名规则。这又是一个单一模型。这个模型告诉用户所有关于网站的命名规则都适用于网站收藏夹。如果程序设计基于一个能够反映出用户和领域专家所关心的基本问题的模型,那么与其他设计方式相比,这种设计可以将其主旨更明确地展示给用户。让用户了解模型,将使他们有更多机会挖掘软件的潜能,也能使软件的行为合乎情理、前后一致。

HANDS-ON MODELER

人们总是把软件开发比喻成制造业。这个比喻的一个推论是:经验丰富的工程师做设计工作,而技能水平较低的劳动力负责组装产品。这种做法使许多项目陷入困境,原因很简单——软件开发就是设计。虽然开发团队中的每个成员都有自己的职责,但是将分析、建模、设计和编程工作过度分离会对MODEL-DRIVEN DESIGN产生不良影响。
我曾经在一个项目中负责协调不同的应用程序开发团队,帮助开发可以驱动程序设计的领域模型。但是管理层认为建模人员就应该只负责建模工作,编写代码就是在浪费这种技能,于是他们不准我编写代码或者与程序员讨论细节问题。
开始项目进展的还算顺利。我和领域专家以及各团队的开发负责人共同工作,消化领域知识并提炼出了一个不错的核心模型。但是该模型却从来没有派上用场,原因有两个。
其一,模型的一些意图在其传递过程中丢失了。模型的整体效果受细节的影响很大,这些细节问题并不是总能在UML图或者一般讨论中遇到的。如果我能撸起袖子,直接与开发人员共同工作,提供一些参考代码和近距离的技术支持,那么他们也许能够理解模型中的抽象概念并据此进行开发。
第二个原因是模型与程序实现及技术互相影响,而我无法直接获得这种反馈。例如,程序实现过程中发现模型的某部分在我们的技术平台上的工作效率极低,但是经过几个月的时间,我才一点一点获得了关于这个问题的全部信息。其实只需较少的改动就能解决这个问题,但是几个月过去了,改不改已经不重要了。因为开发人员已经自行编写出了可以运行的软件——完全脱离了模型的设计,在那些还在使用模型的地方,也仅仅是把它当作纯粹的数据结构。开发人员不分好坏地把模型全盘否定,但是他们又有什么办法呢?他们再也不愿意冒险任由呆在象牙塔里的架构师摆布了。
与其他项目一样,这个项目的初始环境倾向于不让建模人员参与太多的程序实现。对于该项目所使用的大部分技术,我都有着大量的实践经验。在做建模工作之前,我甚至曾经在同类项目中领导过一个小的开发团队,所以我对项目开发过程和编程环境非常熟悉。但是如果不让建模人员参与程序实现,我就是有这些经历也无法有效地工作。
如果编写代码的人员认为自己没必要对模型负责,或者不知道如何让模型为应用程序服务,那么这个模型就和程序没有任何关联。如果开发人员没有意识到改变代码就意味着改变模型,那么他们对程序的重构不但不会增强模型的作用,反而还会削弱它的效果。同样,如果建模人员不参与到程序实现的过程中,那么对程序实现的约束就没有切身的感受,即使有,也会很快忘记。MODEL-DRIVEN DESIGN的两个基本要素(即模型要支持有效的实现并抽象出关键的领域知识)已经失去了一个,最终模型将变得不再实用。最后一点,如果分工阻断了设计人员与开发人员之间的协作,使他们无法转达实现MODEL-DRIVEN DESIGN的种种细节,那么经验丰富的设计人员则不能将自己的知识和技术传递给开发人员。
**HANDS-ON MODELER(亲身实践的建模者)**并不意味着团队成员不能有自己的专业角色。包括极限编程在内的每一种敏捷过程都会给团队成员分配角色,其他非正式的专业角色也会自然而然地产生。但是如果把MODEL-DRIVEN DESIGN中密切相关的建模和实现这两个过程分离开,则会产生问题。
整体设计的有效性有几个非常敏感的影响因素——那就是细粒度的设计和实现决策的质量和一致性。在MODEL-DRIVEN DESIGN中,代码是模型的表达,改变某段代码就改变了相应的模型。程序员就是建模人员,无论他们是否喜欢。所以在开始项目时,应该让程序员完成出色的建模工作。因此:
任何参与建模的技术人员,不管在项目中的主要职责是什么,都必须花时间了解代码。任何负责修改代码的人员则必须学会用代码来表达模型。每一个开发人员都必须不同程度地参与模型讨论并且与领域专家保持联系。参与不同工作的人都必须有意识地通过UBIQUITOUS LANGUAGE与接触代码的人及时交换关于模型的想法。
将建模和编程过程完全分离是行不通的,然而大型项目依然需要技术负责人来协调高层次的设计和建模,并帮助做出最困难或最关键的决策。
MODEL-DRIVEN DESIGN利用模型来为应用程序解决问题。项目组通过知识消化将大量杂乱无章的信息提炼成实用的模型。而MODEL-DRIVEN DESIGN将模型和程序实现过程紧密结合。UBIQUITOUS LANGUAGE则成为开发人员、领域专家和软件产品之间传递信息的渠道。
最终的软件产品能够在完全理解核心领域的基础上提供丰富的功能。
如上所述,MODEL-DRIVEN DESIGN的成功离不开详尽的设计决策。

参考

《领域驱动设计 软件核心复杂性应对之道》 Eric Evans 著, 赵俐 盛海艳 刘霞 等译, 任发科 审校

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1978732.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

拉刀基础知识——拉刀的种类

如前面所说:近期要围绕拉削和拉刀这个话题,分享一些相关的内容,从最基础的知识开始,为此还专门买了本旧书——《拉刀设计》入门学习。废话不多说,直接开始。 拉刀最早由冲头演变而来,用于加工方孔&#xf…

【Web】TFCCTF 2024 部分题解

目录 GREETINGS SURFING SAFE_CONTENT FLASK DESTROYER GREETINGS 打express的SSTI GitHub - TheWation/NodeJsSSTI: Express app with Pug templates demonstrating SSTI vulnerability and secure implementation for educational purposes. payload: /result?user…

历史标签如何时间迁移?

本文解析的论文是: Lin, C.; Du, P.; Samat, A.; Li, E.; Wang, X.; Xia, J. Automatic Updating of Land Cover Maps in Rapidly Urbanizing Regions by Relational Knowledge Transferring from GlobeLand30. Remote Sens. 2019, 11, 1397. https://doi.org/10.33…

一刷代码随想录(动态规划2)

62.不同路径 题意: 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少…

我的面包多

我的面包多主页:https://mbd.pub/o/author-bGubnGpq 欢迎咨询。

JavaSE面试篇章——一文干破Java集合

文章目录 Java集合——一文干破集合一、集合的理解和好处1.1 数组1.2 集合 二、集合的框架体系三、Collection接口和常用方法3.1 Collection接口实现类的特点3.2 Collection接口遍历元素方式1-使用Iterator(迭代器)3.2.1 基本介绍3.2.2 迭代器的执行原理3.2.3 Iterator接口的方…

数据库典型例题2-ER图转换关系模型

1.question solution: 2.做题步骤 一些解释&#xff1a; <1弱实体把强属性的主键写进去&#xff0c;指向强属性。eg:E6_A13指向E5_A13 <21:1&#xff0c;1:n&#xff0c;m:n&#xff1a;将完全参与的一方&#xff08;双线&#xff09;指向另一方&#xff0c;并将对方的…

AutoCAD ObjectArx二次开发(三) 创建MFC界面

主题&#xff1a;本章节主要介绍在ObjectARX项目中如何使用MFC界面进行交互操作&#xff0c;具体采用模态对话框的形式。 一、创建MFC的对话框 在项目中添加新项&#xff0c;选择MFC类&#xff0c;点击确定按钮&#xff0c;如下图所示。 然后会出现下图界面&#xff0c;填写类…

苹果应用程序清理卸载工具:App Cleaner Uninstaller Pro for Mac

App Cleaner & Uninstaller Pro 是一款专为 Mac OS X 操作系统设计的应用程序清理和卸载工具。这款软件的主要功能是帮助用户彻底删除不需要的应用程序、插件和残留文件&#xff0c;从而释放磁盘空间并提高系统性能。 特点和优势&#xff1a; 彻底卸载应用程序&#xff1a;…

【软件设计书】详细设计说明书和概要设计说明书(Word原件直接套用)

系统详细设计说明书案例&#xff08;直接套用&#xff09; 1.系统总体设计 2.性能设计 3.系统功能模块详细设计 4.数据库设计 5.接口设计 6.系统出错处理设计 7.系统处理规定 软件开发全文档下载&#xff08;下面链接或者本文末个人名片直接获取)&#xff1a;本文末个人名片直接…

【C语言】文件操作(下)

文章目录 前言1. 文件的读和写2. 文件的顺序读写2.1 顺序读写函数的介绍2.1.1 fgetc 和 fputc2.1.2 fgets 和 fputs 3. 文件缓冲区4. 总结 前言 在之前文件操作&#xff08;上&#xff09;和文件操作&#xff08;中&#xff09;的文章中&#xff0c;我从为什么要使用文件再到文…

RabbitMQ高级特性 - 生产者消息确认机制

文章目录 生产者消息确认机制概述confirm 代码实现return 代码实现 生产者消息确认机制 概述 为了保证信息 从生产者 发送到 队列&#xff0c;因此引入了生产者的消息确认机制. RabbitMQ 提供了两种解决方案&#xff1a; 通过事务机制实现.通过发送确认机制&#xff08;confi…

CPU利用率100%该怎么办

1 节拍率 Linux 作为一个多任务操作系统&#xff0c;将每个 CPU 的时间划分为很短的时间片&#xff0c;再通过调度器轮流分配给各个任务使用&#xff0c;因此造成多任务同时运行的错觉。 为了维护 CPU 时间&#xff0c;Linux 通过事先定义的节拍率&#xff08;内核中表示为 H…

AI大模型定级体系

前言&#xff1a;一直以来人们对通用人工智能&#xff08;AGI&#xff09;的定义始终缺乏一个具体的衡量标准&#xff0c;而现在OpenAI已创建了一套分级系统。 AI大模型定级 OpenAI对于其大模型的定级有一个独特的分级体系&#xff0c;旨在描述其人工智能系统的发展阶段以及距…

网络安全埋头干活,也要抬头看路

24年的Hvv大家干的正是热火朝天&#xff0c;也有的干的无可奈何&#xff0c;确实Hvv的核心其实是在Hvv前的准备阶段&#xff0c;Hvv中可能更多的是盯监控。 7月份我也出了两趟差&#xff0c;看了一下在Hvv项目上的小伙伴&#xff0c;30%的时间是在处理误报&#xff0c;60%的时…

51 for 循环与 while 循环

Python 主要有 for 循环和 while 循环两种形式的循环结构&#xff0c;多个循环可以嵌套使用&#xff0c;并且还经常和选择结构嵌套使用来实现复杂的业务逻辑。 while 循环一般用于循环次数难以提前确定的情况&#xff0c;当然也可以用于循环次数确定的情况。 for 循环一般用于…

共享`pexlinux`数据文件的网络服务

实验环境准备&#xff1a; 1.红帽7主机 2.要全图形安装 3.配置网络为手动&#xff0c;配置网络可用 4.关闭vmware DHCP功能 一、kickstart自动安装脚本制作 1.安装图形化生成kickstart自动脚本安装工具 2.启动图形制作工具 3.图形配置脚本 这里使用的共享方式是http&#xff0…

SpringBoot + Vue + ElementUI 的人力资源管理系统-附项目源码与配套文档

摘 要 在如今这个人才需求量大的时代&#xff0c;各方企业为了永葆企业的活力与生机&#xff0c;在不断开 拓进取的同时&#xff0c;又广泛纳用人才&#xff0c;为企业的长久发展奠定了基础。于是&#xff0c;各个企业与部 门机构&#xff0c;都不可避免地会接触到人力资源管理…

微信小程序之behaviors

目录 概括 Demo演示 进阶演示 1. 若具有同名的属性或方法 2. 若有同名的数据 3. 若有同名的生命周期函数 应用场景 最后 属性&方法 组件中使用 代码示例&#xff1a; 同名字段的覆盖和组合规则 概括 一句话总结: behaviors是用于组件间代码共享的特性, 类似一…

03 RabbitMQ:HelloWorld

03 RabbitMQ&#xff1a;HelloWorld 1. 目标2. 实现2.1. 新建Spring Boot 项目2.1.1. 新建生产者&#xff08;producer&#xff09;项目2.1.2. 新建生产者&#xff08;consumer&#xff09;项目 2.2. 导入依赖2.3. 代码2.3.1. 发送消息&#xff08;producer&#xff09;2.3.2. …