《领域驱动设计之PHP实现》全书翻译 - DDD入门

DDD 入门

  1. 《领域驱动设计之PHP实现》全书翻译 - DDD入门
  2. 《领域驱动设计之PHP实现》全书翻译 - 架构风格
  3. 《领域驱动设计之PHP实现》全书翻译 - 值对象
  4. 《领域驱动设计之PHP实现》全书翻译 - 实体
  5. 《领域驱动设计之PHP实现》全书翻译 - 服务
  6. 《领域驱动设计之PHP实现》全书翻译 - 领域事件
  7. 《领域驱动设计之PHP实现》全书翻译 - 模块
  8. 《领域驱动设计之PHP实现》全书翻译 - 聚合
  9. 《领域驱动设计之PHP实现》全书翻译 - 工厂
  10. 《领域驱动设计之PHP实现》全书翻译 - 仓储
  11. 《领域驱动设计之PHP实现》全书翻译 - 应用服务
  12. 《领域驱动设计之PHP实现》全书翻译 - 集成上下文
  13. 《用PHP实现六边形架构》

如果你已经读过 Vaughn Vernon 和 Eric Evans 著作里的这些主题,你可能会很熟悉我们讲的是什么,因为我们大量借鉴了他们的定义和解释。领域驱动设计(DDD)是一种帮助我们理解和构建软件模型设计的方法。它提供了战略和战术模型工具来帮助我们设计高质量的软件从而达到我们的业务目标。

本书的主要目标是向你展示领域驱动设计战略模式的 PHP 代码实例。如果你想了解更多战略模式和领域驱动设计的核心,你最好去读 Vaughn Vernon 的《领域驱动设计精简版》和 Eric Evans 的《领域驱动设计参考:定义和模式摘要》。

更重要的是,领域驱动设计不是关于技术的。相反的,它是关于围绕业务挖掘知识,用技术来提供商业价值的。只有当你有能力理解你公司的业务时,你才可能参与到软件模型的探索过程,从而得到一个通用语言(Ubiquitous Language)。

为什么领域驱动设计重要

软件不仅仅是代码。如果你好好思考过这一点,代码绝非我们所追求的最终目标。代码只不过是解决业务问题的手段。领域驱动设计强调要确保业务和软件使用同一语言。所以为什么非要用不同的语言来讲述呢?只要打破其中壁垒,就不再需要翻译或者繁琐的同步过程,信息也不会丢失。每个人都可以为探索业务领域做出贡献,而不仅仅是程序员。最终所产出的软件就是公共语言的真实体现。

领域驱动设计同时提供了战略和战术设计的框架 -- 战略设计精确定位业务中最重要的领域,根据业务价值来进行开发;战术设计则通过久经考验构建块和模式来建立可工作的领域模型。

领域驱动设计的三个核心

领域驱动设计是交付软件的一种方法,它主要聚集三个核心要点:

1. 通用语言

为了建立业务领域的共同语言,领域专家和软件开发人员需要一起工作。这里没有我们与他们之分,这里只有我们。开发软件是一种商业投资而不仅是花销。为构建通用语言而作出的努力将帮助团队所有成员对领域有更深的认识。

2. 战略设计

领域驱动设计指明业务方向背后的战略,而不仅仅是技术层面。这有助于定义内部关系和早期预警反馈系统。在技术面,战略设计通过提供如何实现面向服务架构的动机来保护每个业务服务。

3. 战术设计

领域驱动设计提供工具和构建块来持续交付软件。战术设计工具产出的软件,不仅是正确,而且同时是易测试的和出错少的。

通用语言

通用语言,以及第十二章整合限界上下文,是领域驱动设计最具威力的一部分。

就上下文而言
现在设想一个限界上下文就是一个系统内的概念边界,边界内的通用语言是有其特殊含义的,而边界外的上下文概念可能有不同的含义。

那么,如何寻找,发现和捕获这些非常特别的语言呢,下面将列出几个要点:

  • 识别业务流程的关键点,输入和输出
  • 创建一些词汇和定义
  • 用一些文档来捕获重要的软件概念
  • 与团队其他人分享和扩展以上知识(开发人员和领域专家)

自从领域驱动设计诞生以来,改进构建通用语言进程的新技术层出不穷。其中最重要的也是现今使用最普遍的,就是事件风暴

事件风暴

