领域驱动设计(DDD)是由Eric Evans首次提出的一种软件开发技术,包含战略,哲学,战术和技术要素,并且与许多特定实践相关。我已经写了关于为什么需要DDD的文章(即使您认为自己不需要),但是在决定使用DDD之后的问题是:如何学习DDD?
尽管完整的解释将需要两百本500页的书,但DDD的本质却非常简单:以域术语捕获域模型,将模型嵌入代码中,并保护其免受损坏。我们可以理解这些概念,并立即加以利用。尽管这种理解水平还很浅,但是它仍然很有用,并且可能足够令人耳目一新,可以大跌眼镜。(注意:在下面的示例中,我假设了一个面向对象的项目,但是原理仍然适用于其他情况。)
DDD战略 与理念
您可能知道DDD具有战略价值。这就是为什么许多拥有极其复杂域的公司都依赖它来生产可以随着业务快速发展的软件的原因。但是您知道DDD也有一个哲学主题吗?您可能已经听说过“泛在语言”一词,当您谈论它时,这是一个大嘴巴,但这是强调DDD基本原理的一种简捷方式。使它无处不在。在实践DDD时,领域术语至上的基本哲学可以分布在三个指导原则中:
通过与领域专家的互动,以领域的方式捕获领域模型。 换句话说,与您要解决问题的业务部门的人交谈,并首先从他们的角度了解他们。这是形成领域无处不在的语言并为和谐模型奠定基础的方式。
在代码中嵌入领域术语。这意味着用领域专家命名事物的方式来命名事物,包括类,方法,命令,尤其是域事件。这就是您在代码中反映域模型的方式。
保护域知识免受其他域,技术子域等的破坏。如果发现您的代码在谈论两个不同的问题(例如,域解决方案和技术实现),则将这些组件分开以使子域分开。这种策略往往导致班级只担负单一职责,并且词汇集中而简洁。在子域之间的边界处放置“翻译器”,以防止它们不必要地依赖于彼此的结构,并且还可以防止域术语含义的模糊。
这三个原则指导并告知了DDD。即使没有其他DDD做法和模式,了解和使用它们也会带来好处。让我们看一下如何在我们的软件开发项目中使用这些信息可以从中获得一些好处
了解领域并构建通用 语言(UL)
第一个元素是用领域术语捕获领域模型,它是泛在语言概念的基础,后者是整个团队(技术人员和非技术人员)都使用的一组通用术语和定义。该语言与业务领域有关,并使用业务领域中的术语,而不是IT技术术语。
从业务角度理解业务问题可以使开发人员与业务利益相关者以及技术团队之间进行更清晰的沟通。最后,当每个人都使用相同的术语并讲述相同的故事时,整个团队将拥有一个共享的问题模型,该模型可以将解决方案引导至反映业务运作方式而不是软件运作方式的模型。
与领域专家交谈时,请先做好功课,以便您提出主要问题。使用真实的场景来推动对领域的理解,并使用简单的模型来捕获基础知识,并在进行过程中注意问题和难点。让领域专家向您解释操作和问题,并使用相同的术语将其解释回来。以相同的方式浏览建议的解决方案,在发展通用语言并确定相关子域的同时,增进对术语和问题的相互理解。
建模 就是设计
在某种程度上,这都是设计。不同之处在于,某些模型的保真度较低,仅是为了提醒人们进行更大的讨论,而其他模型则经过详细描述和形式化,足以直接执行。是的,代码是另一种建模语言。它恰好是可执行的。即使是简单的事件建模,也可能对所有相关人员具有指导意义,并且足以识别关键功能和操作。然而,在所有级别的设计中,UL必须保持突出地位。
如果发现自己正在对计算机进行建模,请停止。 您已经太深入了,除非您想弄清楚子域中的一些技术难题。通常,将模型保持在领域专家会感兴趣的水平上。尽快用原型和峰值对模型进行验证,以更快地了解有效的方法和无效的方法。
意图公开界面
当您开始将域模型嵌入代码中时,很明显,您应该将类的名称与现实世界中的类命名为相同的名称,但是并不是很明显,您也应该将与域级操作相同的方法命名。
例如,如果领域专家说用户可以在应用程序的某个点上注册杂志订阅,请不要将实现该功能的方法命名为“ CreateCustomer”。而是以域的方式尽可能具体地命名。想象一下,领域专家将有一天查看您的代码。她会从接口(即从类和方法名)中了解其要点吗?与其说“ CreateCustomer”,不如说是“ SignUpForMagazineSubscription”。
通过这种方式,您的代码自然会构建出一组反映域概念的类和接口。通过在对话,模型和代码中使用相同的术语,这就是实现和谐模型的方式。结果是与用户的心理模型相匹配的面向任务的用户界面,或对熟悉该领域的新开发人员有意义的API。
上下文 映射
领域讨论中的某些概念将成为关键。他们将以他们为中心进行活动,并且与其他领域的定义可能会有所不同。换句话说,孤岛将开始在边界上下文周围的模型和对话中形成,它们是代码的子集,它们具有自己的独立模型,并具有供代码的其他部分使用的特定接口。
上下文边界的典型模式是从外部接受的一组命令,边界上的数据/对象转换从“外部”到“内部”。所有外部知识和相关性都不受此边界的约束,从而允许边界内的代码仅使用其自己的特定于子域的类,接口和术语。这支持独立的开发流,并通过定义明确的接口进行通信。
这也支持另一项活动:上下文映射。这包括识别和分类域内有限上下文之间的关系。
假设您有两个有界上下文,一个负责维护可用杂志订阅的目录,另一个负责处理信用卡费用。这两个上下文之间的关系很重要。它们可能共享一些共同的概念,并且在信息交换期间具有不同的信任级别。
这在开发过程中起作用,表现为两个团队之间的关系。如果负责信用卡收费的团队直接取决于目录团队使用的杂志类别,那么我们说信用卡收费团队与目录团队具有“消费者”关系。如果目录团队必须更改杂志类别,则将触发信用卡上下文的更改。
同样,如果信用卡团队需要将其他信息添加到杂志类中以完成功能,但目录团队不会进行更改或四周内无法进行更改,这将在很大程度上影响实施计划。这通常称为团队之间的“带宽”。请注意,依赖关系可能是相互的。在最坏的情况下,每个上下文都可能依赖于相同的“共享库”,如果不稳定,则可能会破坏每个上下文中的开发。
在上下文映射中发现的信息使您可以通过调整“旋钮”来减轻和重新排列上下文之间的依赖性,以实现最大程度的独立开发。请注意,在某些情况下,团队/上下文的边界可能是由业务约束而非逻辑/软件约束决定的。上下文映射也可以揭示这些问题。