我眼中的领域驱动设计(转)

原文地址:http://www.cnblogs.com/richieyang/p/5373250.html

有幸参与了一些领域驱动的项目,读了一些文章,也见识了一些不伦不类的架构,感觉对领域 驱动有了更进一步的认识。所以今天跟大伙探讨一下领域驱动设计,同时也对一些想要实践领域驱动设计却又无处下手,或者一些正在实践却又说不上领域驱动设计 到底好在哪的朋友一些指引方向。当然对于”领域驱动设计”这个主题而言从来不乏争论,所以大家可以在畅所欲言。

为什么要使用领域驱动设计?

从Eric Evans的《领域驱动设计:软件核心复杂性应对之道》一书的书名就可以看出这一方法论是为了解决软件核心复杂性的。也就是说软件业务越来越复杂了,领域驱动设计可以让事情变得简单。而实际情况是:领域驱动设计的门槛很高,没有很深厚的面向对象编码能力几乎不可能实践成功。

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

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

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

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

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

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

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

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

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

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

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

以Repository的设计为例,我经常看到一些文章中对IRepository定义为:


1

2

3

4

5

6

7

8

public interface IRepository<TAggregateRoot>

{

    TAggregateRoot Get(int id);

    void Remove(TAggregateRoot aggregateRoot);

    void Update(TAggregateRoot aggregateRoot);

    //What‘s this?

    TAggregateRoot Where(Expression<Func<TAggregateRoot, bool>> filter);


1

2

    //…

}

领域驱动设计是以领域模型为基本单位,这表明在IRepository<TAggregate>接口中,只有Get(int id),Update(TAggregateRoot aggregate),Remove(TAggregateRoot aggregate)这三个接口是有意义的。 Where(Expression<Func<TAggregateRoot,bool>> filter)这一定义暴露了你是在在进行单表操作,对于领域模型来讲没有查询一说。

而对于IUserRepository这样一个稍微具体的接口定义:


1

2

3

4

5

6

7

public interface IUserRepository : 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

public class User:AggregateRoot

{

    private List<Rule> GetRules()

    {

        return null;

    }

    

    public void ApproveRequest(Request request)