Alberto Brandolini 在一个博客帖子上介绍了事件风暴及其好处,他解释得比我们简洁多了。事件风暴,就是一种快速发现复杂业务领域的研讨会形式:

  • 它非常强大:它让自己和许多参与者在数小时内提出一个完整业务流的综合模型,而以前往往需要数周。
  • 它非常吸引人:它整个想法就是为了构建一个模型而把有疑问的人和知道答案的人聚在同一个屋子里。
  • 它非常高效:它所得到的模型完全切合领域驱动设计的实施方式(尤其适合一种事件源方法),并可以快速确定上下文和聚合边界。
  • 它很简单:它的表示符号非常简单,也没有复杂的 UML 以致参会者想从讨论核心中抽身离开。
  • 它很有乐趣:我总是能主持一个美妙的工作会议,大家总是很投入并能提出超出他们预期的东西。正确的问题浮现了,气氛也是融洽的。

如果你想对事件风暴了解更多,请查阅 Brandolini 的书 《Introducing EventStorming》

关于领域驱动设计

领域驱动设计并不是银弹,就像软件里的一切都依赖于上下文。作为一个经验法则,使用它可以让你的领域模型简单化,绝不会增加复杂性。

如果你的应用仅仅是以数据为中心,用例也主要是在数据库行列上做 CRUD 操作,即增删改查,那么你并不需要领域驱动设计。你所需要的仅仅是在数据库之上做一个漂亮的前端。

如果你的应用少于 30 个用例,那最好使用像 Symfony 或者 Laravel 这样的框架来简单处理你的业务逻辑。

可是,如果你的应用超 30 个用例,你的系统可能走向一个可怕的大泥球。如果你知道你的系统确定无疑会变得越来越复杂,那么你应该考虑用领域驱动设计来应对这些复杂度。

如果你并不理解你工作中那些领域,因为它们很陌生,之前也没有任何人给出解决方案,那么对你来说应用领域驱动设计将变得很复杂。在这种情况下,你最需要与领域专家紧密工作来得到正确的模型。

棘手的部分

应用领域驱动设计并不容易。它需要时间和努力来投入到业务领域,术语,文献中,而不是代码堆里。你需要领域专家承诺参与到整个进程当中。这需要一个开放的,友好的持续交流,来将他们的行话(spoken lauguage)转化为我们的软件模型。另外我们要努力避免使用技术思维,而应该首先认真思考对象的行为和通用语言。

战略总结

为了给出领域驱动设计战略这部分一个总体概括,我们将用 Jimmy Nilsson 的书《应用领域驱动设计与模式》中的一种方法,即考虑两个空间:问题空间和解空间。

在问题空间里,领域驱动设计用领域和子域来规划和规类公司想要解决的问题。在在线旅行社(OTA)这个例子当中,问题是关于处理像航班机票,酒店预订这样的事情。这样的领域就可以规划为不同的子域,如价格,库存,用户管理等等。

在解空间里,领域驱动设计提供两种模式:限界上下文和上下文映射图。其目标是定义如何为所有已识别的子域提供一个实现,通过定义他们的交互和这些交互的细节。继续用 OTA 这个例子,每个子域问题都将用一个限界上下文实现来解决。例如,一个由价格管理子域团队开发的自定义 web 应用,以及用户管理子域的一个现成解决方案。上下文映射图将展示每个限界上下文是怎样与其它部分发生关联的。在上下文映射图内,我们可以看到两个限界上下文间有什么关联形式(例如:客户-供应商,伙伴)。理想的方案是每个子域由一个限界上下文实现,但实现不可能总是如此。就实现来说,依照领域驱动设计你就最终以一个分布式架构结束。正如你已经知道的,分布式架构远比单体架构复杂,那么为什么这个方法有意思,尤其是对大而复杂的公司来说?这真的值得吗?是的,它值得!

分布式架构被证明能提高企业整体生产效率,因为它能为你的产品定义好边界,从而让专门的团队来开发。

如果你需要解决的领域问题不是很复杂,应用领域驱动设计的战略部分会增加不必要的开销同时会拖慢你的研发速度。

如果你想了解更多关于领域驱动设计的战略部分,你应该去看看Vaughn Vernon的书《实现领域驱动设计》的前三章,或者Eric Evans的《领域驱动设计:软件核心复杂性应对之道》,二者对于这方面都有专门的讲解。

相关趋势:微服务与自包含系统

