笔记 - 《领域驱动设计:软件复杂性应对之道》

简介: 笔记 - 《领域驱动设计:软件复杂性应对之道》

最近翻了一下《领域驱动设计-软件复杂性应对之道》这本书,翻完之后,由于很难完全静下心来以及自己的经验尚浅,总感觉体会不是很深,所以为了加深理解并方便后续回味,觉得需要再梳理一下并记录一下,故结合书中的一些知识点和本人的一些思考写下本文。

通过本文,你可以:

1)了解到领域驱动设计的一些名词和概念。

2)看到一些结合这些元素的示意图,便于理解和记忆。

1 道是什么

这本书的子标题还是很吸引人的:软件复杂性应对之道。看完这本书就能得其"道"了吗?它讲的”道“是什么那?本人看完之后刚开始是比较失望的,因为联想了日常的工作,好像并没有实质性明确的方法步骤,能解很好地告诉我该如何解决软件复杂性。

但是真正的道不正是如此吗。我们曾做过无数的试卷,并没有哪本试题集可以把所有的题型介绍完,但是每到题拆解下来,它的知识点都是可以映射到考点里面、书本中的。

我们想直接看到题目的答案,却忽略了锻炼做题的思路。很多人做错后,归结于智商(能力)的问题,但本质还是没有抓住重点。我们需要研究问题的解法和策略,思考解题的思路,并进行类似题型的训练,才能把短期的信息沉淀为长期的知识,把别人的”智商“转化为自己的能力。

image.png


1.1 注重基础概念

作者在提出领域驱动设计的概念之后,花了很多篇幅去介绍一些非常基本的概念。比如:实体 entity、值对象 value object等。很多人根据仅有的经验,会认为就是换一种称呼而已,我们内部有自己的一些命名也是差不多的意思。是的,单个对象的定义本身就是很简单,但是正是这些简单的定义才能演化出复杂的推导。

西方文明是讲逻辑的,通过大前提小前提等前提条件推出对应的结论,但是还是很多人是以经验主义为主的,根据以往的经验能够快速地对号入座,给出理解。但是这样的经验主义,如果遇到超出经验之外的,就很难推导出新的结论。就好比是一个神经网络,看上去训练很方便,但是要解释其运作的原理其实是很困难的,后面需要不断地调参,获取更好地效果的成本也很大。

所以,我们的应对之道非常重要的一点是建立大家公认的简单的基础概念。通过这些可以进一步进行演化推演,获得更高级的知识,从而从容应对复杂问题。

1.2 沉下心来

作者曾在书中不止一次地表达,好的模型需要不断演化。的确,我们实际解决的问题和固定的考试题目是不一样的。我们的要解决的业务场景问题是不断发展变化的,即使现在看起来完美的解决方案,可能过一个阶段就难以胜任了。

就好比射击运动员打移动的靶子一样,如果你无法预估靶的运动位置,那么就很难命中。我们要想很好地解决业务问题,需要看到业务发展的趋势,甚至业务所在整个大背景下的趋势。但是也不能过度设计,这样很可能靶还没有到那个位置,你就急于出手而错过了。

但是对整个业务发展把握得非常准确的,是少之又少的。根据一万小时定律,一个人要成为某个领域的专家,至少需要5年。有多少人能在一个领域坚持下来那?运动员也是需要无数个日夜刻苦地训练,才能成为一个神枪手。

所以,说了这些,深刻地感受到,领域驱动设计不是一个简单地事情,他是需要你不断坚持练习,并不断根据效果调整自己判断的长期训练过程。所以,软件复杂性应对之道还需要我们要有良好的心态,这是一个持续打靶的过程,每个阶段我们能很好地解决一部分问题就已经很庆幸了。

2 基础概念

