微服务架构风格的DDD补充 Q&A

之前写了点随笔《微服务架构风格的DDD》,与朋友交流后,有些问题与想法,作些补充。

一、Q & A

  1. 【问】可否将《DDD》所说的Applicaiton Layer,Domain Layer 理解为两组不同的应用进程?例如有a, b, c 三个不同的应用程序属于Application Layer, 而d, e, f 三个不同的应用程序属于Domian Layer。a,b,c 依赖于 d,e,f 。

【答】不是不可以,但没必要这么理解。如果一个公司的业务很复杂,有大量的应用程序,需要对这些应用程序分组(划分子系统/ 划分系统 / 划分层次),可以按照更符合企业现状及发展规划的方式划分层次,例如: 前台系统、中台系统、后台系统,或者:前端应用层、编排层、共享服务层、核心层、主数据层, 或者:作业层、能力层、决策支持层、平台层。这也是《DDD》16.3章节所提到的大型结构Responsibility Layer 职责分层。面对大量的应用程序,参考公司所在的行业对应用系统分层,比简单的技术特征分层更有意义,没必要局限在Applicaiton Layer / Domain Layer /Infrastructure Layer 的划分。类似的 SaaS / PaaS / Iaas 也是技术特征分层,没有反映出公司所在的行业特征。 在单个应用程序里,理解Applicaiton Layer / Domain Layer /Infrastructure Layer的区别,对于架构设计及项目实施有意义。

  1. 【问】Android App 前端应用程序是否也可以按DDD划分 UI Layer / Applicaiton Layer / Domain Layer /Infrastructure Layer

【答】是的。 单独看前端应用(例如Android App),也可以很复杂,除了界面展示的部分,也会有缓存、也会有远程API调用,也会有手机硬件设备(摄像头、指纹)等的基础设施访问。也是一个独立的完整的应用程序,可以独立部署(安装)并运行。当然是可以按《DDD》的概念分层的。但客户端应用程序架构风格上与20年前传统的桌面应用程序架构风格差不了太多,只是运行的设备不同,体现在基础设施层有些差异。所以Android App按DDD概念分层在《DDD》这本书已经说得非常透彻,甚至个人认为有点啰嗦。前文主要想讨论对于微服务架构风格如何DDD。所以,从更高的层面上,将Frontend App与BFF App都看作是DDD分层的UI层。

  1. 【问】BFF 是属于UI层还是属于Application层?

【答】UI层。Backend For Frontend,顾名思义,服务于前端应用程序的后端应用程序,一个前端应用(包括不同平台的版本)通常对应于一个BFF。而一个前端应用,很可能是大杂烩,涉及多个领域,例如支付,理财,信贷,购物,都在一个前端App。这需要后端很多个领域的应用系统来支撑,这个BFF起到转发的作用,将支付,理财,信贷,购物等不同的请求代理到不同领域的系统。初看也有服务编排的作用,与Application Layer有些相似。但BFF并不聚焦于某个领域,而是与前端应用程序同步发展,可以认为它协调的是不同的Application Service。所以我个人倾向于将BFF归到UI层。

  1. 【问】《DDD》给出了哪些工具帮助拆分微服务应用?微服务应用应该拆分多大的粒度。

【答】我认为《DDD》中有两个比较重要的概念对解决这个问题有帮助:Ubiquitus Language、Bounded Context。Ubiquitus Language是要努力建设一份通用语言,这是在学习业务领域的专业知识,获得一致的理解。Bounded Context与Context Map则是要求找到业务领域的自然划分,与相互关联。Bounded Context概念还是有些抽象,能否顺利落地取决于对业务领域的认识有多深入。庖丁解牛就是最好的解释。先秦·庄周《庄子·养生主》:“依乎天理,批大郤,导大窾,因其固然”。顺着牛体的肌理结构,劈开筋骨间大的空隙,沿着骨节间的空穴使刀,都是依顺着牛体本来的结构。拆分微服务应用也是一样的,首重对业务领域的理解,再考虑团队结构等因素。更简单点的说,是遵循 “高内聚,低耦合”的基本原则。