现在还有一些其他遵循领域驱动设计的相关运动正蓬勃发展,微服务和自包含系统就是很好的例子。James Lewis 和 Martin Fowler 在 Microservices Resource Guide 里是这样定义微服务

微服务架构风格是把单个应用的开发分解为一个个小的服务的方法,每个服务都有自己独立的进程和轻量的通信机制,通常是用 HTTP 资源的 API。这些服务都是围绕其业务能力构建,可独立部署自动升级,去中心管理。服务可以用不同的程序语言编写,使用不同的数据存储技术。

如果你想了解更多关于微服务的内容,他们的指导就是一个良好的开端。微服务是怎样与领域驱动设计发生关系的?按照 Sam Newman 《微服务设计》 一书中的解释,微服务就是领域驱动设计的限界上下文的实现。

除了微服务,另外一个相关的趋势就是自包含系统(SCS),依据自包含系统官网的解释:

自包含系统是关注将功能分离至许多独立系统的一种架构,由这些独立系统相互协作来提供一个完整的逻辑系统。这可以避免单体系统不断成长导致最后变得不可维护的问题。纵观过去的几年里,我们看到许多中型和大型项目受益于此。这个思路是把一个大型系统分解为一些更小的自包含系统,依照下列确定的规则(官网上同样有阐明自包含系统 的七个特征):
  1. 每个自包含系统是一个自治的web应用。对于自包含系统里的所有处理数据的逻辑和所有渲染 web 界面的代码都包含在其中。一个自包含系统可以完全自主处理其所有的用例,不用依赖其它可用的系统。
  2. 每个自包含系统由一个团队管理。这并不是必然意味着只有一个团队才可以改变代码,不过所有者团队可以最终决定什么可以放进代码库,例如合并,拉取请求。
  3. 不同自包含系统或者第三方系统间的通信任何情况下都应该是异步的。尤其是其他自包含系统或扩展系统不应该同步访问自包含系统内的请求/回复。这一点能帮助解耦系统,减少失败结果,因此能支持自治性。其目的是关于时间上的解耦:一个自包含系统可以很好的工作即使其它自包含系统临时离线。即使通信技术层次是同步的也能实现,例如复制数据或缓存请求。
  4. 一个自包含系统有可选的服务 API。因为每个自包含系统都有自己的 web UI 同用户交互,所以不需要一个 UI 服务。不过即便如此,为移动客户端或者其他自包含系统而存在的 API 仍然是有用的。
  5. 每个自包含系统必须包含数据与逻辑。要真正实现任何有意义的功能,两者都是必需的。一个自包含系统应该通过自身实现所有功能因此两者缺一不可。
  6. 一个自包含系统通过自身的UI将功能交给最终用户使用。因此自包含系统不应该与其他自包含系统共享UI。自包含系统可能仍旧与其它系统有关联。不过,异步整合意味着即使在其他自包含系统不可用的情况下还应该能正常工作。为避免紧耦合,一个自包含系统不应该与其他自包含系统共享业务代码。也许可以通过创建一个自包含系统或者公共库,例如:数据驱动层或者认证客户端。

练习

与你的同事讨论例如分布式架构的利弊。考虑用不同语言,部署过程,责任心等等。

小结

在这一章里你将学到:

  • 领域驱动设计不是关于技术的;它的价值实际上是通过聚焦你工作领域中的模型体现的。每个人都参与到领域发现的过程,开发人员和领域专家用相同的语言来共同建立知识,即通用语言。
  • 领域驱动设计提供战术和战略模型工具来设计高质量软件。战略设计关注业务方向,帮助明确内部关系,用明确的强边界严格地保护好每个业务服务。战术则为迭代设计提供有用的构建块。
  • 领域驱动设计只有在明确的上下文中才有意义。它并不是软件中所有问题的银弹,所以是否用它应根据你手中工作的复杂度来决定。
  • 领域驱动设计是一项长期投资;它需要长期努力。开发人员需要与领域专家紧密合作,同时要根据业务思考。最后,业务中的客户因素也是需要你考虑的。

实现领域驱动设计需要努力。如果它很简单,那每个人都可以写出高质量代码了。准备好了,因为你马上就要开始学习怎么写这些代码了,在读的过程中,你将可以完美地描述清晰你公司现有的业务。享受这段旅程吧!

阅读 4k

推荐阅读