“为什么努力了还是没有成功?当我们遇到这个问题时候,我们不得不去想一下:我们知道该怎么做好吗?如果只是凭着一腔热血,盲目地重复,那么你再努力,也没有实质性的变化。举个极端的例子,一个小学生不管如何努力,是解不了微积分这样的大学问题的。不是说他不是个好学生,是他根本没有基础知识的储备,没有这些基石,他只能去猜,这注定是无法解决问题的。

所以,领域驱动设计非常重要的一块是基础概念的介绍。接下来需要再体会理解一下。

2.1 构造块

构造块是指领域设计中最基本的一些元素。“不积跬步,无以至千里”,接下来看一下这些基础元素。

image.png

2.1.1 分层架构

分层是常用的方法,不仅仅是领域驱动设计,我们在学习计算机网络的时候也会看到分层的思想:网络的七层模型。分层的好处是显而易见的:可以让我们集中精力关注每一层的功能职责,更好地理解,更好地进行分工协作。

要注意的是,分层的基本原则是:层中的任何元素都仅依赖于本层的其他元素或其下层的元素。下层如果不得已要调用上层的话,需要采用合适的模式,如观察者模式或者回调机制等。

常见分层如下:

1)用户界面层(或表示层):展示信息等。如:web 服务的 Contoller。

2)应用层:协调领域对象。如:支撑 Contoller 的 Service 服务。

3)领域层(或模型层):表达业务概念、业务状态信息、业务规则。

4)基础设施层:为上面各层提供通用的技术能力。如:数据库操作等。

2.1.2 关联

我们的模型其实是真实世界模型的一个子集,我们不需要每个细节都需要反映出来,关联关系也是如此。

关联描述的是对象之间的联系,作者描述了3种减少关系复杂度的方法:

1)规定一个方向遍历。如:学校和校长是多对多的双向关系,但是如果可以考虑一个校长只在一个学校担任的职务的话,可以简化为学校到校长的1对多的关系。

2)添加一个限定符,以便有效的减少多重关联。如:在学校的某个特定时期,只有一位正校长。

3)消除不必要的关联。按需描述即可,less is more。

image.png

2.1.3 entity

实体 entity 的定义:主要由标识定义的对象被称做entity,特点是在整个生命中具有连续性。

简单理解为领域层中带有意义ID的对象,如:订单信息(含订单ID)。

2.1.4 value object

值对象 value object 的定义:用于描述领域的某个方面而本身没有概念标识的对象称为 value object。

简单理解为对象属性决定差异的个体,如:颜色RGB对象,只要rgb值一致,那么其实是一种颜色,并不关心对象的本身到底是哪个实例。

这类对象特点:

1)经常作为参数在对象之间传递消息,常常是临时对象。

2)设计选择:复制、共享或保持value object不变。

3)为性能优化提供了更多选择:可缓存、可共享等。

2.1.5 service

领域层中服务 service 的特征:

1)与领域概念相关的操作不是 entity 或 value object 的一个自然组成部分。

2)接口是根据领域模型的其他元素定义的。

3)操作是无状态的。

简单理解为领域层中一些需要协调多个对象的无状态函数。可以暴露领域中的一些功能。

值得注意的是:

1)service不只是在领域层使用,根据职责不同,service也会分到各层中。

2)可以控制领域层中接口的粒度。

2.1.6 aggregate

聚合 aggregate 是一组相关对象的组合,有一个根(root)和一个边界(boundary)。外部对象只能引用根,而边界之间的内部对象之间则可以相互引用。

image.png

2.1.7 factory

工厂 factory 负责复杂对象和aggreate的创建,是领域设计的一部分。

2.1.8 repository

存储库 repository 为那些需要直接访问的对象提供数据访问能力,封装底层存储逻辑。

2.1.9 specifiction

声明 specifiction 是为特殊目的创建了谓词形式的 VALUE OBJECT,是一个谓词,可以用来确定对象是否满足某些标准。

场景:某些业务规则不适合作为Entity 或 VALUE OBJECT 的职责。

2.2 柔性设计

