当前位置: 首页 > news >正文

我眼中的领域驱动设计

说软件业务越来越复杂了,领域驱动设计可以让事情变得简单。而实际情况是:领域驱动设计的门槛很高,没有很深厚的面向对象编码能力几乎不可能实践成功。

这一说法是否自相矛盾呢?Martin Fowler在PoEAA一书中给了一个有力的解释:

我们把三层架构等除了领域驱动之外的架构方式都可以归纳为以数据为中心的架构方式,在图中是黑色的粗实线;

领域驱动设计在图中是绿色的粗实线。

  • 当软件在开发初期,以数据驱动的架构方式非常容易上手,但是随着业务的增长和项目的推进,软件开发和维护难度急剧升高。
  • 领域驱动设计则在项目初期就处在一个比较难以上手的位置,但是随着业务的增长和项目的推进,软件开发和维护难度平滑上升。

这幅图形象的解释了领域驱动设计和传统的软件架构模式两者在软件开发过程中解决复杂性之间的差异。

领域驱动设计的核心是什么?

顾名思义,领域驱动设计的核心是领域模型,这一方法论可以通俗的理解为先找到业务中的领域模型,以领域模型为中心驱动项目的开发。而领域模型的设计精髓在于面向对象分析,在于对事物的抽象能力,一个领域驱动架构师必然是一个面向对象分析的大师。

在面向对象编程中讲究封装,讲究设计低耦合,高内聚的类。而对于一个软件工程来讲,仅仅只靠类的设计是不够的,我们需要把紧密联系在一起的业务设计为一个领域模型,让领域模型内部隐藏一些细节,这样一来领域模型和领域模型之间的关系就会变得简单。这一思想有效的降低了复杂的业务之间千丝万缕的耦合关系。

下图为“以数据为中心的架构模式”,表和表之间关系错综复杂:

下图是“领域模型”:领域和领域之间只存在大粒度的接口和交互:

初期学习DDD的朋友一定不会错过Eric Evans写的《领域驱动设计:软件核心复杂性应对之道》,这本书名气很大,也是很多人入门领域驱动设计的首选读物,这本书提到了领域驱动设计中的一些概念:Repository,Domain,ValueObject等。但是初学者有可能得出一个错误的结论:有人误认为项目架构中加入***Repository,***Domain,***ValueObject就变成了DDD架构。如果没有悟出其精髓就在项目中加入这些概念,那充其量也不过是个三层架构;反之对于一个面向对象分析的高手而言,不使用这些概念也可以实现领域驱动设计。

以IUserRepository这样一个接口定义为例:

1

2

3

4

5

6

7

publicinterfaceIUserRepository : IRepository<User>

{

//What's this?

List<Rule> GetRules(int id);

//....

}

一个IUserRepository是一个Repository,他只能以User聚合根为单位进行操作。方法List<Rule> GetRules(int id)将此Repository打回了原形,这不再是一个Repository,这是一个DAL。

正确的实现方式:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

publicclassUser:AggregateRoot

{

privateList<Rule> GetRules()

{

returnnull;

}

publicvoid ApproveRequest(Request request)

{

varrules = user.GetRules();

//......

//如果有权限就批准

}

}

这段代码体现了User作为一个领域模型,他拥有自己的职责和能力。

如何开始实践领域驱动设计?

正如本文通篇所说,领域驱动设计讲究的是领域模型的分析和对事物的抽象,从来没有提起过数据如何存取这个话题,言下之意在领域驱动设计中,我们不关心过数据如何存取,怎么样写linq效率高,使用懒加载还是include,这些实现细节会将你带入传统的三层架构模式中。

在领域驱动设计中要先设计领域模型,接着写Domain逻辑,至于数据库,仅仅是用来存储数据的工具。使用database first那不叫领域驱动设计,很明显你先设计的表结构,所以应该叫数据库驱动设计更为准确。更不要引入数据库独有的技术,例如触发器,存储过程等。数据库除了存储数据外,其余一切逻辑都是Domain逻辑。

我们不妨以大家都比较熟悉的医院门诊看病流程举个例子,看看如何开始实践领域驱动设计:

我们暂且认为一个门诊看病流程就是一个完整的领域模型,此时你要忘掉数据库,不要再想表结构如何设计,而是就这一领域模型进行抽象:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

publicclassOutPatientProcess:AggregateRoot

{

publicRegistration _registration { get;privateset; }//挂号单

privateList<Examination> _examinations;

publicIReadOnlyList<Examination> Examinations => _examinations.AsReadOnly();//化验单

publicPrescription Prescription { get;privateset; }//处方

publicDateTime ConsultaionTime { get;privateset; }//接诊时间

publicDoctor Doctor { get;privateset; }//接诊医师

//开始一个门诊治疗过程

publicvoid StartProcess(Registration registration)

{

_registration = registration;

InquireSymptoms();

WriteOutExamination();

WritePrescription();

}

//询问病人病情

publicvoid InquireSymptoms()

{

}

//开立化验单

privatevoid WriteOutExamination()

{

_examinations.Add(newExamination());

}

//填写处方

privatevoid WritePrescription()

{

}

}

