DDD战略4 架构

GitChat课程《领域驱动设计--战略篇》笔记,课程作者张逸

一.分层架构

  • 分层架构是运用最为广泛的架构模式,软件系统通过层(Layer)隔离不同的关注点(Concern Point),以应对不同需求的变化,同时隔离业务复杂度与技术复杂度,而引起技术实现变化的原因与引起领域逻辑变化的原因显然不同,这将导致基础设施和领域逻辑问题会以不同速率发生变化
1.经典的三层架构
  • 用户界面层(User Interface Layer) + 业务逻辑层(Business Logic Layer) + 数据访问层(Data Access Layer)
  • 当时企业系统都较为简单,本质上都是一个单体架构(Monolithic Architecture)的数据库管理系统,传统三层架构有效隔离了业务逻辑与数据访问逻辑,使得这两个关注点能够相对自由和独立地演化
2.DDD的经典分层架构
  • User Interface Layer + Application Layer + Domain Layer + Infrastructure Layer
层次 职责
用户界面/展现 层 向用户展现信息以及解释用户命令
应用层 很薄的一层,用来协调应用的活动。不包含业务逻辑,也不保留业务对象的状态,但保有应用任务的进度状态
领域层 包含关于领域的信息,是业务软件的核心所在。保留业务对象的状态,但对业务对象和它们状态的持久化被委托给了基础设施层
基础设施层 本层作为其他层的支撑库存在。提供层间的通信,实现对业务对象的持久化,包含对用户界面层的支撑库等作用
DDD的经典分层
3.分层的依据与原则
  • 分层架构模式有助于构建这样的应用:应用能被分解成子任务组,其中每个子任务组处于一个特定的抽象层次上
  • 分层的依据
    1)基于关注点为不同的调用目的划分层次。如传统三层架构:其上面向用户的体检与交互,居中面向应用与业务逻辑,其下面对各种外部资源与设备
    2)面对变化。分层时应针对不同的变化原因确定层次的边界,严禁层次之间互相干扰,至少把变化对各层带来的影响降到最低。层与层之间应该是正交关系,即两层之间唯一相关的依赖点是两层之间的协作点
    3)分层时应保证同一层的组件处于同一抽象层次
4.层之间的协作
  • 传统分层架构的依赖都是自顶向下传递,层次越处于下端,与具体的业务隔离得越远,越通用越公用。而越通用的层越可能与外部平台或框架形成强依赖,若依赖的传递方向仍然自顶而下,就会导致系统的业务对象也随之依赖于外部平台或框架
  • 依赖倒置原则(Dependency Inversion Principle,DIP)对传统的自上而下的依赖提出了挑战,要求高层模块不应依赖于底层模块,二者都应依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象,因为抽象更稳定
  • 高层模块不依赖于低层模块的优点有
    1)低层模块的细节实现可以独立变化,避免变化对高层模块产生污染
    2)编译时,高层模块可以独立于底层模块单独存在
    3)对于高层模块,低层模块的实现是可替换的
  • 根据依赖倒置原则,高层依赖于底层的抽象,即高层通过接口隔离了对具体实现的依赖,意味着整个具体依赖被转移到了外部,即发生了依赖注入(Dependency Injection)
  • 结合依赖倒置原则和依赖注入,可以有效地解除高层对低层的依赖

二.分层架构的演化

1.整洁架构
  • 在架构设计时,设计出干净的应用层和领域层,保持它们对业务逻辑的专注,而不掺杂任何具体的技术实现,从而完成领域与技术之间的完全隔离
  • 分为四层
    1)企业业务规则(Enterprise Business Rules)
    2)应用业务规则(Application Business Rules)
    3)接口适配器(Interface Adapters)
    4)框架与驱动器(Frameworks & Drivers)
    其中Application层面向应用,实现支持领域逻辑正常流转的非业务功能,通常为一些横切关注点,如日志、安全、事务等
整洁架构
  • 整洁架构包括以下特征
    1)层次越靠内的组件依赖的内容越少,处于核心的Entities没有任何依赖
    2)层次越靠内的组件与业务的关系越紧密,因而越不可能形成通用的框架
    3)Entities层封装了企业业务规则,准确地讲,它应该是一个面向业务的领域模型
    4)User Cases层是打通内部业务与外部资源的通道,提供了输出端口与输入端口,但它对外的接口展现其实是应用逻辑,或者说是一个用例
    5)Gateways、Controllers与Presenters本质都是适配器(Adapter),用于打通应用业务逻辑与外层的框架和驱动器,实现逻辑的适配以访问外部资源
    6)系统最外层包括框架和驱动器,负责对接外部资源,不属于系统开发的范畴,但选择这些框架和驱动器,是属于设计决策要考虑的内容
  • 属于适配器的Controllers、Gateways与Presenters可以视为网关(Gateway),对下,针对数据库、消息队列或硬件设备的适配器是南向网关,对于当前限界上下文是一种输出的依赖,对上,针对Web和UI是北向网关,对于当前限界上下文是一种输入的依赖