    {

        var rules = 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

public class OutPatientProcess:AggregateRoot

{

    public Registration _registration { get; private set; }//挂号单

    private List<Examination> _examinations;

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

    public Prescription Prescription { get; private set; }//处方

    public DateTime ConsultaionTime { get; private set; }//接诊时间

    public Doctor Doctor { get; private set; }//接诊医师

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

    public void StartProcess(Registration registration)

    {

        _registration = registration;

        InquireSymptoms();

        WriteOutExamination();

        WritePrescription();

    }

    //询问病人病情

    public void InquireSymptoms()

    {

       

    }

    //开立化验单

    private void WriteOutExamination()

    {

        _examinations.Add(new Examination());

    }

    //填写处方

    private void WritePrescription()

    {

        

    }

}

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

CQRS架构展望

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

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

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

分类: .NET

好文要顶 关注我 收藏该文

richiezhang
关注 - 20
粉丝 - 399

荣誉:推荐博客

+加关注

时间: 2024-10-13 16:08:37

我眼中的领域驱动设计(转)的相关文章

我眼中的领域驱动设计

有幸参与了一些领域驱动的项目,读了一些文章,也见识了一些不伦不类的架构,感觉对领域驱动有了更进一步的认识.所以今天跟大伙探讨一下领域驱动设计,同时也对一些想要实践领域驱动设计却又无处下手,或者一些正在实践却又说不上领域驱动设计到底好在哪的朋友一些建议.当然对于领域驱动设计这个主题而言从来不乏争论,所以大家可以在畅所欲言. 为什么要使用领域驱动设计? 从Eric Evans写的<领域驱动设计:软件核心复杂性应对之道>一书的书名就可以看出这一方法论是为了解决软件核心复杂性的.也就是说软件业务越来越

领域驱动设计之单元测试最佳实践(二)

领域驱动设计之单元测试最佳实践(一) 介绍完了DDD案例,我们终于可以进入主题了,本方案的测试代码基于Xunit编写,断言组件采用了FluentAssertions,类似的组件还有Shouldly.另外本案例使用了Code Contracts for .NET,如果不安装此插件,可能有个别测试不能正确Pass. 为了实现目标中的第二点:"尽量不Mock,包括数据库读取部分”,我尝试过3种方案: 1.测试代码连接真实数据库,只需要将测试数据库配置到测试项目中的web.config中,即可达到这一目

领域驱动设计战略原则

之前发在别的地方了,据说标题不好,改个标题... 去年就打算总结一下,结果新换的工作特别忙,就迟迟没有认真动手.主要内容是很多初学DDD甚至于学习很长时间的同学没有弄明白DDD是什么,适合什么情况.这世界上没有银弹,抛开了适合的场景孤立的去研究DDD,在学习过程中还可以,但是应用到实际项目时就会遇到各种坑,到头来各种妥协,我看到很多同学遇到这种情况,最后怪DDD,说DDD不实用云云.另有一些都没细了解过就抨击反对的,事实上,要想否定个东西,你总要了解了才有发言权. 一.误区 综合起来主要有一下几

领域驱动设计-入门

领域驱动设计围绕着对象进行设计,类似于传统的OO,但是还是不同的. 传统的OO更像是贫血的领域对象,它具有数据,很多get set方法,但是缺少业务逻辑.客户端使用时,需要进行一大串的set操作.举个栗子: 这种方式中customer是一个贫血的领域对象,客户端必须进行很多的set,最后调用dao进行保存. --------------- 那么,怎样才是不贫血的领域对象呢?主要看两点:boundedcontext,通用领域语言. 领域对象是通过通用领域语言进行描述的,通用语言是领域专家和开发人员

我的“第一次”,就这样没了:DDD(领域驱动设计)理论结合实践

写在前面 插一句:本人超爱落网-<平凡的世界>这一期,分享给大家. 阅读目录: 关于DDD 前期分析 框架搭建 代码实现 开源-发布 后记 第一次听你,清风吹送,田野短笛:第一次看你,半弯新湖,鱼跃翠堤:第一次念你,燕飞巢冷,释怀记忆:第一次梦你,云翔海岛,轮渡迤逦:第一次认你,怨江别续,草桥知己:第一次怕你,命悬一线,遗憾禁忌:第一次悟你,千年菩提,生死一起. 人生有很多的第一次:小时候第一次牙牙学语.第一次学蹒跚学步...长大后第一次上课.第一次逃课.第一次骑自行车.第一次懂事.第一次和喜

DDD领域驱动设计基本理论知识总结

领域驱动设计之领域模型 加一个导航,关于如何设计聚合的详细思考,见这篇文章. 2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity in the Heart of Software (领域驱动设计),简称Evans DDD.领域驱动设计分为两个阶段: 以一种领域专家.设计人员.开发人员都能理解的通用语言作为相互交流的工具,在交流的过程中发现领域概念,然后将这些概念设计成一个领域模型:由领域模型驱动软件设计,用代码来实现该领域模型:

(转载)浅谈我对DDD领域驱动设计的理解

原文地址:http://www.cnblogs.com/netfocus/p/5548025.html 从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品.所以,自然而然就想到要做一个普通电商系统,用于实现在线销售自己企业产品的目的. 再比如,我是一家互联网公司,公司有很多系统对外提供服务,面向很多客户端设备.但是最近由于各种原因,导致服务经常出故

WCF客户端配置以及代理-----基于DDD领域驱动设计的WCF+EF+WPF分层框架(4)

写在最前面:转载请注明出处 目录置顶: 关于项目--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(1) 架构搭建--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(2) WCF服务端具体实现---------基于DDD领域驱动设计的WCF+EF+WPF分层框架(3) WCF客户端配置以及代理-----基于DDD领域驱动设计的WCF+EF+WPF分层框架(4) Domain具体实现------------基于DD

什么是领域驱动设计(Domain Driven Design)?

本文是从 What is Domain Driven Design? 这篇文章翻译而来. ”…在很多领域,专家的作用体现在他们的专业知识上而不是智力上.“ -- Don Reinertsen 领域驱动设计(Domain Driven Design)是一种软件开发方法,目的是让软件系统在实现时准确的基于对真实业务过程的建模并根据真实业务过程的调整而调整. 传统的开发工作趋向于一种以技术为先导的过程,需求从业务方传递到开发团队,开发人员依据需求上的描述创造出最有可能的假想. 在瀑布开发过程中,这导致