什么是领域模型 StarUmL

37344人阅读
JAVA(69)
架构师(8)
软件开发要干什么:
反映真实世界要自动化的业务流程解决现实问题
领域Domain
Domain特指软件关注的领域在不能充分了解业务领域的情况下是不可能做出一个好的软件
领域模型驱动设计
}& 分层架构
}& 值对象
分层架构:
}& 将领域模型相关的代码集中到一个层中,把它从用户界面、应用和基础设施代码中分隔开来
}& 释放领域对象的显示自己、保存自己、管理应用任务等职责,让它专注于展现领域模型
}& 复杂的程序切分成层
}& 层中采用内聚的设计
}& 层仅依赖于它底下的那层
实体entity:
有一类对象拥有唯一标识符
}& 能够跨越系统的生命周期甚至能超越软件系统的一系列的延续性和标识符
}& 这样的对象称为实体。
值对象-value Object
}& 对某个对象是什么不感兴趣,只关心它拥有的属性
}& 用来描述领域的特殊方面、且没有标识符的一个对象,叫做值对象
}& 能被简单的创建和丢弃,生命周期中不会被持久化
}& 值对象可以被共享,值对象应该不可变
服务-service(比webservice更细粒度服务描述)
}& 领域中的一些动词,代表了领域中的一个重要的行为,却不属于任何对象
o&&&&&&服务执行的操作涉及一个领域概念,这个领域概念通常不属于一个实体或者值对象
o&&&&&&被执行的操作涉及到领域中的其他的对象
o&&&&&&操作是无状态的
}& 服务对象不再拥有内置的状态
}& 服务对象担当重要的协调功能
}& 开发通用语言时,领域中的主要概念被引入到语言中,语言中的名词很容易被映射成对象。
语言中对应那些名词的动词变成那些对象的行为。但是有些领域中的动作,它们是一些动词,看上去却不属于任何对象。它们代表了领域中的一个重要的行为,所以不能忽略它们或者简单的把它们合并到某个实体或者值对象中。给一个对象增加这样的行为会破坏这个对象,让它看上去拥有了本该属于它的功能。
}& 将相关领域模型提炼分类,分而治之
}& 将高关联度的模型分组到一个模块以提供尽可能大的内聚(以能完整完成任务为准)
}& 分层是水平划分
}& 模块是垂直划分(Domain内部)
参考架构概述
}& 领域驱动设计(DomainDriven Design)有一个官方的sample工程,名为DDDSample
}& 该工程给出了一种实践领域驱动设计的参考架构
架构详解:Interfaces-接口层
}& 该层包含与其他系统进行交互的接口与通信设施,在多数应用里
}& 可能提供包括WebServices、RMI或Rest等在内的一种或多种通信接口
}& 该层主要由Facade、DTO和Assembler三类组件构成,三类组件均是典型的J2EE模式
}& DTO- DataTransfer Object(数据传输对象),也常被称作VO-ValueObject(值对象)
}& DTO设计之初是为了将细粒度的领域对象包装为粗粒度的数据结构,减少网络通信并简化调用接口
}& 减少网络流量
}& 简化远程对象和远程接口
}& 传输更多的数据减少远程调用次数
}& 避免将领域状态跨层次传递
}& 由于同步和版本控制增加了复杂性
DTO 应用时序图
}& DTO与领域对象之间的相互转换工作多由Assembler承担
}& 因此Assembler几乎总是同DTO一起出现。
Assembler 实现方案
}& 实践Facade的过程中最难把握的问题就是Facade的粒度问题。
}& 传统的Service均以实体为单位进行组织,而Facade应该具有更粗粒度的组织依据,较为合适的粒度依据有:
}& 一个高度内聚的模块一个Facade
}& 或者是一个“聚合”(特指领域驱动设计)一个Facade.
Facade 实现方案
Facade 应用时序图
}& Service会与多种组件进行交互
}& 这些组件包括:
o&&&&&&其他的Service
o&&&&&&领域对象
o&&&&&&Repository
o&&&&&&DAO
Service 应用时序图
Domain-领域层
}& Domain层是整个系统的核心层,该层维护一个使用面向对象技术实现的领域模型,几乎全部的业务逻辑会在该层实现
}& Domain层包含:
o&&&&&&Entity(实体)
o&&&&&&ValueObject(值对象)
o&&&&&&Domain Event(领域事件)
o&&&&&&Repository(仓储)等
Infrastructure-基础设施层
}& 基础设施层nfrastructure为Interfaces、Application和Domain三层提供支撑
}& 所有与具体平台、框架相关的实现会在Infrastructure中提供,避免三层特别是Domain层掺杂进这些实现,从而“污染”领域模型
}& Infrastructure中最常见的一类设施是对象持久化的具体实现
“传统”架构-贫血领域模型
DDD && SOA
}& DDD 领域模型驱动设计
}& SOA& 面向服务的架构
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1795510次
积分:13923
积分:13923
排名:第884名
原创:142篇
转载:241篇
评论:221条
(1)(2)(1)(1)(3)(2)(2)(2)(4)(4)(1)(26)(1)(1)(8)(2)(4)(8)(4)(1)(1)(9)(2)(2)(1)(2)(2)(1)(4)(8)(3)(4)(1)(3)(11)(7)(2)(3)(1)(1)(2)(3)(4)(4)(2)(6)(5)(7)(5)(17)(17)(1)(10)(6)(16)(11)(4)(9)(3)(15)(18)(7)(16)(29)(12)(7)(1)关注51Testing
为什么要让我们的“领域模型”充血裸奔?
发表于: 10:10 &作者:蔡阿斌 & 来源:码字
推荐标签:
  做不完的应用软件  我爸是个乡村小学教师,对我所从事的软件行业一无所知,但是他对我的工作稳定性表示怀疑:“你这做软件的,要是有一天软件做完了,你岂不是要失业了?”也许他想起了他作为老师的情况,教完一批学生,下一批又上来了,一茬一茬的。于是又问我:“你们是不是一个软件接着一个软件做?”我回答他:“不是,就一个软件,好几十个人得做好几年呢。”解释了很多次仍旧没有消除他的疑问:“你们做软件怎么会一直做下去?怎么没有个做完的时候呢?”。  如果他在通往张江的地铁上,知道有那么多我伤不起的IT同类们,他也许会更加迷惑。为什么如此庞大的程序员大军,日复一日年复一年地敲着代码,生产出无数的软件,可是他们不用担心失业?为什么需要那么多看上去类似的软件?为什么这些软件永远没有做完的那天?  应用软件独一无二的地方  答案其实很简单,我们做的每一套软件,都是为了解决某个领域的业务需求。而业务需求永远没有停止变化的一天,这就是为什么应用软件永远也做不完的原因。  想想我们为了构建一个应用软件,需要做哪些事?古老的三层架构里,我们要实现业务层(Domain Business Logic)、展现层(Presentation)、存储层(Persistence)。还有Authentication、Authorization、Performance、Security等等。再复杂的系统还包括很重要的系统集成。  稍微思考一下就会发现,在这些要做的事情里面,只有Business Logic是独一无二的。,而其它都有框架。  Presentation层。从古老的Delphi,Winform,WPF到现代化的AngularJS、ReactJS,演化得风生水起。  Persistence层,从关系型、存储过程,到ORM框架,以及各种NoSql……同样种类繁多。  缓存,安全,系统集成,莫不如是。所有的东西都可以找到框架,这就是为什么写应用软件,跟写游戏,或者写等比起来,让人觉得是最没有含量的。  然而唯独“业务逻辑”没有框架。正是这“业务逻辑”,让每个应用软件区别于其它应用软件。因此我们决定要做一个应用软件,我们要做的就是实现客户的业务逻辑,这是唯一真正的目的。持久化,UI,Cache……,都是手段。  技术与设计  上面提到技术含量的论断,当然是值得商榷的。在软件行业浸淫十多年后,我越来越倾向于把技术能力和设计能力分开。  什么是技术?对RabbitMQ很精通,我们说这人技术不错。什么是设计?什么时候应该用RabbitMQ?什么时候不应该用?以及如何使用?对于所需的场景,用里面的什么模式?这些都是设计。另外一个例子,技术好的人对C#语言本身很精通,设计好的人则知道应该怎么写代码,代码应该封装到哪一层,哪个类中,类与类之间怎么协作,等等。    我把技术称为术,把设计称为道。道与术是互相促进的,对RabbitMQ越精通,才知道各种情况下如何使用它。反过来,越是熟悉各种情况下使用它的套路,换一个ZeroMQ,也越容易上手。  我一直鼓吹道术双休,不是因为其中一个比另一个更重要,而是因为大部分的程序员,对道的关注都太少了。只关注术的结果,相信每个人都见识过,身边一定有无数这样的人:年纪越来越大,对术的学习能力越来越赶不上年轻人,所以要么转行做管理做业务,要么在技术这条路上慢慢被淘汰。  为什么需要设计?  先退一步,为什么需要设计?两个原因:  需求会变。我们的设计就是为了适应可能的变化,我们不能低估需求变化的可能性,导致设计很难适应变化,又不能高估需求变化的可能性而引入过度设计,所以设计是一种平衡的艺术,而且设计的输入就是需求。  这是这是程序员的自我需要。写代码的过程有点像写的过程,要前后连贯,后面的段落跟前面的段落要有逻辑关系。10分钟后写的代码,10分钟前写的代码能够呼应,我们需要重构10分钟前写的代码,来让现在的代码能按新的思路写下去。代码不是写出来的,代码是重构出来的。就像去看作家们的手稿,哪个不是修修改改无数次,曹雪芹一部红楼梦,是于悼红轩中披阅十载,增删五次而成。当然不排除有下笔即成的天才,但我们还是别认为自己是天才为好。我们需要好的设计是因为我们需要读昨天的代码来写今天的代码,我们不希望我们自己都看不懂昨天的代码了。  架构与设计  在技术这条路上一直走下去,终极目标基本是架构师,很酷的名字。然而,什么是架构?  想想为什么需要架构?因为需求会变,因为这是程序员的自我需要。答案跟为什么需要设计是一样的。所以架构就是设计,我很喜欢Uncle Bob对架构的定义:  架构是设计里不可逆的部分。  一般的设计,如果错了,大不了重构,代价可以承担。但大的设计,比如决定了用AngularJS来写前端,并且建立了一套的框架,如果发现错了,重构的代价就大了。架构就是不可逆的设计,所以架构的要求高得多。反过来说,要做架构师,从注重设计开始。  建筑与软件  说了这么多,那么什么是中的设计?  有一种把用建筑来隐喻软件开发的说法,建筑的设计师是指那些设计图纸的人,建筑工人依据这些设计图纸来实施。换到软件行业,架构师写出设计文档,程序员根据设计文档用代码实现出来。这是错误的隐喻。正确的隐喻是:写代码的程序员才是建筑里的设计师,MsBuild等编译工具才是建筑里的建筑工人。  就是说,软件开发中,代码才是设计,MsBuild把代码build成exe才是实现。  程序员是多么幸福,比较一下大楼的设计人员,当他们设计好的方案(设计图纸)一旦被建筑工人们开始“实现”的时候,他们的设计几乎就不能再改了,因为“实现”的成本太昂贵了。而作为软件设计师,我们的“建筑工人”MsBuild包工头以及它的团队(CPU,RAM等小兵)是多么的廉价和高效,几秒钟就把我们的设计给实现了。这就是我们能够利用“重构”技术的理由。(想象一下如果大楼设计人员也这么说:“你们先按照这方案盖起来,我看效果,然后再调整(重构)”……)  与传统行业的设计师相比,我们软件设计师能得到的反馈更快更多(因为我们面对的是电脑),这就是我们幸福的地方,也是我们应该利用的地方。  需求与架构  刚刚讲了,需求是设计或者架构很重要的输入。我们来看一下有哪些需求?  业务需求。  UI需求,现在前端火的一个重要原因是手持终端的多样性突然爆发,完全吞噬了市场上可用的前端开发。  存储需求,大数据时代,对数据的格式,数据的volume,都提出了新的挑战。  性能需求。  安全需求。  等等...  架构的一个重要使命,就是用好的设计来应对各种需求的各种可能变化。而第一步,就是要隔离这些不同的需求,架构里的每一块砖头,只负责实现一种需求,这样当某种需求变化时,只需要改其中一块砖头,而不是改整面墙。(这不就是面向对象里SOLID原则里的Single Responsibility Principal吗?再一次佐证架构就是设计。)  如果对上面的需求进行分类,第一个我们成为功能性需求,其它则是非功能性需求。话题拉回来,满足功能性需求是软件的根源目的,满足非功能性需求则是为了让软件可用好用而衍生出来的其它目的。  我们做的每一个系统,都在为客户交付独一无二的业务价值。同时,我们也在进化出不同的框架,来满足各个领域的非功能性需求。这些就解决了程序员们“存在的意义”的哲学问题。  在哪里实现业务逻辑?  以上都是铺垫。至此,我们厘清了两点:一是业务需求是软件的根源目的;二是代码是唯一的设计。现在让我们戴上架构师的帽子,想想如何用代码在哪里实现业务逻辑,有很多选择:  前些年很常见如今被人很鄙视的一种是,存储过程。这种曾经非常流行的技术,自然有它产生的原因。存储过程是什么?是数据库里的东西,而且是关系型数据库里的东西。很多人的思维是这样的:当他试图理解一个业务逻辑时,他心里想的是表以及表与表之间的关系,这就是Database-Driven逻辑,在这种逻辑下,把业务逻辑写在存储过程里,是很自然的事情。如果把思维切换到Domain-Driven的模式中:业务逻辑是我的核心,持久化只是一个辅助的手段,我可以用关系型数据库,也可以用NoSql,而NoSql根本没有存储过程,如此,你把业务逻辑写在存储过程中让人情何以堪啊?  写在UI里,这就要提到当年的RAD之王Delphi了。并不是说在Delphi里只能这么做,而是说Delphi里很多人就这么做,UI直接绑定DataSet,用户点击了某按钮,直接在IDE里双击该按钮,生成Btn1_Click方法,把业务逻辑通通写在那。这么做有一万种缺点,但有一个优点,就是RAD中的R(Rapid)。  写在MVC的Controller里,其实等同于2。MVC/MVP/MVVM是类似的一组模式,但是要知道,它们属于Presentation Layer的pattern,不是属于Domain Layer的。  最好的方式,当然是写在一个独立的Domain Layer里。别忘了业务逻辑是一个应用系统唯一独一无二的地方。  如何实现Domain Layer?  我做过几次技术面试,一般都会有个问题:“你能说说你对架构的理解吗?”得到的回答,第一句往往是:“关于架构,一般是分成3层,Presentation,Business Logic,Persistence……”。这句话即使是很Junior的人也能说得上来,可是再往下问就能问出有意思的东西了:3层之间的依赖关系是怎样的?  一般的回答是:Presentation依赖于Business Logic, Business Logic依赖于Persistence。  可是既然每个应用系统的“业务逻辑”才是应用系统存在的理由,才是开发它的目的所在。而UI展现、数据库存储、Cache等都是为了实现“业务逻辑”这个目的所提供的手段,都有成熟的框架、模式可用,都可以是雷同的。  那么为什么“业务逻辑”要依赖于“存储技术”?为什么“目的”要依赖于“手段”?  逻辑依赖与物理依赖  其实“目的”依赖于“手段”并没有什么问题,但更准确的说法应该是“目的”受约束于“手段”,具体说就是“业务逻辑层”受约束于“数据存储层”,举个例子,如果使用NHibernate作为ORM框架,设计的“领域模型”一定是把所有属性都设置为virtual,为了迁就于NHibernate的LazyLoad实现技术。这种迁就或者依赖是无法消除的,然而这里说的是概念上或逻辑上的依赖。  如果到了具体实现上,仍然存在这种依赖,就成了物理上的依赖,简单地说就是BLL这个assembly/package会对DAL这个assembly/package有个引用。物理依赖有什么问题?可以有很多答案,本文余下部分从和角度去佐证。  反馈延迟带来的伤害  先离题一下说说反馈。举个例子,我们拿着杯子去饮水机接水,随着水位的上升,我们知道何时应该停止,这就是眼睛看到水位后,大脑给出的反馈。如果反馈延迟(哪怕只延迟2秒)甚至根本没有反馈,会有什么后果?水溢出来了,大脑才反应过来,后果一定是手被烫到。  简单的例子可以说明反馈被延迟带来的危害。然而在软件开发中,很多团队不断地被延迟的反馈所反复蹂躏伤害。此话怎讲呢?  举个例子吧,“代码即设计”,如果代码就是我们的设计,那么如何保证我们的设计正确?很多团队最常见的办法是人肉测试。把代码打包成软件,然后丢给测试人员甚至客户。在我经历过的一个瀑布式软件过程里,今天写好的代码,也许要一个月后才会到测试人员手中,半年后到客户手中,也就是说,外界对我们设计(代码)的验证和反馈周期,需要几个月之久。这是多么大的延迟,2秒延迟就会烫伤我们的手,几个月,我们伤的起吗?  如何加速反馈  这就是“迭代开发”被引入的一个理由:缩小反馈周期。一个迭代(常见的是2周)内必须把反馈圈给结束掉,也就是2周内完成一个Feature的、设计、代码、测试等所有环节。从这个角度出发,如果一个迭代里不能getting things done(比如有些团队会在下一个迭代里测试上个迭代开发的用户故事),那不叫迭代,那就叫“两周”。  对于一个Feature来说,两周的反馈周期是可以接受的,毕竟每两周有个功能点给客户看看,确保我们do the right thing,很不错了。  然而如何保证我们do things right(比如,设计和可维护性等等足够好)呢?还有,这两周做的正确的东西,如何保证随着功能的不断增加而不会在将来被破坏呢(答案:回归测试)?如果每两周都人肉回归以前做过的所有功能,那就需要太多QA了。  答案就是自动化测试。Unit Test保证do things right;验收测试/集成测试来保证do right things。  自动化测试金字塔    如图,意思是什么呢?如果一个项目的所有自动化是100,那么最下面的Unit Test应该占80个左右,中间的集成测试占15个左右,上面的UI驱动的验收测试占5个左右。(还有个最上面的人肉测试,那是浮云:))为啥呢?因为Unit Test的ROI(投资回报率)最高,它上手容易、运行快,UI驱动的验收测试的ROI最低,运行慢、维护成本高(因为UI是很易变的,UI一变,UI测试脚本就得改。)  但是要注意,这个金字塔不代表下面的测试比上面的重要。它们都重要,处在“浮云”的人工测试也是很重要的,很大的比例是探索性测试。只不过最下层的Unit Test,实现起来是成本最低的,所以一个团队如果要开始自动化测试,最好从Unit Test开始。而最应该写Unit Test的地方是哪个地方呢?毫无疑问,是我们的“目的层”——“领域模型层”。    多说一个题外话:在有些人眼中(比如我),Unit Test不属于自动化测试的范畴,因为Unit Test首先是一种设计,其次才是测试。这里不展开了。  Persistence Ignorance  回到我们的问题,“领域模型层”对“数据存储层”有物理上的依赖,导致的不好的结果就是,很难写Unit Test。想象一下,有个Customer类,它的AddOrder()方法里面调用了DAL层的东西,也就是连接了数据库,那我跑我的UT时也一定要连数据库。连数据库的UT那不叫UT。  怎么办呢?“依赖反转”,Inversion Of Control,IOC。具体做法是:本来BLL依赖于DAL,现在抽一个接口IDAL,让BLL依赖于IDAL,DAL从IDAL继承。从Assembly上来说,BLL和IDAL放到一个Assembly里,DAL放到另一个Assembly,那么DAL这个Assembly现在对BLL那个Assembly有个依赖了。——这样,就把依赖给反转了。然后通过Dependency Injection,在运行时把DAL作为IDAL的运行时实例,注入到BLL中。这就是IOC和DI的关系,他们其实不是一个东西,只不过很相关,有时就用IOC或DI泛指这项技术了。  BLL对DAL的依赖,从编译期延迟到了运行期,编译期对DAL没有依赖,只对IDAL有依赖,这就是Persistence Ignorance。  Unit Test  总结下思路:领域模型裸奔的其中一个好处是可以写丰富的Unit Test,Unit Test不连接database,是为了让它跑起来更快,带来的反馈也就更快。  每个unit test都用其方法名说明了我们的设计意图,甚至小片业务逻辑,比如有个测试用例,方法名叫“should_promote_to_VIP_when_customer_buying_platinum_card”,如果让你接手一个别人留下的代码,你不是很清楚里面的业务逻辑,你是愿意去看文档?还是愿意去看他留下的存储过程、或者100行又臭又长的方法?还是愿意看这样的一句话:“当客户买了白金卡后,应该把它提升为VIP”?  unit test的覆盖率足够高时,我们读完所有一个类的所有unit test方法名(只是名字),我们就知道这个类是干嘛的了。  事实上,一个项目的维护成本往往是开发成本的四五倍甚至几十倍(越差的代码,这个比例越高)。另外大家也深有体会:读代码比写代码难,我们90%的时间都花在读代码上而不是写代码上了。让代码可读,这是对人对己功德无量的事情。  丰满的领域模型裸奔着向我们呼啸而来  下图是敏捷宣言签署者之一的Alistair Cockburn的Hexagonal Architecture,很精彩的图,留作参考资料了。   后记  想看DDD的人可能会后悔,本文其实没怎么讲DDD,但如同我说的道与术,我们的思考模型、心智模型,我们对软件开发的理解,是道中之道,在这个级别,DDD本身也属于术的范畴了。比如DDD中的Repository模式,就是依赖反转的一个应用。    本文提到了Unit Test和测试金字塔,但对于TDD、BDD没有展开;另外,Unit Test是为了提高代码可读性和可维护性的,可是很多人都有困惑,他们的Unit Test只是让需要维护的代码Double了,本来只要维护生产代码,现在多了一套测试代码要维护,每次做点改动,都要两边改。为什么会这样?因为你的Unit Test写错了。应该怎么写?下篇分解。