2.六边形架构
  • 六边形架构(Hexagonal Architecture)在满足整洁架构思想的同时,更关注与内层与外层以及与外部资源之间通信的本质
  • 内部六边形对应于DDD的应用层与领域层,外部六边形之外则为系统的外部资源,两个六边形之间的区域,均被视为适配器,并通过端口完成内外区域之间的通信与协作,故六边形架构又被称为端口-适配器模式
六边形架构
3.微服务架构
  • 自顶向下
  • 由内至外
微服务架构

三.领域驱动架构的演进

1.避免贫血的领域模型
  • 传统的"贫血模式",位于数据访问层的Java Bean只包含get和set方法
贫血模型架构
  • 合理将操作数据的行为分配给领域模型对象(Domain Model),即Entity和Value Object,而不是Service对象。同时,由于领域模型对象包含了领域逻辑,就需要从数据访问层转移到业务逻辑层。而那些不属于任何领域模型对象的领域逻辑,仍然应放在Service对象中
  • 演进后的领域驱动架构如下
将操作数据行为分配给领域模型对象的分层架构
2.保证领域模型的纯粹性
  • 上述架构中领域层与基础设施层之间产生了“双向依赖”,根据稳定依赖原则,需要让领域层依赖于一个更加稳定的基础,改进办法是增加DAOs的抽象
根据稳定依赖原则,增加DAOs抽象的架构
  • 领域驱动将管理领域对象的数据结构抽象为资源库(Repository),通过资源库访问领域对象自然就成为一种领域行为,演进后的架构如下
引入资源库的架构
3.用户展现层的变迁
  • 用户展示层需要不同的用户交互方式与呈现界面,例如Web、Windows或者多种多样的移动客户端,因此在分层架构中,无法再用“用户展现层”涵盖整个业务系统的客户端概念
  • 我们需要采取前后端分离的架构思想,使用户展现出分离为一个完全松耦合的前端层
  • 为了简化前端与后端的通信集成,可以为系统引入一个开放主机服务(OHS),为前端提供统一而标准的服务接口,即Controllers组件,演化后的分层架构如下
为解耦前端,引入Controllers组件的分层架构
4.引入应用层
  • 由于领域层的设计粒度过细,Controllers需要与封装了Entity与Value Object的Aggregate、Services以及抽象的Repositories接口协作。基于KISS原则或最小知识原则,调用者了解的知识越少越好,调用越简单越好,这就需要引入一个间接的层来封装,这就是应用层存在的主要意义,演进后的架构如下
为简化接口调用,引入应用层的分层架构
  • 应用层是高层抽象的概念,表达的是业务的含义。领域层是底层实现的概念,表达的是业务的细节。DDD要求应用层不包含业务逻辑,但对外提供一个一致的体现业务用例的接口
5.基础设施层的本质
  • 网关的含义有助于理解基础设施层的本质
  • 网关组件自身会参与到业务中,但真正做的事情只是对业务的支撑,提供了与业务逻辑无关的基础功能实现

四.限界上下文与架构

1.限界上下文的架构范围
  • 限界上下文不仅是针对领域模型的边界划分,还是对整个架构(包括基础设施层以及需要使用的外部资源)垂直方向的划分
  • 对于基础设施层需要访问的外部资源,以及为了访问它需要重用的框架或平台,不属于限界上下文的代码模型,但仍属于架构设计的考量范围
2.限界上下文的通信边界
  • 限界上下文之间是否为进程边界隔离,直接影响架构设计,这是因为进程内核进程间在如下方面存在迥然不同的处理方式
    1)通信
    2)消息的序列化
    3)资源管理
    4)事务与一致性处理
    5)部署
    6)系统对各个组件(服务)的重用方式与共享方式
  • 进程内的通信边界
    1)进程内通信意味着限界上下文运行在同一个进程中,可以通过实例化的方式重用领域模型或其他层次的对象。存在命名空间级别和模块级别两种设计方式,两者仅仅存在编译器的差异,模块级别的解耦更加彻底
    2)限界上下文在进程内通信意味着彼此之间的协作更加容易和高效,但也更容易产生耦合,注意使用防腐层(ACL)隔离不同限界上下文
  • 进程间的通信边界
    1)进程间通信意味着限界上下文以进程为边界,彼此之间不能直接调用彼此的方法,需要通过分布式的通信方式,产生了数据库共享和零共享两种风格的架构
    2)数据库共享架构
    分开考虑代码模型和数据库模型,多个限界上下文代码运行是进程分离的,但共享彼此的数据库
    3)零共享架构
    每个限界上下文都有自己的代码库、数据存储以及开发团队,每个限界上下文选择的技术栈和语言平台也可以不同,限界上下文之间仅仅通过限定的通信协议和数据格式进行通信。
    零共享保证每个限界上下文可以独立演化,易于管理。但需要考虑通信的健壮性,数据库的一致性,运维和监控的复杂度
3.通信边界对架构的影响
4.限界上下文、六边形架构与微服务
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容