看dddsample 实例 后的个人理解

感谢banq,这几天一直在研究dddsample 实例代码,结合目前项目正在使用的开发框架写点理解心得:

先说一下目前我们项目框架也是三层结构(见下图):
显示层:struts配置文件(jsp,js,action,actionform)
业务层:spring配置文件(service,vo)
持久层:spring,hibernate配置文件(dao,po)

在业务层里存放的对象有业务服务对象(Service)和业务值对象(VO),在Service里控制业务逻辑或业务规则,同时管理业务一致性(控制事务),在持久层里控制业务值对象持久化及查询服务(即业务数据的增删改查),层之间有明确的职责划分。
可能上面框架也是国内公司基本采用的模式,下面再结合DDD领域设计及dddsample 代码的风格谈一下自己的理解
1) DDD对领域层有了特别关注,站在业务领域的角度分析和设计领域对象及对象之间的关系,领域对象反映了现实业务领域的真实场景,加上聚合,工厂模式更能应对今后业务的变更或发展
反观我们目前的框架,没有从整体的业务领域来构思对象,大多都是单表PO,VO在操作,PO主要是为了快速持久化,VO主要是为了传递业务数据,而VO的聚合对象都靠程序来管理(如根据主PO再查询关联从PO集,在转换成从VO,设置到主VO里),也就是VO很大部分作用都是为了显示层来服务,显示什么数据就构造什么属性的VO。
认识:领域对象可供整个业务领域复用,但我们框架的VO基本无法复用(因为它是因显示层来构造)

2)DDD中应用层和领域层分开,应用层控制业务界面操作的事务一致性,根据界面操作调度业务逻辑并给用户输出显示结果,领域层显示领域内的业务数据及数据转换规则。应用层是转为显示层服务的,所以不可复用;而领域层是可供多个应用层进而为多个显示层服务。
反观我们目前框架,中间只有一个业务层,里面夹杂着业务服务,有的是显示层服务方法,有的是业务规则判定供业务复用公共方法,所以既有对显示层专有不可共享的方法,又有其他业务共享的方法;这样在实际使用过程中会很难管理(比如业务发生变化后,有些被共享的业务层方法会随业务变更进行调整,但因为已被复用,所以经常会改造,出现多个相似作用的方法)
认识:稳定的领域层是可被共享的,但我们框架的业务层只有一层很难稳定,业务无法有效共享;

看代码理解的最大困难:
1)领域仓库CargoRepository 和基础实施层的仓库实现CargoRepositoryHibernate,分别属于两个层;这与我头脑中的一贯思维不符,一直想着CargoRepository 接口和实现应该在一个层里。因为interfaces和 application 下的接口和实现都在一个层里,如过他们在一个层里那我理解会舒服点(也即领域对象是属于领域层,但领域层的获取属于基础实施层)。。。。。

2)代码中的包结构,按我正常理解应该以业务来组织,如booking/application,domain,infrastructure,interfaces;

[该贴被agilestone于2009-10-21 18:39修改过]


非常不错,这是目前大多数软件架构,最大的问题就是在在Service里控制业务逻辑或业务规则,造成没有领域模型层,没有领域层,或者领域模型变成数据表的VO,满天飞,这比贫血模型失血模型更加严重。

说到底,还是因为我们在追寻纯技术架构时,忘记了我们的目的在哪里?更好地解决业务需求,只有领域模型才能更自然反映业务需求,结果,新技术架构掌握了,根本的东西丢了,不存在的。

在你这样的架构中,业务需求其实反映在数据表上,而不是领域模型上,是一种数据表驱动开发架构,而不是对象模型驱动开发架构,造成的后果,是随着软件复杂增加,开发效率越来越低,软件稳定性越来越差,性能越来越低。

而基于内存中对象模型的驱动开发方式,从其血液上融合了真实简单反映需求,以及天然具有可伸缩性和扩展性,将功能性需求和非功能性需求完美天然有机结合在一起。

如果采取数据表驱动开发,功能性需求和非功能性需求是分别实现的,由于两者是分开实现,不具备天然融合,因此,在软件规模扩大发展时,就分别出现严重问题,功能性需求难以快速满足,性能等非功能性需求又出现天花板,无法提高,特别是当前局域网概念已经抛弃,互联网网络概率需求场景已经普及情况更加特出。


看贴后,又有了深刻的感触。

我们项目组开发情况就是典型的数据表驱动开发方式,虽然使用着面向对象语言和很成熟的开源框架,项目的分析设计很大精力就在数据表建模,开发人员看到表结构和业务需求基本上内心就有了对象的组织结构和实现方式,一律的面向数据表的业务逻辑判断、增删改查。

正如 banq 所预知,现在项目代码混乱,vo满天飞(一个模块至少一个VO),业务层service和持久层dao充斥着各式各样的方法,之前写代码的开发人员后来都理解不了。而且基于sql 的业务查询(多表关联,或更复杂sql)性能也逐渐暴露出来......

造成这种现状,现在看来就是缺少业务分析,更准确的说使用面向对象的业务分析 的软件过程。光有好的开源框架也不定做出好的软件来。

看代码理解的最大困难:
1)领域仓库CargoRepository 和基础实施层的仓库实现CargoRepositoryHibernate,分别属于两个层;这与我头脑中的一贯思维不符,一直想着CargoRepository 接口和实现应该在一个层里。因为interfaces和 application 下的接口和实现都在一个层里,如过他们在一个层里那我理解会舒服点(也即领域对象是属于领域层,但领域层的获取方式都属于基础实施层)。。。。。

这个问题 那位能帮解释一下呢,现在头脑感觉还很别扭。

至于CargoRepository和CargoRepositoryHibernate是否一个层问题,这是由于目前技术造成的,关系数据库技术需要保存操作,而对于领域模型来说,自身是否持久保存不是其关心的职责。

CargoRepositoryHibernate应该是隐藏在领域模型后面的动作,如果按照通常的三层结构划分,就被划分到持久层。

这种不对称疑惑很多,不必在意。

我这个 dddsample 实例难道版本不同 怎么没有struts 全是servlet的??

接口和实现不在一个层很好理解啊,因为接口本来就是一个api,这个api属于业务层。而实现类就是要跟真实的存储打交道,今天有个hibernate,可能后天会换成mybatis或nosql。然而接口是不会变的,也就是我们常说的:实现依赖于接口而不是接口依赖于实现。至于服务层那种接口和实现在一个层的情况,只是为了贯彻面向接口开发这一原则,在依赖注入盛兴的今天,本来就没多大用处。