当我们对领域模型有了深沉次的理解后,就能设计出更”软“的软件,下面介绍一下作者介绍的一些柔性模式。

柔性设计(supple design):运用深层模型所蕴含的潜力来开发出清晰、灵活且健壮的实现,并得到预期的效果。

image.png

2.2.1 intention-revealing interfaces

释意接口 intention-revealing interfaces:在命名类和操作时要描述它们的效果和目的。

2.2.2 side-effect-free function

无副作用函数 side-effect-free function:尽可能把程序的逻辑放到函数中,函数不产生明显的副作用,把命令(引起明显状态改变的方法)隔离出来。

2.2.3 assertion

断言 assertion:对程序某个时刻正确状态的声明,在编程中编写 assertion 或者单元测试。

2.2.4 conceptual contour

概念轮廓 conceptual contour:领域本身的一致性,把设计元素分解为内聚的单元。

2.2.5 standalone class

孤立的类 standalone class:无需引用任何其他对象(系统的基本类和基础库除外)就能够理解和测试的类。把无关对象提取到对象之外,保持低耦合。

2.2.6 closure of operation

闭合操作 closure of operation:闭合操作提供了一个高层接口,同时又不会引入对其他概念的任何依赖。在适当的情况下,可以定义操作的返回类型与参数类型相同。

2.3 战略设计

当我们的系统不断复杂起来,或需要和其它团队系统进行交互的时候,我们需要看到更高层次的策略。下面的描述的策略关注了系统之间交互的关系。

战略设计(strategic design):一种针对系统整体的建设和设计决策。

image.png

2.3.1 bounded context

限界上下文 bounded context:特定模型的界限应用。限界上下文使团队知道什么必须保持一致,什么必须独立开发。

2.3.2 continuous integration

持续集成 continuous integration:把工作足够频繁地合并到一起,并使它们保持一致。

2.3.3 context map

上下文图 context map :项目所涉及的界限上下文以及它们与模型之间的关系的一种表示。描述模型之间的联系点,明确所有通信需要的转换,并突出任何共享的内容

image.png

2.3.4 shared kernel

共享内核 shared kernel :通常是共享核心领域或者是一组通用子领域。

image.png

2.3.5 customer/supplier

客户/供应商关系 customer/supplier:上下游关系。不同客户需要协商来平衡,上游团队需要有自动测试套件。

image.png

2.3.6 conformist

跟随者模式 conformist:单方面跟随模式。上游的设计质量较好,容易兼容,可以采用严格遵循上游团队的模型。

image.png

2.3.7 anticorruption layer

防腐层 anticorruption layer:防腐层、隔离层,使用 facade or adapter 等模式。可以减少其它系统变动对本系统的影响。

image.png

2.3.8 separate way

各行其道 separate way:声明一个与其它上下文毫无关联的 bounded context,使开发人员能够在这个小范围内找到简单、专用的解决方案。

image.png

2.3.9 open host service

开放主机服务 open host service:开放子系统供其他系统访问。

image.png

2.3.10 published language

共享语言 published language:把一个良好文档化、能够表达领域信息的共享语言作为公共的通信媒介,必要时在其它信息与该语言之间进行转换。

2.4 精炼

人们常常感叹数学的优美:它可以通过非常简单的公式来推导并解释复杂的现象。这正是因为它总结得很精准,抓到了问题的本质。

我们在领域建模的时候也不可能面面俱到,这样就太过细节了,注定会陷入其中,缺少前进的动力。我们也需要抓住模型关键的部分。下面就介绍一下作者书中介绍的一些模式。

精炼(distillation):是把一堆混杂在一起的组件分开的过程,从中提取出最重要的内容,使得它更有价值。

image.png

2.4.1 core domain

核心领域 core domain:模型中最关键的部分,是程序的标志性部分,也是应用程序的核心诉求。

2.4.2 generic subdomain

通用子领域 generic subdomain:识别出对项目意图无关的内聚子领域,提取通用模型,并分离出来。

