1 领域驱动开发是用来解决业务系统业务逻辑的复杂性而产生的,使用领域建模可以搭建业务功能到系统实现之间的直通车道,让软件变得更容易维护。
2 领域驱动开发适合和极限or敏捷开发结合,领域建模也应该是迭代的过程,避免过度设计.
3 建模是对现实事物的一种简化和抽象,忽略和问题域无关的事实,提取和问题域息息相关的信息。
4 任何一个软件都是用来满足用户需求的,这个软件之于用户的主题就是领域。例如淘宝网的领域就是网购。
有些领域是能和现实世界的有形实体对应的,如订票系统,有些则是无形的,如账户系统。
5 model在领域驱动模型中的作用有
- model和设计核心互相塑造
- model是团队沟通的基础语言
- model是经过提炼的领域知识
6 领域建模的过程是一个迭代、提炼的过程,通过和领域专家的沟通、问答,不断的深化开发人员对领域的认识,并抽象为对象以及对象行为,画出类图,并且让领域专家也能理解我们建好的模型。模型会在商业软件的迭代开发过程中不断的优化,沉淀。一个好的模型,可以提高产品功能的实现效率。
(在我们的开发工作中,很少正儿八经的去进行领域建模)。
7 在设计代码的时候,要对一些重要的领域业务进行抽象,最好的结果就是能够用代码中的对象类来反应开发人员对于领域的理解,并让领域专家也能看懂。这样才能进行循环反馈,深化领域模型。
8 在领域驱动开发项目里,领域模型是团队内部人员进行沟通的共同语言,领域模型通过uml、或者其他文档和图表来表达。建立共同的语言是必要的,否则会造成信息流的传递失真、误解,进而提高技术人员之间的沟通成本、技术人员和市场人员之间的沟通成本,甚至理解不一致,对项目造成严重影响。建立共同的语言,必须抽象、统一领域模型中的概念,然后用统一规范的概念来交流。当这种团队建立好这种通用语言后,会发现,这些用这些语言,技术和领域专家可以无阻碍交流,通用语言又能反应业务,又能反射出系统实现、类和方法。随着业务的发展,通用语言也会逐渐丰富和演化,系统也会随着领域的演化而进行重构发展。
9 要让模型易于理解,不光要依靠UML,同时也可以依靠一些比较接近普通人思维的示意图来表示模型。
10 即便在分析阶段已经建立好模型,但是在设计阶段、代码阶段如果和模型偏离,那么模型就失去了意义。当设计、编码阶段出现一些偏离模型的情况时,应该需要反思模型的正确性,双向反馈,最终让设计和模型趋于一致,让代码直接反应模型,避免系统陷入混乱。
11 领域模型开发需要有模型范式工具,而面向对象语言刚好符合领域模型的理念,用对象、类来表示领域模型中的抽象。
12 在领域驱动开发中,如果建模者和编码人员严重分离,会导致编码人员不知道模型的目的,重构之后,不同步修改模型,最后导致代码和模型脱节。因此领域驱动模型,建模和编码是一体的,编码人员不仅是编码、还要理解领域模型,甚至是建模者。编码人员必须能够通过代码来解读领域模型。
13 领域模型相对于一个庞大的系统而言,尽管很重要,但是涉及的概念在一个软件系统里面的所有概念的比例不会太大。必须能够把领域模型的概念从软件系统中独立出来,否则会一片混乱。
14 在没有经过领域模型设计的软件系统里,领域逻辑经常会散落在技术实现的各个模块,比如ui组件、数据库脚本,要了解某块业务逻辑,需要一丝不苟的阅读多个模块的代码。当业务和技术实现强耦合时,领域模型的实施则会相当蛋疼。
15 业界比较统一认可的一种架构是分层架构,每一层模块依赖下一层模块,调用只能由上至下。比较标准的一种分层架构如下图:
- UI层,负责展示数据,接收用户的操作,解释用户的操作
- 应用层,对应一个具体的操作,是个很薄的层,不应该包含商业规则、商业知识,和业务状态(除了用户某次操作的进度展示)。针对一个具体的操作,应用层通过调用领域层,让领域层来执行业务规则,组合协调一个或者多个领域对象来完成任务。
- 领域层,是商业软件的核心,负责表示商业规则、商业知识,商业状态。技术细节、存储委派给基础设施层。
- 基础设施层,为上层模块提供通用的技术功能,比如消息通信、数据持久、ui组件、页面渲染。甚至基于框架提供四层之间的通信方式
并非所有的分层架构都拥有这四层,比如有些架构将UI层和应用层放到一起。对于领域驱动开发而言,领域层才是不可或缺的。
分层可以把对复杂的系统进行分治管理,在每一层进行设计,既能保持独立性,容易理解和维护,又能相互协作。每一层都只依赖他之下的那层。领域层可以专注于领域对象的数据和行为,无需关心Ui和数据存储,这样就做到了领域模型的隔离,这是进行领域驱动开发的基本前提。
具体的例子,银行转账
16 基础设施层有时候涉及到框架的选择,尽量避免选择倾入性大的框架,比如ejb之类的,需要强迫领域对象实现某些特定接口或者继承某些基类,这将引入杂质,导致领域对象难以维护。
17 领域建模需要抽象出对象集合,同时也要设计出对象和对象之间的关系,有一对一,一对多,甚至还有多对多。设计的原则是尽量减少对象之间的关联,尽量不要有多对多。可以通过一些简化、限定、提炼,把多对多转化为一对多,化繁为简。
18 实体对象用于表示系统中的一个独一无二的实例,需要分配一个全局唯一的id。通过id来区分不同的对象,而不是属性。属性相同的对象不一定是同一个。唯一的id对用户来说是透明的,仅仅是系统用于区分不同的对象。
还有一种叫做值对象,仅仅是用来描述数据,不涉及到区分不同对象,无唯一id。是否相等完全靠判断他们的属性。值对象最好是使用不变模式,final修饰。使用不变模式的好处是,减少并发修改同一个对象产生程序错误,简化系统设计。当值对象的属性需要变化时,最好创建一个新的值对象。当然也有不使用不变模式的场景,比如性能优化、减少创建对象的开销、值对象没有并发修改的可能性、值对象修改很频繁。
值对象可以作为实体对象的一个属性,分担实体对象的职责,减少实体对象的复杂度。
19 实体和值对象是领域模型中最主要的两类对象,同时包含数据和行为。然而,在领域模型中,还有一些行为涉及到多个不同对象,操作很难将之归于某个实体、值对象,但是在面向对象设计中又必须用对象建模,于是领域模型拥有了另一类对象,称之为服务对象,服务对象代表一组操作,本身没有状态。操作的名字应该是领域建模中的某些行为相关的概念。如果说实体和值对象是名词,那么服务对象就是动词。
不管领域层有服务对象,应用层、基础设施层也有服务对象。基础设施层的服务对象是比较容易识别的,因为它是纯技术层面的对象。而应用层和领域层的服务对象则较难区分,一般来说,不包含商业知识、商业规则的服务对象是应用层的,而包含商业规则的服务对象是领域层的。
如下图
小粒度的实体和值对象很容易把领域知识暴露到应用层、UI层,而中粒度的服务对象正好在中间起到一个桥梁作用,封装细粒度的实体、值对象,作为应用层到领域层的分割线。
一般来说在系统中,服务对象会以单例的形式存在,因为它没有状态。在我们的业务系统中,AO可以认为是应用层的服务对象,而manager可以认为是领域层的服务对象,目前缺少的就是领域层的实体对象了。
20 模块化技术,把领域模型的概念分模块,每个模块高内聚,低耦合。模块的意义在于,可以按照分治法的思路去理解一个系统,既能看到全局,不同模块之间的关系。又能选择其中的某一个模块深入理解,各个击破。既见树木,又见森林。
21 领域对象的生命周期解决方案----中间周期聚集。领域对象之间会有所关联,在一个领域对象的生命周期中,往往会和其他领域对象作为一个整体,变化会有联动,解决的办法是采用聚集技术。每一个聚集都有一个根对象,这个对象是一个实体,具有唯一标识符。这个对象在创建以及之后的修改都会管理很多其他对象。根对象是客户使用这个聚集的一个句柄,客户无法直接操作聚集内部的数据,根对象是对外唯一接口。这样严格的封装性,就可以统一操作入口,对聚集的修改进行整体联动,避免数据不一致。聚集内部的对象也可以引用另外一个聚集。对于聚集,需要考虑并发修改的可能性,如果不对并发修改做控制,那么就有可能导致聚集的商业规则被破坏。例如订单场景,订单对象的一个不变式规则是一个订单对象最多只能包含10个商品,如果多个用户并发操作订单对象,就有可能出现持久化到数据库里的商品数量超过10个。所以对于这种业务场景,需要综合考虑,如果竞争不厉害,那么最好是进行锁操作,每个时刻只允许一个用户操作订单。如果竞争厉害,那么就需要调整模型,例如采用值对象。从性能和满足业务规则中找到平衡点。
订单例子
22 领域对象生命周期解决方案---初始周期工厂,工厂模式也是设计模式之一,对于复杂的对象构建,如果给客户端来构建,无疑增加使用难度。在领域模型设计中,还会把领域层的逻辑暴露到应用层。因此需要有一个领域层的对象来封装复杂的创建过程,那就是工厂。工厂可以封装聚集的复杂创建过程。工厂除了直接从内存创建新对象之外,还有一种是重建,比如使用ORM技术,把数据库中已经存在的对象,重新构建对象。
23 领域对象生命周期解决方案-中期和后期-仓库,仓库提供了客户检索对象的机制,也包含保存对象的机制。他封装了数据源,使得持久化技术方案对客户保持透明。他和工厂的区别是,工厂主要是用来创建新对象(直接在内存中),特别侧重于构建复杂的聚集对象。尽管工厂也包括了从持久数据源重建老对象到内存中的机制。而仓库主要是用来检索老对象的,客户必须知道它检索的是本来就存在于内存或者数据源中的对象。仓库也可以和工厂协作,比如仓库可以利用工厂重建来自于持久数据源的老数据。用户也可以先通过工厂构造新数据,在利用仓库进行持久化。
24 领域驱动开发整体思路。首先,建立好模型,找出商业系统中的对象,定义好对象的属性,行为。在对象中找出实体对象、值对象。寻找对象与对象之间的关联,并进行简化,并使用聚集技术,把有关联的对象组合为聚集。基于分层架构隔离领域层,根据系统提供的具体功能定义应用层,应用层负责调度领域层的对象完成一个完整功能。领域层需要根据对象的创建场景定义出工厂对象。另外领域层不直接和持久层数据打交道,需要抽象出仓库对象,由仓库对象负责对象的检索和保存操作。