5 领域驱动设计实战
5.1传统的开发方式和领域驱动设计对比
传统的软件一般是action->service->dao,系统大部分的业务逻辑都在service,没有一个核心领域的概念,这样整个软件系统在扩展起来就是通过在Service里面增加方法或者继续增加更多的Service,这样以来,随着系统开发过程的不断演进,这个service层也就变的非常庞大,这个时候已经完全丧失了领域的概念,业务逻辑的复用性变的很低,有时候为了实现某种功能,自己都很难发现到底这个功能再哪个Service里已经实现了,即使自己知道实现相同的功能的那个service,为了实现某个功能,你需要把其它的Service整个注入进来,这样不仅破坏了封装性而且也更不容易维护。
传统的开发方式的架构图如图5-1 所示:
5-1 贫血模型架构图
采用传统的开发方式,系统调用的序列图如图5-2所示:
5-2 贫血模型序列图
采用领域驱动开发的方式以后,也许刚开始项目会进展慢一点,因为毕竟要从需求中提炼出领域,而领域模型也不能一下子就形成,这需要一个过程,但是随着软件项目开发进程的不断推进,等领域模型慢慢建立起来以后,我们就会发现速度越来越快,因为领域模型经过迭代和重构,已经与真实的领域形成了共振,这个时候需要实现什么功能,我们只需要调用有丰富业务逻辑的领域对象来完成,而不是重新开发出一套接口。
 
采用领域驱动设计以后,整个系统的架构图如图5-3所示:
5-3 DDD architecture 
采用DDD以后,系统调用的序列图如图5-4所示:
5-4 DDD sequence
 
6 领域驱动设计实战总结
 
6.1 Extendability(扩展性)
采用DDD以后,随着系统的开发不断继续,领域模型也逐渐形成,同时领域模型也逐渐的反映了领域的实质,这样系统需要新的功能的时候,领域模型已经完成了很多业务逻辑操作,我们需要做的仅仅是指导已经存在的领域模型对象来实现新增加的功能。如果领域模型里面没有完成新功能所需要的逻辑,那么此时就需要将新增加的功能增加到领域模型中,这个增加不是随便的增加,一定要增加到合适的领域模型对象里面,这样以来越到开发后期,因为领域模型越来越完善,这样增加新功能所需要的工作量也将越少。
6.2 Reuseability(复用性)
采用DDD以后,因为领域模型已经实现了很多的业务逻辑,并且这些业务逻辑都是可以在不同的业务操作期间复用的,而不像传统的那种方式一样,要想复用就需要将其它Service注射到当前完成业务操作的Service中来,这样就造成Service的业务逻辑封装性降低,并且使得Service越来越多,复用性越来越低。
6.3 Maintainability(维护性)
采用传统的开发方式,业务逻辑通过Service实现,这样就会造成非常多的Service,这样在维护的时候,也相应的增加维护的工作量,从而增加维护成本,更糟糕的是Service没有领域的概念在里面,在维护的时候,我们没有办法按照领域实际的概念去理解它,比如上面的ForumThread例子,维护人员在维护的时候,找到FroumThread就可以找到与ForumThread的一些行为,而这些行为在论坛这个真实的领域中也是确实存在的。
 
 领域驱动设计和缓存