2.4.3 domain vision statement

领域愿景说明 domain vision statement:类似愿景说明文档,关注领域模型的本质,以及如何为企业带来价值。

2.4.4 highlight core

突出核心 highlight core:编写简洁文档描述 core domain 以及核心元素之间的主要交互过程,并把 core domain 标记出来。

2.4.5 cohesive mechanism

内聚机制 cohesive mechanism:把内聚的部分分离到一个单独的轻量级框架汇中, 并用释意接口暴露框架功能,从而使得领域元素关注做什么怎么做转移给了框架。

2.4.6 segregated core

分离的核心 segregated core:把核心概念从支持性元素中分离出来,并增强核心的内聚性。把通用元素、支持性元素提取到其它对象中。

2.4.7 abstract core

抽象核心 abstract core:识别模型中最基本的概念(能表达出主要组件的大部分交互),并分离到抽象模型中(类、抽象类或接口)。抽象模型放在自己的模块中,实现类留在子领域定义的模块中。可以减少模块间依赖的复杂性。

image.png

2.5 大型结构

欲穷千里目,更上一层楼,当我们面对复杂的大型系统时,我们需要站在更高层次去思考解决方案,下面介绍一下作者对大型结构的理解。

大型结构(large-scale structure):一组高层的概念、规则,它为整个系统建立了一种设计模式,能够从更大的角度来讨论和理解系统。

image.png

2.5.1 evolving order

演变有序 evolving order:让概念上的大型结构随着应用程序一起演变,甚至可以变成一种完全不同的结构风格。

image.png

2.5.2 system metaphor

系统隐喻 system metaphor:当系统的一个具体类比正好符合团队成员对系统的想象,并且能够引导他们向着一个有用的方向进行思考时,就应该把这个类比用作一种大型结构。例如:用真实大楼的防火墙来类比网络的防火墙。

image.png

2.5.3 responsibility layer

职责分层 responsibility layer:在具有自然层次的模型中,可以围绕主要职责进行概念上的分层,这样可以结合使用”分层“和”职责驱动的设计“这两个有力的原则。

image.png

2.5.4 knowledge level

知识级别 knowledge level:是一组描述了另一组对象应该有哪些行为的对象。当我们需要让用户对模型的一部分有所控制,而模型又必须满足更大的一组规则情况时,可以利用这个模式来处理。可以通过类型的知识,去选择不同的策略。

image.png

2.5.5 pluggable component framework

可插入式组件框架 pluggable component framework:从接口和交互中提炼出一个抽象核心 abstract core,并创建一个框架,这个框架要允许这些接口的各种不同实现被自由替换。


3 后续

本文对书中重要的概念进行了一下摘录并加入简单的理解,同时参考作者的思维导航图绘制了几个大图建立思维索引,方便后续实践时检索。

虽然有了这些基础概念,但其实本人对如何进行领域驱动设计还是比较模糊的,发现很难结合到日常的工作中。这个一方面可能是我们的场景还没有那么复杂,毕竟模型是对真实的世界建模;另一方面,在阅读的过程中也发现了一些高频名词“贫血模型”、“富血模型”。

它们的含义是:

1)贫血模型:领域对象里只有get和set方法(POJO),所有的业务逻辑都不包含在内而是放在应用层。

2)富血模型大多业务逻辑和持久化放在领域对象里面,应用层只是简单封装部分业务逻辑以及控制事务、权限等。

            image.png

作为业务部门,一般我们日常的开发,基本上以贫血模型为主,快速迭代上线,领域对象基本都退化成了数据对象。当然不是说一定要用“富血模型”,毕竟合理的领域划分还是很有挑战的,没有深刻的理解反而弄巧成拙,徒增复杂度。

但是我们还是需要大量的实践感悟。一方面可以去关注领域设计的机会,另一方面也可以阅读别人的一些实践文章。

