浅谈我对 Domain-Driven Design 的理解和 Rails 中的实作

..

Domain-Driven Design (DDD)是一门不明觉厉的软件架构设计理论,本文整理了我的一些杂记。

我发现 DDD 每个人看到的重点都不一样,我到现在还是觉得有种瞎子摸象的感觉。

我想这是因为 DDD 是个大杂烩,把软件需求管理、大架构、小架构 在同一个理论下解释。因此三个部分,看你爱讲哪一块,每个人看的重点不同。

1. 和领域专家的共通语言,重点放在与领域专家的沟通 。我认为这一块就是需求管理、专案管理。很多其他书跟这部分重叠了,讲的也比 DDD 详细完整。

2. 战略层次: 拆解不同领域,例如用 microservice 来做,或是用 Modular 来做(下述) 。我认为这一块是 DDD 比较精彩的部分,也是我认为最重要的部分。

3. 战术层次: 例如 Service Object。我认为这一块偏向物件导向理论,我觉得也不是重点,有很多其他讲物件导向的书在讲了,例如 SOLID 和各种 Design Pattern 等等。用了这些招数,也不表示就是 DDD。这块还有出一些很进阶的大架构常跟DDD一起讨论,例如 Hexagonal Architecture, Clean Architecture 等等,但我觉得都太难理解导入了,已经远离DDD的本质。

基础理论

DDD 的圣经本是 领域驱动设计,不过我真正有看完的是这两本 领域驱动设计精粹领域驱动设计精简版,重点大约是

  • Bounded Context 跟 Ubiquitous Language
    • Subdomain, Core Domain, Supporting Subdomain, Generic Subdomain 等等概念
    • 大体上就是将软件切开成不成领域,每个领域内有自己统一的领域语言,让所有关系人(包括PM跟开发人员)都用一样的语言沟通
  • Context Mapping
    • 描述各种 bundled context 之间的沟通方式和 API 方法
  • Entity, Aggregate, Value Object, Service
    • 定义模型物件的种类,算物件导向的常识了
    • 根据业务,用 transaction 为单位隔离出来
    • 不需要即时的,非 transaction 的,则拆开不同 aggregate 用异步处理
  • Domain Event 领域事件
    • 不需要同一个 transaction 的其他操作,用异步的 event 发布出去
    • 用于跨 bounded contexts 的异步(de-couple)沟通
    • 这也可以存下来
  • 建模方法
    • 和领域专家做 「事件风暴 Event Storming」。但其他方法也大同小异的样子,包括:
      • Specification by Example 但这个作者认为会额外费工15%~25%…. XD
      • Impact Mapping
      • User Story Mapping
    • Event Storming 在 DDD 圣经本并没有,我其实不喜欢后来这被加进 DDD 里面一起讲。我之前也有花钱上过课程,也听过现场演示,但上完的感觉就是看不出来跟 User Story 需求收集有什么不一样。DDD 我认为还是讲技术就好,硬是把需求管理也纳进来包山包海都讲,我觉得太分散重点了。

以上就基础理论来说,其实也不是这么难。本质目标就是大型软件需要做(概念上的)切分。剩下的问题是如何实作。

更多DDD介绍可以参考 DDD 学习路径与资源分享

实作

我认为DDD其实是没有告诉你一定要怎么实作切分的,因此找东西看的时候就会坠入五里雾,例如就是要弄微服务才是 DDD?

圣经 Domain-Driven Design 这本书,这本书也没讲微服务。但是后人实作时,就把微服务扯到也是DDD。做 SOA? 或是 Clean Architecture (这更难懂)?或是 Hexagonal Architecture (也很难懂) 以及加上这本 Implement Domain-Driven Design 书也提到很多技巧,例如 Repository, Event Sourcing 等等,好像这些设计都派上用场,才是 DDD ?

