领域驱动设计


读《领域驱动设计——软件核心复杂性应对之道》一书的一些读书笔记。本书介绍了很多复杂领域设计的框架和理念,着重从项目架构师角度考虑如何设计一个大型项目,建立模型。目前来说,这本书中有很多东西我并不十分理解,或许缺乏经验导致无法深刻体会其价值。先做一些简单的笔记,以后慢慢体会。

一、运用领域模型

领域驱动,即模型驱动(概念类似Spring MVC中的M)。通过模型与领域专家沟通。

1、 建立一种基于模型的语言

用模型为领域专家和开发人员提供一种相互交流的语言,避免无法理解对方的术语
体现在画图、讲话、写东西、代码命名各方面

运用图帮助沟通:UML图,类图和图像交互图为主
避免 包罗万象的对象模型图 和 包含所有细节的图
图只是体现思想纲要,设计细节由代码体现

2、绑定模型和实现

模型驱动设计。不能将建模和编程完全分离。每个开发人员必须不同程度参与模型讨论并与领域专家保持联系。

二、模型驱动设计的构造块

提炼一组基本的构造块,团队一致使用这些标准模式可以使设计井然有序。
分层:用户界面层;应用层;领域层;基础设施层(与MVC理论差不多)

1、软件中表示的模型

包含Entity、Value Object、Service
对象之间的关联:简化;添加约束
Entity(Reference Object):主要由标识定义的对象,具有生命周期,这期间它们的形式和内容可能改变,但要保持一种内在连续性
标识:唯一性,某些数据属性或属性组合可以确保唯一性 / 生成ID
Value Object:没有标识、不可变(共享安全)
具体值对象和引用对象区别可以参考博客:添加链接描述
Service:一些操作,不属于任何对象
Module(Package):Module之间低耦合,内部高内聚
有时候分层会导致内聚性变差
对象范式模型驱动设计的核心

2、领域对象的生命周期

这一部分内容其实不太理解,提出了三种模式,最后给了一个示例。
感觉Aggregate就是为了将项目中的对象划分成几块,每块有个根,再考虑对哪些根建Repository。
不是很清楚如何应用,不知道是否因为现在很多都有了方便的架构,比如与数据库交互用spring中@Repository声明下DAO组件。也可能没有理解其精髓,以后再看。

Aggregate 聚合
定义清晰的所属关系和边界,避免混乱、错综复杂的对象关系网
将Entity和Value Object分门别类,聚集到Aggregate中,并定义每个Aggregate的边界。
在每个Aggregate中,选择一个Entity作为根,并通过根来控制对边界中其他对象的所有访问(只有根才可能有Repository)。

Factory 工厂
创建工作复杂,或暴露过多内部结构,用工厂封装

Repository 存储库
通过一个全局接口提供访问,提供查找和检索持久化对象,包括添加删除对象的方法,将实际存储和查询技术封装。
@Repository,spring中的注解,用于标注数据访问组件,即DAO组件

三、重构

1、将隐式概念转变为显式概念

通过与领域专家沟通,找到隐含的某个概念(一些能简洁表达复杂概念的术语 / 纠正你的用词),在模型中加入一个或多个对象或关系,将次概念显式表达。
显式的约束
单独的约束方法,甚至加入一个新类来反应约束
将过程建模为领域对象
service是显式表达过程的一种方式,同时会将复杂的算法封装。如果过程的执行有多种方式,用Strategy策略模式。
Specification规格模式
Specification就是一个谓词,判断对象是否满足某些标准
谓词是指计算结果为true或false的函数,可以用and、or、not操作符连接。
也可以通过直接在基础结构层中生成SQL,就不用这个模式了。其实也没大明白这模式有啥精妙的。
添加链接描述

2、柔性设计

使项目能够随着开发工作加速前进,而不会由于它自己的老化停滞不前,设计要易于修改。

Intention-Revealing Interfaces 表明意图的接口

类和操作好的命名,描述效果和目的,不要表露它们是通过何种方式达到目的的。
创建一个行为之前先为它编写一个测试

Side-Effect-Free Function 不产生副作用的函数

副作用:任何对未来操作产生影响的系统状态改变

  • 把命令和查询放在不同的操作中,确保导致状态改变的方法不返回领域数据,并尽量简单。
  • 采用一种替代的模型和设计,不修改现有对象,而是创建并返回一个Value Object(不可变)。

Assertion 断言

把副作用明确的表示出来,使更易于处理。通过给出类和方法的断言,使开发人员知道肯定会发生的结果。理清固定规则、前置条件、后置条件。如果不允许直接写Assertion,可以将它们编写成自动的测试单元,还可以写到文档或图中。

Conceptual Contour 概念轮廓

功能的分解,寻找在概念上有意义的功能单元,方便使用和组合。
把设计元素(操作、接口、类、Aggregate)分解为内聚的单元,使模型预领域中那些一致的方面相匹配。

Standalone Class 独立类

低耦合,把其他所有无关概念提取到对象之外。

Closure of Operation闭合操作

定义操作时让它的返回类型与其参数的类型相同。如果是实现者的状态在计算中会用到,实现者也应有相同的类型。闭合操作引入了一个高层皆苦,同时又不会引入对其他概念的依赖。(没觉着特别有用)

声明式设计

把程序或程序的一部分写成一种可执行的规格。声明式编程和面向对象编程一样是一种编程风格。提到这个的很少,暂时没太搞明白。
添加链接描述