搜索风云榜
51Testing官方微信
51Testing官方微博
测试知识全知道【图文】领域模型_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
&&good,good
大小:2.08MB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢领域模型中的逻辑划分 -
- ITeye博客
博客分类:
最近在学习领域模型,一直纠结在领域层跟数据库层的划分,网上也查了不少资料,趁这段时间有空,好好总结下!
如何进行逻辑划分:
在软件架构中,最常见的一种架构模式就是层式架构模式,即把一个系统按逻辑上的功能拆分成多个层,层与层之间保持单向依赖关系,每层只依赖于其直接下层,以保证每层的良好封装性和独立性。而层式架构模式最常用的就是:展示层-服务层-数据访问层,应用领域驱动设计(充血模型),服务层还应被进一步拆分为:应用服务层和领域层。
综上所述,每个模块(子系统)又可以拆分成:展示层-应用服务层-领域层-数据访问层四个逻辑层。
重要原则:每层只依赖于其直接下层,层与层之间不能形成循环依赖关系。
领域层跟数据库层关系非常密切,所以我们放在一齐进行论述。两者的关系是:数据访问层依赖于领域层。
先来看看领域层,领域层包含了该模块的领域对象和这些领域对象对应的仓库接口(Repository——对应J2EE设计模式的DAO,为了让设计和开发人员在进行建模的阶段尽量脱离具体技术去进行建模,我们建议使用Repository的模型术语取代DAO这个技术化的出于设计模式的术语),注意,这里包含的是仓库的“接口”,而非实现。对于这样的划分,一直存在着争议,最大的争议点在于:领域层的职责是负责领域业务逻辑处理和封装,不应该把数据访问的相关职责也纳入领域层。下面我们对这种设计进行分析和论述,并提供案例作为理据去论证这种设计的原因。
在现实世界中,完成一个业务,并不需要有所谓“持久化”的操作,但这种模型一旦反映到软件中,“持久化”就变成了必不可少的步骤,我们从两个方面去进行分析:
从领域层本身来看,由于领域对象本身包含了绝大部分的业务逻辑,在一些复杂的业务逻辑中,我们有时候出于性能等因素的考虑,是需要在领域对象被加载到内存后,在其业务逻辑中还需要通过额外的查询去查找完成某个业务所需的数据,而这些数据通常并不是领域对象自身的数据,而可能是从属这个领域对象的其他领域对象的数据,下面举一个例子来说明:
上图是案例系统的出版物管理模块的其中一个主要的领域对象:Pulication——刊物,我们看到,Publication有两个从属的领域对象:PriceItem(历史价格明细)和IssueSchedule(历史出版发行计划),PriceItem保存的是对应Publication在不同时间段的不同销售区域的指定币种的销售价格,IssueSchedule保存的是对于Publication在不同时间段的出版发行计划。现在假设有一个订单业务,需要在某个时间段订阅某Publication,那么,为了计算订单的金额,则必须通过该Publication获取两个关键数据:该Publication在指定区域的指定币种的当前销售价格,以及该Publication在这段时间的发行期数。而这两个数据,均来源于Publication本身,也就意味着,Publication领域对象具有两个职责:获取当前区域指定币种销售价格——getCurrentPriceForCurrency和统计指定时间段的出版期数——countIssues,我们有两种方案去实现这个两个方法:
一、通过ORM工具(Hibernate)对Publication的两个聚合对象集合PriceItem、IssueSchedule进行延迟加载设置,当调用该方法时,就可以不显式的调用Repository,而由ORM自动实现所有从属对象的加载。
二、在两个方法的方法体内,显式的调用Repository接口提供的专有查询方法来获取相应数据,该专有查询方法并不把所有从属对象全部加载到内存,而是通过具体的持久化实现的查询语言(SQL、HQL、XQuery等),把符合条件的数据查询出来,而且,该专有查询方法并不带有任何与具体持久化实现耦合的信息,它只提供抽象,具体实现由数据访问层的领域对象仓库实现类提供。
我们来对比一下两者,前者有两个明显的缺陷:1)从方法的职责上来说,方法只返回符合条件的结果,而这种实现会把所有从属数据(并且包括所有字段)都加载到内存,然后进行筛选,这种做法对内存的损耗是可以非常可观的(试想如果历史数据已经积累了很多年,数据量很大的情况)。2)这种设计(实现)方法是依赖于具体技术的,这就意味着,如果具体技术发生改变,会直接影响到Publication的模型,甚至产生更大的连锁效应。而第二种实现方案恰恰弥补了上述两种严重的缺陷。有人可能会认为,第二种方案的最大缺点是让领域对象与Repository形成了一种双向依赖关系而导致两者无法分开,但我们从本质上分析,第一种方案中,其实领域对象Publication依然无法逃脱其业务逻辑需要查询其从属领域对象这个潜在要求,只不过,第一种方案利用了ORM工具的技术手段,让代码中不需要建立这种显式的依赖而已。所以,从功能上来讲,无论何种实现方案,只要业务上有这样的要求(如上例),领域对象是无法与Repository完全脱离关系的。
综上所述,我们从领域层本身出发,得出的结论是:领域对象与领域对象仓库之间存在着天生的互相依赖关系,这种关系在业务简单的时候可能体现不出来,但随着业务的进展,这种潜在的依赖关系很大机会会出现。因此,无论开始的时候业务是否足够简单,以至于领域对象的业务逻辑并不需要有额外的查询,出于对模型的统一的考虑,还是应该在开始设计的时候,把领域对象仓库接口划入领域层(即使它从职责划分原则上来看并不完全合理,但这世界上通常没有完美的解决方案,只有最适合的解决方案)。
从应用服务层的角度看,对于领域层,应用服务层是其客户程序,因为应用服务层依赖于领域层提供领域对象完成相应服务。正如前面所述,在软件世界中,要完成一个业务,“持久化”几乎必不可少,那么,对于应用服务层来说,单独的使用领域对象或领域对象仓库并没有任何的意义,因为应用服务层如果要使用领域对象,则必须通过领域对象仓库对其进行加载,如果是创建新的领域对象,也必须(绝大部分情况)通过领域对象仓库实现领域对象的持久化。同时,应用服务层使用领域对象仓库的唯一目的就是实现对领域对象的增删改查操作。
综上所述,我们从应用服务层的角度看,对于应用服务层来说,领域对象和领域对象仓库本身就是一体化的东西,无法割裂。
经过上述的详细分析,我们已经有充足的理据支持领域对象与领域对象仓库被统一划入领域层的设计。
我们再来分析数据访问层,前面已经说过,数据访问层是依赖于领域层的,它实际上是为应用服务层提供领域层领域对象仓库的具体实现,通过这样的设计,领域层虽然把数据访问的抽象纳入到其职责范围之内,但依然与具体的数据访问实现细节解藕,只要数据访问的抽象不发生变化,持久化实现发生变化只会影响数据访问层,对其他层是完全透明的。
wenrunchang123
浏览: 182504 次
来自: 深圳
受教了,谢谢
楼主,语句2
||前面已经为true,后面的(0 & ...
Javascript引擎单线程机制及setTimeout执行原 ...
感谢LZ的总结, 受教了

我要回帖

更多关于 领域模型优点 的文章

 

随机推荐