重视对象的生命周期
1.1 什么是对象的生命周期
任何事物都有生命,当然系统中的对象也不例外,一个对象从创建到最终从系统中消失这整个过程就是领域对象的生命周期,对象的生命周期如果控制不好,将会对系统带来很大的负面影响。
1.2 为什么重视对象的生命周期
为什么会出现Spring/Jdon这样的IOC容器,为什么会出现一些中间件服务器?IOC容器和中间件服务器到底为系统带来了什么好处?这里面其实就包含着对象生命周期的思想。在系统中,有很多无状态的线程安全的组件,通俗点说也可以理解为Service,这些组件我们通过IOC容器来进行管理,IOC容器其实主要就是管理了这些组件的生命周期。还比如Jboss等中间件服务器,它们也提供了对EJB组件的生命周期管理。
无状态的那些组件我们通过容器的概念进行了管理,那么我们系统中模型对象怎么管理呢?这个地方就要用到缓存了。
Java语言来说,JAVA具有垃圾收集器,但是有了垃圾收集器,我们是不是就不需要考虑对象的生命周期了呢?不是,一个对象new出来以后,如果使用完了就扔掉,那么势必会造成越来越多的垃圾对象,这样也就会使得垃圾收集器频繁的启动,而垃圾收集器的算法是依赖于不同的JVM版本,以及JVM启动时候,你自己设置的JVM参数,因此如果不注重对象的生命周期,那么就会引起垃圾收集器不要的启动,这对"stop-the-whole-world"的次要GC来说,将会极大的影响到系统的性能。因此我们需要引入一种能复用对象的机制,而这种机制目前最合适的就是通过缓存。
2领域模型为什么需要缓存
为什么会在领域驱动设计里面涉及缓存?经过领域建模以后,整个模型对象都是完全面向业务的,并且具有丰富的行为,而对象和数据库之间又存在天然的矛盾,这种矛盾主要体现在领域对象保存到数据库时,需要把其一层层打开,然后放入数据库,而从数据库里生成领域对象有需要将裸体的数据穿上衣服,最终形成领域对象,这个过程非常的麻烦,因此我们为什么不在领域对象用完以后放到某一种地方,而这种地方可以方便快捷的取出对象和放入对象,这个地方其实就是缓存。
缓存是领域对象在内存中的生存场所,是一种面向业务的存储方式,而同时我们的领域模型也是一种面向业务的模型,有了面向业务的存储以后,我们就可以进行面向业务的运算,而正是这种面向业务的运算使得我们的系统具有更好的伸缩性和扩展性。因为此时的领域对象通过缓存都是跑在J2EE中间件中,而在负载增多的时候,通过水平的增加中间件服务器来进行水平伸缩。
缓存+领域模型是面向业务的对象模型,面向业务的存储,面向业务的运算结合的基础,而数据库则是一种完全面向数据的存储方式,因此数据库思维和对象模型思维是不匹配的。
 
 
缓存概述
3.1 缓存定义
缓存是计算机领域非常重要的一个概念,它是介于应用程序和永久性的存储系统(比如数据库,文件系统等)之间一种媒介。缓存降低了应用程序对持久性数据源的访问,从而使得应用程序具有更好的性能。
3.2 缓存种类
3.2.1 按照缓存中的元素划分
缓存按照缓存中存储的内容来划分可以划分为:
数据缓存(比如数据库中内置的缓存)
文件缓存(比如文件系统的缓存,浏览器的缓存js,css文件等)
对象缓存
      对象缓存是这次交流的重点,采用了DDD以后,系统会有一个完整的领域模型,这个领域模型一般目前都是通过对象模型来实现的,那么这个对象模型的就要用到对象缓存了。
   4) 其它缓存(比如CPU内部的高速缓存等等)
3.2.2 按照缓存和应用程序的耦合度划分
Local Cache
Local Cache是指应用程序与缓存运行在统一进程中,比如目前J2EE界流行的OScache,Ehcache,jbosscache等。这一类型的缓存离应用程序近,因此效果也比较好,但是无法应对分布式和集群情况下的缓存要求,因为local Cache多数都是采用了广播机制来对各个节点进行更新,这样在集群环境下,当节点个数比较多的时候,广播通信就会对系统带来很大的开销,甚至这种开销抵消了缓存带来的性能方面的提升。
Remote Cache
Remote Cache是指应用程序与缓存运行在不同的进程中,这种类型的缓存的代表是著名的MemcachedRemote Cache是一种分布式和集群级别的缓存,这种情况下,应用程序一般通过缓存系统的协议与缓存系统进行交互。
3.2.3 按照缓存的生命周期划分
按照缓存的生命周期来划分,缓存可以分为以下几种类型:
Request或者事务级别
Request级别的缓存一般存在于单个的Request生命周期里面,当一个Request结束的时候,缓存也就消失了。Request级别的缓存的代表是Hibernate的一级缓存。Request级别的缓存也可以叫作事务级别的,因为每次事务结束的时候,缓存也随之消失。
Session级别
Session级别的缓存一般是扩展到了整个Session期间,它存活于整个Session周期中,当Session结束时,Session级别的缓存也随之结束。这方面的缓存比如Httpsession或者Statefull session bean 扩展持久化上下文。
Application级别
Application级别的缓存存在一个整个Application的生命周期之中,当应用程序启动的时候,缓存生命周期开始,当应用程序结束的时候,缓存的生命周期结束。
在采用了DDD建模以后形成了领域模型,这个领域模型需要缓存,此时主要是指Applicaiton级别的缓存,是整个应用程序级别的缓存。
3.3 缓存的清除策略
 
当缓存中的元素超过缓存的限制的时候,缓存系统就会采用一定的缓存策略将缓存中的元素从缓存中移除,缓存的清除策略主要有以下3种:
FIFO  (First In First Out)
这种缓存策略表示当缓存清除缓存元素的时候,缓存系统会清除在缓存中时间最长的元素。
LRU  (Least Recently Use)
这种缓存策略表示当缓存清除缓存元素的时候,缓存系统首先清除最近最少使用的元素,这种情况下,一般缓存元素都会有一个时间戳,缓存系统会选择清除时间戳离当前时间最远的元素。
LFU  (Less Frequently Use)
这种缓存策略表示当缓存清除缓存元素的时候,缓存系统首先选择缓存中一直以来最少使用的元素,这种情况下,缓存元素一般都会有个hits属性,每次命中以后,hits都会加一,当要清除的时候,缓存系统选择hits最小的元素清除。
在选择缓存清除策略的时候,我们根据当前业务系统的特点来进行选择,每一种缓存清除策略都有它自己的优点。一般情况下推荐使用LRU。 
 