3、应用分析模式和设计模式

分析模式:一种概念集合,用来表示业务建模中常见结构。

四、战略设计

针对企业大规模系统的设计,三条原则:上下文、提炼、大型结构。

1、保持模型的完整性

大型系统领域模型的完全统一是不可行的。难以协调、过于复杂。

Bounded Context 限界上下文

明确定义模型所应用的上下文,设置模型边界,边界中严格保证模型一致性,不受边界外问题的干扰。
识别不一致:最明显的是接口不匹配
不同模型元素组合到一起引发两类问题:

  • 重复的概念:每当这个概念信息变化,必须更新两个地方。
  • 假同源:使用相同术语,但其实概念有差别。
    出现这类问题,可能需要将模型重新整合为一体,并加强预防模型分裂。

Continuous Integration 持续集成

很多人在同一个Bounded Context工作,模型很容易分裂。但如果将系统分解为更小的context,又难以保持集成度和一致性。持续集成可以维护单一模型的完整性。

  • 分步集成,频繁集成,采用可重现的合并技术
  • 自动化测试快速查明模型分裂问题
  • 坚持使用Ubiquitous Language,达成共识

Context Map 上下文图

定义不同上下文之间关系,并在项目中创建一个所有模型上下文的全局视图,减少混乱。

  • 识别项目中起作用的每个模型,并定义其Bounded Context,为每个Bounded Context命名,并将名字添加到Ubiquitous Language。
  • 描述模型之间的联系点,明确所有通信需要的转换,并突出任何共享的内容。

Bounded Context之间的关系

Shared Kernel 共享内核

不同团队开发一些紧密相关的程序。一开始就新选出两个团队都同意共享的一个子集,包括模型、代码、数据库,明确共享内容地位特殊不可擅自更改。

Customer/Supplier Development Team 客户/供应商

一个子系统主要服务于另一个子系统,上游服务下游

  • 下游团队是客户,需求很重要
  • 共同开发自动化验收测试,验证预期接口

若上游不满足下游,要么完全放弃对上游使用,若必须保持依赖,两种选择。
上游设计很好,使用跟随者模式;上游设计很差,开发转换层。

Conformist 跟随者模式

严格遵从上游团队的模型。消除在Bounded Context质安监转换的复杂性。

Anticorruption Layer 防护层

创建一个隔离层,内部在两个模型见进行必要双向转换,这个层通过另一个系统现有接口与其对话,只需对那个系统作出很少修改。
利用Facade(外观)和Adapter(适配器)模式

Separate Way 各行其道模式

严格划定需求范围,如果两组功能之间关系不是必不可少,则应使他们完全独立。

Open Host Service 开放主机服务

定义一个协议,把你的子系统作为一组Service供其他系统访问。有新需求则扩展这个协议。

Published Language 公共语言

把一个良好文档化、能够表达所属领域信息的共享语言作为公共的通信媒介,必要时在其他信息与该语言间转换。
XML(可扩展标记语言),通过DTD(文档类型定义)或XML模式正式定义一个专用的领域语言。

转换

合并Context:Separate Way -->Shared Kernel–>Continuous Integration

2、精炼

Core Domain:分出优先级,找到真正核心,提炼模型,将核心域与其他辅助作用的模型和代码分开。
Generic Subdomain:识别与项目意图无关的内聚子领域,放到单独module,考虑找现成的解决方案或公开发布的模型。
注意通用子领域不应引入行业专业的模型元素,行业专用的概念要么属于core domain,要么属于它们自己更专业的子领域。
Domain Vision Statement(领域愿景声明):简短描述Core Domain及它将创造的价值。不能将你的领域模型和其他领域模型区分开来的方面就不用写了。尽早写出来,作为指导,随着新的理解再随时修改。
Highlighted Core(突出核心):编写一个简短的文档(3-7页),描述Core Domain和Core元素之间的主要交互过程。
Core Domain需要很容易分辨,标记出来。如果精炼文档概括了Core Domain的核心元素,就可以指示模型改变的重要程度。模型或代码修改涉及精炼文档,则需要与团队其他成员协商和通知。
Cohesive Mechanism(内聚机制):把解决问题的算法封装,用一个接口暴露功能。领域中其他元素专注于表达问题(做什么)。
和Generic Subdomain动机相同,为Core Domain减负
Segregated Core(分离核心):模型中元素可能一部分属于Core Domain,一部分起支持作用,应将核心概念从支持性元素中分离出来,增强Core的内聚性。把所有通用元素或支持性元素提取到其他对象中,并把这些对象放到其他的包中,即使把一些紧密耦合的元素分开也值得。
Abstract Core简化不同module的子领域之间的大量交互。设计抽象模型,表达出重要组件之间的大部分交互。专用的详细的实现类留在子领域定义的Module中。

3、大型结构

大型结构随着应用程序一起演变,不要过分限制详细的设计和模型决策,应在掌握了详细知识之后才能确定。
运用隐喻帮助理解系统。
划分职责层:潜能层、作业层、决策支持层、策略层、承诺层……
可插入式组件框架:从接口和交互中提炼一个Abstract Core,并创建一个框架,允许这些接口的各种不同实现自由替换。同样,不论是什么应用程序,只要严格地通过Abstract Core接口进行操作,就可以允许它使用这些组件。

不要滥用框架和死板的实现大比例结构。大比例结构最重要的作用是具有概念上的一致性,并帮助我们更深入理解领域。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值