DDD的思考

时间:2023-03-09 08:39:01
DDD的思考

概述

DDD领域驱动设计,它是对面向对象的的分析和设计(OOAD,Object Orient Analysis Design)的一个补充,对技术框架进行了分层规划,同时对每个类进行了策略和类型划分。领域模型是领域驱动的核心 ,采用DDD的设计思想,业务逻辑不再集中在几个大型的类上,而是在大量相对小的领域对象上,这些类具有自己的状态和行为,每个类都是完成的独立的,并与现实领域的业务对象形成一种映射。基于DDD的架构设计,保证了系统的可维护性,扩展性和敏捷性,在处理复杂业务逻辑方面有着明显的优势!

在DDD设计中,系统分为UserInterface->Application->Domain->Infrastructure,每一层都独立的职责,UserInterFace也就是所谓的UI层,负责页面展示;Application层是很薄的一层,对外为展现层提供各种应用功能(包括查询或命令),对内调用领域层(领域对象或领域服务)完成各种业务逻辑,应用层不包含业务逻辑。Domain是领域层,负责主要的业务逻辑,这一层是业务的核心。Infrastructure是基础设施层,主要为其他层提供通用的技术支持,为领域层实现持久化机制;

领域层是业务的核心层,有几个概念需要阐述下;

  • 实体(Entity)

    实体就是领域中需要唯一标识的领域概念。因为我们有时需要区分是哪个实体。有两个实体,如果唯一标识不一样,那么即便实体的其他所有属性都一样,我们也认为他们两个不同的实体;因为实体有生命周期,实体从被创建后可能会被持久化到数据库,然后某个时候又会被取出来。所以,如果我们不为实体定义一种可以唯一区分的标识,那我们就无法区分到底是这个实体还是哪个实体。另外,不应该给实体定义太多的属性或行为,而应该寻找关联,发现其他一些实体或值对象,将属性或行为转移到其他关联的实体或值对象上。
  • 值对象(Value Object)

    实在领域中,并不是没一个事物都必须有一个唯一标识,也就是说我们不关心对象是哪个,而只关心对象是什么。,如果两个对象的所有的属性的值都相同我们会认为它们是同一个对象的话,那么我们就可以把这种对象设计为值对象。因此,值对象没有唯一标识,这是它和实体的最大不同。另外值对象在判断是否是同一个对象时是通过它们的所有属性是否相同,如果相同则认为是同一个值对象;而我们在区分是否是同一个实体时,只看实体的唯一标识是否相同,而不管实体的属性是否相同;值对象另外一个明显的特征是不可变,即所有属性都是只读的。因为属性是只读的,所以可以被安全的共享;
  • 领域服务(Domain Service)

    领域中的一些概念不太适合建模为对象,即归类到实体对象或值对象,因为它们本质上就是一些操作,一些动作,而不是事物。这些操作或动作往往会涉及到多个领域对象,并且需要协调这些领域对象共同完成这个操作或动作。如果强行将这些操作职责分配给任何一个对象,则被分配的对象就是承担一些不该承担的职责,从而会导致对象的职责不明确很混乱。但是基于类的面向对象语言规定任何属性或行为都必须放在对象里面。所以我们需要寻找一种新的模式来表示这种跨多个对象的操作,DDD认为服务是一个很自然的范式用来对应这种跨多个对象的操作,所以就有了领域服务这个模式。

    领域服务还有一个很重要的功能就是可以避免领域逻辑泄露到应用层。因为如果没有领域服务,那么应用层会直接调用领域对象完成本该是属于领域服务该做的操作,这样一来,领域层可能会把一部分领域知识泄露到应用层。

  • 工厂(Factory)

    DDD中的工厂也是一种体现封装思想的模式。DDD中引入工厂模式的原因是:有时创建一个领域对象是一件比较复杂的事情,不仅仅是简单的new操作。正如对象封装了内部实现一样(我们无需知道对象的内部实现就可以使用对象的行为),工厂则是用来封装创建一个复杂对象尤其是聚合时所需的知识,工厂的作用是将创建对象的细节隐藏起来。客户传递给工厂一些简单的参数,然后工厂可以在内部创建出一个复杂的领域对象然后返回给客户。目前渐渐通过IOC替代,常用的IOC组件有Unity、StructureMap等
  • 仓储(Repository)

    仓储被设计出来的目的是基于这个原因:领域模型中的对象自从被创建出来后不会一直留在内存中活动的,当它不活动时会被持久化到数据库中,然后当需要的时候我们会重建该对象;重建对象就是根据数据库中已存储的对象的状态重新创建对象的过程;所以,可见重建对象是一个和数据库打交道的过程。从更广义的角度来理解,我们经常会像集合一样从某个类似集合的地方根据某个条件获取一个或一些对象,往集合中添加对象或移除对象。也就是说,我们需要提供一种机制,可以提供类似集合的接口来帮助我们管理对象。仓储就是基于这样的思想被设计出来的;

    仓储里面存放的对象一定是聚合,原因是之前提到的领域模型中是以聚合的概念去划分边界的;仓储还有一个重要的特征就是分为仓储定义部分和仓储实现部分,在领域模型中我们定义仓储的接口,而在基础设施层实现具体的仓储。

关于DDD的一些思考

1、聚合根尽量要小,封装了聚合根内部的所有操作;外部必须通过聚合根才能操作聚合根内部的实体;
2、多个聚合根协调完成一项工作,通常采用的处理方式有两种:领域服务和领域事件

1>、领域服务将多个聚合根整合在一起协调处理完成相应的业务。

2>、领域事件通过订阅发布的形式完成多个聚合根的协同工作,事件异步通讯框架EQueue、nservicebus;

3、Repository是聚合根的仓储,用于储存聚合根的数据;IRepository的定义放在领域Domain,Repository的实现放在基础设施层,这样就可以做到领域不依赖基础设施层,而仅仅依赖接口;Repository一般包含CRUD的操作,领域层通过Repository仅仅获取数据,返回最新聚合根。而应用层却通过Repository来持久化数据到DB;数据持久化的组件也有很多:EF、NHibernate、Dapper;
4、一个聚合根的Update流程:

1>、应用层通过仓储获取领域层相应的聚合根;

2>、调用聚合根内部的方法来更新聚合根到最新状态,返回最新聚合根到应用层;

3>、应用层利用仓储更到DB;

备注:当然上述是简单流程,视情况而定,有时候,我们会调用聚合根内部的方法来Rasie一个领域事件,让相应的EventHandler来处数据

参考文章:

http://www.cnblogs.com/netfocus/archive/2011/10/10/2204949.html

http://www.cnblogs.com/jesse2013/p/the-first-glance-of-ddd.html