4 领域模型和缓存如何配合
4.1 对底层缓存系统进行封装
采用DDD建模和缓存以后,首先我们面临的问题就是如何对缓存系统进行封装的问题,因为缓存系统是一种技术的时候,同时也有好多种选择,我们不能让领域模型依赖于具体的缓存技术,这个时候就需要进行缓存的封装。而在本次CBB LPQ的设计中,我们的团队也对底层缓存系统进行了封装,具体在封装的时候,我们可以通过图4-1所示的方式进行封装:
4-1 Cache 类图
4.2 提供统一的缓存入口
在系统中引入缓存之后,我们需要提供一个统一的缓存入口,系统的其它部分要想通过缓存来获取领域对象都必须通过统一的缓存入口。通过提供统一的缓存入口使得缓存的管理更加方面。如果不提供一个统一的缓存入口,这样整个缓存的逻辑就散落在了系统的不同部分,这样维护起来就比较麻烦。
4.2 FactoryCache的结合
采用DDD建模以后,系统中会形成很多领域对象,当需要创建一个领域对象的时候,因为领域对象涉及到很多状态,要构建一个状态非常多的领域对象非常复杂,因此有必要通过工厂来封装,在工厂创建领域对象的时候,因为领域对象聚合了很多的子对象,同时还关联了了其它的聚合根对象,因此当创建一个聚合根的时候,当需要另外的领域对象的时候,首先从缓存中取,如果有就直接返回,没有再查询DB获取,最终形成一个完整的领域对象返回。举个例子,比如创建一个论坛帖子的时候,我们需要获得是谁发帖子,因此在创建帖子的时候,我们需要得到Account(用户)对象,而此时就需要首先从缓存中获取Account对象,如果存在就直接返回,不存在再查数据库。
领域模型和缓存总结
 
经过领域建模以后,系统形成了良好的领域模型,在这个领域模型真正与架构融合,并且跑起来之后,领域模型需要一个生存的场所,领域模型需要在这个生存的场所里面完成它的生命周期,这个生存场所就是缓存。领域模型+缓存是一种面向业务的分析、设计和面向业务的存储结合的结果,这种结合的结果就是能方便的进行面向业务的内存计算。
目前key-value存储系统正处在不断的快速发展过程当中,等以后key-value存储系统真正普及以后,我们可以通过key-value存储系统来作为领域模型的生存场所,这样整个系统也将完全是一种:面向业务分析、设计,面向业务的存储,面向业务内存计算的结合体,这将是非常完美的一个新世界。
5.1 Scalability(伸缩性)
通过将领域模型放入缓存,使得领域模型能够真正的以业务形态存在于内存中,能真正以业务形态进行内存的运算,这样以来,系统负载增多的时候,我们可以采用分布式和集群级别的缓存,这样就增强了系统的水平伸缩性。如果采用传统的那种方式,大部分业务逻辑都与数据耦合,这样当系统负载增多时候,数据库就成为了最不具伸缩性的一层。5.2 Performance(性能)
这也是Hibernate创始人所描述的:数据库成为了大多数企业应用的主要瓶颈,也成为了运行环境中最不具伸缩性的层
 
系统性能方面,因为引入了缓存,这样当每次用户请求来的时候,系统将用更少的时间来完成对用户请求的响应,这样也将提高性能“多快”的方面,同时因为通过缓存降低了对数据库的压力,从而使得系统能应对更多的访问量,这也就增加性能“多大”的方面。同时在性能“多大”方面,通过引入分布式和集群缓存,甚至是引入key-value存储系统,我们的系统能平滑的过渡到一种分布式和集群环境中,这样也使得系统能应对更大负载,从而提升了性能“多大”的方面。
四 总结
采用DDD以后,系统形成了完整的领域模型,这个领域模型已经如实的反映了我们领域的实质,这样领域模型的可扩展性,可维护性和可复用性就非常高,而通过将领域模型和缓存结合以后,整个系统变成了一种:面向业务分析、设计,面向业务的存储和面向业务运算结合的一种更加具有良好性能表现和伸缩性的系统。
因此领域模型和缓存使得系统在可扩展性,可维护下,可复用性,可伸缩性以及性能方面都有良好的改进和提高。
 
结论:
1. 软件不是计算工具,软件帮助人们解决某一领域的复杂问题
2. 软件的核心是领域模型,而不是技术
3. 领域模型是一种面向业务的模型
4. 领域模型更加忠实地反映了软件实质
5. 领域模型+缓存=面向业务的分析和设计+面向业务的存储
6. 领域模型+RDBMS=面向业务的分析和设计+面向关系的存储,不匹配,矛盾!
7. 领域模型使得系统在满足功能的同时,具有可扩展性,可 维护性,可复用性
8. 领域模型和缓存使得系统具有更好的伸缩性和性能
9. 领域模型使得系统天然的具有功能性和非功能性的血统
10.领域模型和架构的关系:分析设计的时候领域模型独立于架构,而实现的时候,领域模型和架构粘合,真正运行的时候,领域模型和架构完全统一(是不是类似于EJB中,编程时分离,部署时粘合,运行时真正统一的思想)

0

收藏

狂放不羁

42篇文章,6W+人气,0粉丝