众所周知,Rails 是一个单体(Monolithic)架构,同一个应用尽量不要实际拆分不同专案。我反对在 Rails 上用非标准的 MVC 架构,因为这会让后续维护非常困难,新人上手难度爆表。任何一个新手学 Rails 三个月之后,若还是完全看不懂你的 Rails 专案架构,我认为很可能就是不好的架构。

因此在追求 Rails 如何实践 DDD 的路上,我看到以下几种方式。基本上,我认为只要可以拆模组边界,都是符合 DDD 的 bounded context 目的。

Interface Object

对 Rails 来说,第一步不应该直接就跳去 SOA 或微服务架构,而是应该先在单体架构内做 Component 切分即可,明确定义 public interface,只透过 class method 呼叫即可。

也就是这篇文章 Ruby on Rails – Bounded contexts via interface objects 所用的方式。

Component-Based Rails Applications

Component-Based Rails Applications

这本书采用 Rails engine 来做 DDD,听起来蛮不错的,实作上会碰到的问题有

  1. require 问题
  2. Migration 问题
  3. Testing 问题
  4. Asset loading 问题

总之就是问题蛮多的,等于是 Rails Engine 有什么坑,你就会踩到什么。

我感觉 Rails Engine 在 Rails core 中并不是优先级很高的功能,社群也不是很多人在用这个功能,多半是一些简单的通用后台在用这个功能而已。 因此在你的应用中依赖这个功能,我认为是蛮危险的,会浪费很多时间在踩坑。

刚点进去看作者官网,发现作者自己都放弃 CBRA 这方式了,改推基于 package 的 Gradual modularization (又一本新书作者还在写)

Domain-Driven Rails

products.arkency.com/domain-driven-rails/

这本书的作者 Arkency 也是不爱 Rails engine,因为它们认为 engine 主要用在拆 UI (web level),例如拆 admin UI,但是拆 admin UI 跟 DDD 要拆的 domain 其实是两回事。另外就是 Rails engine 方案会依赖 Rails,这也是 Arkency 不喜欢的。

几个内容心得:

  • Bounded Contexts
    • 其实就是拆 namespace,只透过 explicit class method 接口去沟通
  • Aggregates, value object, services 是常识了
  • Command bus
  • Domain events 用在跨 bounded contexts 沟通 de-couple
  • CQRS 主要是 read performance,另一个好处则是简化 query,会比 cache 不易出错 consistent
  • Event Sourcing 实作超高难度
    • 理论很美好,但实作感觉超难
    • 整个流程和思维要改成用 event 纪录为主,而且还需要搭配 CQRS 才实用,简直超大工程
    • 有看到一个 Gem 套件 www.sequent.io/

CQRS 跟 Event Sourcing 都是 DDD 圣经本没有的东西,我认为也不是必要的架构,要看业务场景需求吧。我没有实作过。

Shopify 的 Packwerk 方案 (Modular Monolith)

Shopify 是世界上采用 Ruby on Rails 最大的单体应用之一 (在采用 Ruby 的公司市值排名第二) 很具有代表性。他们采用的理论是 Modular Monolith,并且释出自己的 Packwerk 套件。虽然文件上没有特别强调 DDD,但我认为其实这就是在实践DDD最重要的精神。

Modular Monolith 其实呢,就是仍然保持单体应用(一个codebase),但是内部严格限制不同领域的边界(strictly enforced boundaries between different domains)。

实作上其实跟上面 Interface Object 和 Arkency 方式差不多的,只是 Packwerk 这个套件是个检查器(validator),可以定义这个边界,以及严格检查程式码是否满足 package 条件。毕竟团队长这么大,又只有一个 codebase 的话,需要严格的程式码检查 package 之间的依赖,同时 Packwerk 还会检查这个依赖不能是个 cycle 环(指套件互相依赖),必须是 acyclic 无环。

以上,反正我不会纠结到底是不是DDD了。就像 Shopify 那两篇 Blog 文章,完全没有提到 DDD 耶。

请按赞:


想在手机阅读更多Ruby on Rails资讯?下载【香港硅谷】Android应用
分享到Facebook
技术平台: Nasthon Systems