我们暂且不讨论这一模型是否符合真实场景,但是这个例子带你迈入了领域驱动设计的第一步,同时这个例子也向你展示了软件开发可以不用先设计数据库。当你写好所有的Domain逻辑后再考虑把这个类持久化在数据库中就好了。在我眼中,数据库仅仅是一个保存数据的东西,不要把他过早的耦合在代码中。这一强调了很多遍的观点影响着你能否成功实践DDD。

CQRS架构展望

话虽这样说,但是既然你在使用关系数据库,有人就会免不了跟你提起性能怎么优化这样的话题。这也是传统ORM+关系数据库实现领域驱动设计的硬伤,特别是当你的领域模型Scope设计过大,意味着Repository中的操作每次都要关联一堆表出来,特别是有人设计数据喜欢遵守第N范式这种基本就没辙了(没有贬低遵守这些范式的意思,只是这样设计的数据库+ORM会产生较多关联,相对应的设计为表结构冗余设计,有利于ORM提升性能),不得不说到了最后由于数据库的存储性能问题,我们又一次将数据库纳入到了考虑范围。

解决这一问题的方案是CQRS架构, Query端各种缓存和Nosql,顺便把搜索引擎也用上,让你的软件飞奔起来。这一架构解耦了数据库操作,你基本没有机会跟数据库打交道并且还解决了数据存储的性能问题。

这一进化过程也解开了一些人的疑虑,为什么从刚开始写代码就开始学习各种设计模式,但是从来没有机会使用过?因为你所写的代码无时无刻不耦合着数据库这一“毒瘤”,而数据库操作作为一种实现细节掺杂在你的代码中,所以领域驱动设计为此而生,你准备好了吗?

http://www.jsqmd.com/news/1112383/

相关文章:

  • 00668,湘江新区的“尖子生”交卷了!
  • Verilog FFT 设计
  • Adobe-GenP 3.0:基于AutoIt的Adobe CC授权验证绕过技术实现
  • 计算机毕业设计之jsp-驾校预约管理系统
  • 鸿升光HSGQ PON全光网络-三网融合解决方案
  • Codex封装Skill三步法:从一次性对话到可复用自动化工作流
  • 企业仓储数字化如何落地?不同规模仓库WMS仓储系统举例
  • 选对取代度提升包封率!近红外羧基染料 DiR-COOH 全解析
  • AI系统部署后组织效能下降问题剖析:单一工具引入无法驱动业务增长的底层架构原因
  • 电容式触控感应原理,Q-Touch:针对不同的覆盖层厚度或 PCB 布局微调灵敏度 ,快速构建项目
  • 革命性魔兽世界宏引擎:GSE如何重新定义技能自动化
  • 5步掌握Path of Building PoE2:免费开源的角色构建终极解决方案
  • 【系统维护】C盘爆满解决方案:Wise Disk Cleaner 绿色版实操指南
  • 工业级航班延误预测系统:XGBoost端到端落地实践
  • Java计算机毕设之基于 SpringBoot 的中小学优质教学资源推送服务系统的设计与实现 智慧教育背景下中小学教学资源运维系统(完整前后端代码+说明文档+LW,调试定制等)
  • 【信道估计】基于太赫兹集成UM-MIMO和IRS系统的混合球面与平面波信道建模与估计Matlab仿真
  • Win7系统上安装Python教程:轻松上手3.8.6版本
  • 密码学博客:AES-CBC 比特翻转(Bit Flipping)攻击原理、实战与防御
  • 新能源构网型光伏PV+储能SOC+虚拟同步机(VSG)并网逆变器仿真(仿真+参考文献)
  • Android SSL证书固定实战:原理、方案与避坑指南
  • 【刷题日记】LeetCode 21. 合并两个有序列表
  • 医疗电子PCB设计指南:中频理疗仪电路板关键技术
  • 首个threejs项目-前端填坑指南
  • 永劫无间5周年新版本来袭!手机远程对战,周年内容不错过!
  • 专科生必学:8款AI工具提升学习效率
  • YOLOv10模型改进-Backbone改进-第56篇:YOLOv10改进策略【Backbone】| ConvNeXt Backbone替换
  • Zemax界面和功能介绍(三)
  • 数据的存储与运算
  • 2026年暑假学习规划App横评:哪家技术更强?
  • HarmonyOS 模板市场实战:64 款内置卡片、分类补齐与搜索过滤