二、领域对象的生命周期

  1. Aggregate

Aggregate聚合,实际是一组存在依赖(dependency)、关联(association)、组合(composition)、聚合(aggregation)这几种(UML里的)关系的对象集合。Domain Entity与Domain Primitive在内存中的存续期,是通过Aggregate聚合在一起的。
在设计初期,我们不一定能清晰的分析出Aggregate 的Root Entity,但如果遵循下面原则,则Root Entity会逐步浮现:

  • 同一个Aggregate内,一个实体可以引用另一个实体,但不要轻易引用另一个实体的ID;
  • 一些信息如果已经封装在Primitive中,则Entity应当引用Primitive,而不是单独引用Primitive里面的属性。
  • Aggregate内的Entity与Primitive需要保障一致性规则,不能被其它Aggregate操作,可通过root entity作为Aggregate操作的出入口,只有root entity才能依赖于root entity。
  1. Factory

负责创建Entity与VO,并确保“创建即一致”,即所创建出来的对象在Aggregate范围内是符合一致性规则的。可以是Factory Method, Abstract Factory, 也可以是Builder 设计模式,也可以是Spring IOC的 BeanFactory, 甚至是 root entity的某一个方法直接通过new 创建出aggregate内的其它entiry。
VO不能有public setter,Entity对于会影响一致性规则的属性,应该尽量避免 public setter。

  1. Repository

很多Entity最终需要持久化存储,而不能长期存在于内存中。Repository负责持久化或者从持久化存储中查找并初始化。

  • 一个领域模型定义的Repository数量一般与Aggregate数量差不多,Repository数量不与Domain Entity 一对一,一般远少于数据库的表的数量。
  • Repository接口的入参、出参不应该使用底层数据对象(Data Object),而是使用Domain Module所定义的 Entity 或 VO。Repository 接口在Domain Module定义,看不见Repository Module所定义的 Data Object。这个也是为了避免底层实现逻辑渗透到业务代码中的强保障。

三、一些设计技巧

  1. Domain Primitive

【定义】在一个特定的业务领域里,准确的表达一个基础概念、可自我验证、拥有行为Value Object。
DP是 Value Object 的进阶版,在VO的immutable基础上,增加了 Validity 、行为,可以看作是这个业务领域里的基础数据类型。
【用途】

  • Make Implicit Concepts Explicit (将 隐形的 概念 显性化)
  • Make Implicit Context Explicit (将 隐形的 上下文 显性化)
  • Encapsulate Multi-Object Behavior (封装 多对象 行为)
  1. Domain Event Publish-Subscribe

上一篇文章提到可以在Domain Model里定义一些Observer接口,当领域对象的状态发生变更时,或者Domain Service操作到某个点时,或者业务过程发生某个事件时,可以及时知会Application Layer。如果众多的Domain Entity或者Domain Service都需要实现Oberserver设计模式会是比较费力的。一种简单点的办法是实现一个通用的EventPublisher。但缺点是,这个通用的EventPublisher可能没有表示业务领域的概念却侵入到Domain Layer了。

Domain Event的发布与订阅通常是发生在同一个进程内的,事件的发布者并不关心事件的处理结果。事件的订阅者可以自行决定同步处理或异步处理,也可以再次发布到消息中间件或RPC。

  1. Specification Pattern 规约模式

某些业务场景可能特别依赖于大量业务规则,并且这些业务规则还是可灵活配置化的,这些场景可以考虑Specification Pattern。Specification Pattern在《DDD》中有详细描述,要求方法返回boolean,并且Specification对象可以 and / or / not 逻辑组合。

  1. 抽象第三方服务。

如果第三方服务可以抽象出一个接口,并且是当前领域的业务概念(Ubiquitus Language),则可以由领域层定义第三方服务的抽象接口,由基础设施层实现具体的第三方服务通信细节,由应用层将第三方服务的具体实现注入到领域层。这种方式与Repository接口类似。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,270评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,489评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,630评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,906评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,928评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,718评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,442评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,345评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,802评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,984评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,117评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,810评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,462评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,011评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,139评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,377评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,060评论 2 355

推荐阅读更多精彩内容