相关文章
|
测试技术 数据库 安全
带你读《C++代码整洁之道:C++17 可持续软件开发模式实践》之二:构建安全体系
如果想用C++语言编写出易维护的、扩展性良好的以及生命力强的软件,那么,对于所有的软件开发人员、软件设计人员、对现代C++代码感兴趣或想降低开发成本的项目领导者来说,本书都是必需品。如果你想自学编写整洁的C++代码,那么本书也是你需要的。本书旨在通过一些示例帮助各个技术层次的开发人员编写出易懂的、灵活的、可维护的和高效的C++代码。即使你是一名资深的开发工程师,在本书中也可以找到有价值的知识点。
|
6月前
|
存储 设计模式 架构师
编码之道:从技术细节到系统架构的升华
【5月更文挑战第9天】 在编程的世界里,每一行代码都承载着功能与美学的双重使命。本文将探讨如何从关注技术细节出发,逐步深化对系统架构的理解,并在实践中实现从代码编写者到系统设计师的转变。通过分析具体案例,我们将揭示那些看似平凡的技术感悟如何在复杂系统的构建中发挥关键作用,以及这一过程中对软件开发者的启示。
74 3
|
设计模式 消息中间件 架构师
如何成为更好的软件架构师?
如何成为更好的软件架构师?
|
敏捷开发 架构师 项目管理
架构师才能看懂的大型网站架构面临的挑战:业务架构的基本思路
业务架构的基本思路 大型网站系统有很多功能,一次性明确所有的功能需求并设计出一个庞大的业务架构是一件费力不讨好的事情。因为在项目前期,难免会忽视一些琐碎功能,而随着开发的进行,也会有很多新的想法产生,基本上不会存在完全按照最初的业务架构设计完成的软件产品。因此,业务架构不仅要做到“规整功能模块,厘清产品业务逻辑”,更重要的是如何做到“有规划性地应对项目过程中的需求变更”。
|
数据可视化 架构师 前端开发
复杂性应对之道 - 领域建模
复杂性应对之道 - 领域建模
复杂性应对之道 - 领域建模
|
C++ 测试技术
带你读《C++代码整洁之道:C++17 可持续软件开发模式实践》之三:原则
如果想用C++语言编写出易维护的、扩展性良好的以及生命力强的软件,那么,对于所有的软件开发人员、软件设计人员、对现代C++代码感兴趣或想降低开发成本的项目领导者来说,本书都是必需品。如果你想自学编写整洁的C++代码,那么本书也是你需要的。本书旨在通过一些示例帮助各个技术层次的开发人员编写出易懂的、灵活的、可维护的和高效的C++代码。即使你是一名资深的开发工程师,在本书中也可以找到有价值的知识点。
|
Java
复杂性应对之道——抽象
写本文的原因是,抽象是软件设计中最重要的概念。但抽象这个概念本身又很抽象,我们有必要花一些时间深入理解抽象、抽象的层次性,以及不遗余力的不断提升我们抽象能力。
2835 0
|
关系型数据库
企业应用架构实践(复杂性应对之道)
#前言 从业这么多年,接触过银行的应用,Apple的应用,eBay的应用和现在阿里的应用,虽然分属于不同的公司,使用了不同的架构,但有一个共同点就是都很复杂。导致复杂性的原因有很多,如果从架构的层面看,主要有两点,一个是架构设计过于复杂,层次太多能把人绕晕。另一个是根本就没架构,ServiceImpl作为上帝类包揽一切,一杆捅到DAO(就简单场景而言,这种[Transaction Script]
10112 2
|
程序员 领域建模
《领域驱动设计:软件核心复杂性应对之道(修订版)》—第1章 1.2节知识消化
金融分析师要消化理解的内容是数字。他们筛选大量的详细数字,对其进行组合和重组以便寻求潜在的意义,查找可以产生重要影响的简单表示方式——一种可用作金融决策基础的理解。
1437 0
下一篇
无影云桌面