解放思想,走出传统三层架构的束缚

05年底的时候,我们项目组要开发一个ERP的系统。我们选择了jsf(ADF)+spring+hibernate的架构进行系统开发,3层分层架构进行设计开发。技术经理把表设计好,跟我们讲清楚表和表之间的关系,然后我们写model,dao,manager,form来实现。那时候我根本没有什么面对对象的设计或者面向数据库的设计的概念,感觉应用系统主要就是设计表之间的关系,数据库的一系列操作。这样浑浑噩噩过了1年多,在jdon上了解了设计模式,但是那时总觉得很难把设计模式落到实处。可能这也是面向数据库设计的结果吧,领域对象都是贫血的,没有行为,只是一个数据的容器,所以也谈不上封装变化什么的。

又继续用这个框架做了几个项目,项目组的人在无意之中倒也配合我的无知。有人画界面原型,有人直接设计出数据表,然后我就吭哧吭哧根据表建model对象,然后利用自己写的模板,快速生成dao,manger,form,jsp界面。呵呵,我估计现在有些公司也是这么做的,结果很明显的,一开始开发速度很快,随着客户的需求变化,你的manager层的逻辑代码也越来越复杂,model中也出现了一些不想看到的字段,再最后,有些manager的业务方法,都不忍心看了。

一直感觉自己徘徊在oo设计的门外,入不了门,直到最近对业务系统设计有了新的认识,以及明白了领域对象生命周期这个概念。

我现在已经明白对系统进行oo设计还是数据库设计不是jsf+spring+hibernate架构所引导的,而在于你是不是有oo的思想去引导你的设计。但是这个架构,或者说这个架构的一些流行的例子确实会给经验不深的设计者产生束缚,会让技术架构影响了对象设计,从而限制了设计者对对象设计的想象能力。用这个架构的时候,领域对象一般是贫血的,jsf页面直接绑定领域对象,然后在web的action里调用manager层进行对象的数据交互和持久化。在这样的思路下,领域对象好像只是一个临时性的数据容器,为request-response阶段业务数据的交互,持久化服务的,他没有舞台去表现他的表达业务的能力。这难道就是领域对象应该扮演的角色吗?把自己的舞台让给更像是技术对象的service来表演?没有更好的方法去表达一个系统吗?当我看到《领域对象设计》这本书的第6章领域对象的生命周期这一章时,看到banq的cargosimple中用的xxxInMem.java这样的仓储类时,我发现原来可以换个角度来理解一个业务系统的。业务系统是领域对象的活动场所,领域对象本身具有的数据和他的行为才是一个业务系统最核心的东西,而数据库是对象冬眠的一个地方(对象冬眠好象有点不恰当,因为数据库做的只是对象数据的持久化)。而orm这些框架,变通的实现了对象的冬眠,及对象的唤醒。我觉得这样去了解一个业务系统太棒了。因为领域对象有生命的,所以设计师可以决定领域对象存活多少时间。比如说,我以前的项目中,领域对象的生存周期都是在一个request,response中的,时时刻刻都被冬眠,造成的结果就是整个业务系统里,没有活动的领域对象,都是冬眠的对象,而要使用这个系统,就需要先把领域对象唤醒,然后进行业务交互。而现在看的一些例子,dddsample,或者jivejdon,都是把领域对象放到缓存中,对象在里面不需要冬眠,是活着的,那么整个业务系统就是有生命的,或者说效率比较高的。

随着对领域对象生命周期的了解,也解开了看示例项目时产生的一些疑惑。现在看的一些示例项目,发现领域对象里往往都有同步方法,而自己以前写的项目,根本没有用到同步功能。现在分析起来,是因为我以前的项目中,领域对象都是贫血的,没方法需要同步,而且领域对象都是request-response期间的,每次被客户端唤醒(比如通过hibernate)的对象其实是不同的对象(业务上可能是同一个对象,但是机器系统里的这些对象的内存块不一样),因为是不同的内存块,所以他们的方法也就不需要同步了。而现在当领域对象有状态后,可以一直活在内存里,那么每个客户端用的领域对象都是可以是同一个(无论业务上,还是机器系统的内存块),那么领域对象的一些业务方法涉及到领域对象自身的数据变化时,就可能需要同步了。

不知道我对业务系统的理解,领域对象生命周期的理解,及领域对象某些方法需要同步的原因的理解是不是正确,如果有偏差,请道友们指正。

还有几个问题要请教道友们:
1,关于领域对象的延迟保存。
如果单机服务器系统,如果突然关机了,那么缓存中的领域对象是不是没有办法被持久化了?
2,关于分布式缓存在系统中扮演的角色。
1)如果用分布式缓存,那么第1个问题是不是就可以解决了?
2)如果机器1的领域对象的方法执行到一半关机了,那么机器2的领域对象会继续执行下去吗?如果会,用什么方案解决的?
3,关于领域对象业务方法的原子性。
一个领域对象A的业务方法需要对象B的参与,但是需要A的数据和B的数据要么同时变,要么同时不变。如果在这个业务方法执行到一半时,关机了,对象A的数据改变了,对象B的数据还没有改变,会不会有这个问题呢?
[该贴被admin于2010-05-08 09:04修改过]

前面理解基本对头,呵呵,挺不错,很有悟性的,很多人其实还是浑浑噩噩纯粹完成功能,被具体框架产品牵着,而不是从业务和架构设计去选择框架,甚至对框架灵活性提出自己要求。比如Hibernate懒加载其实武断干预了领域对象的生命周期,见这个贴我的想法

2010年05月08日 08:47 "ahcen001"的内容
关于领域对象的延迟保存。
如果单机服务器系统,如果突然关机了,那么缓存中的领域对象是不是没有办法被持久化了?
2,关于分布式缓存在系统中扮演的角色。
1)如果用分布式缓存,那么第1个问题是不是就可以解决了?
2)如果机器1的领域对象的方法执 ...

回答几个问题:
1.如果突然关机,内存就没有了,但是这个关机看怎么样关机,如果是正常关闭JEE服务器,比如使用JBOss的stop命令关闭,那么这些就会触发servletcontext的destroy方法,在这个方法中,我们就可以将内存中数据写数据库中,jdonframework中startable接口的stop方法就是这样,Spring/EJB中也有类似destory方法。
强制关闭电源属于恶意事故情况,一般我们通过备份等失败容错来回避,这是另外一个话题。分布式数据库noSQL其实就是这个原理,它们实际也是内存操作,不过内存容量很大很大,就不叫缓存了,你叫它内存数据库也行,他们有自己的保存硬盘的策略,不同产品策略不同,性能也就不同。

2.关于“领域对象业务方法的原子性”后面的问题,执行一半,如果失败,一致性如何保证,这其实是failover失败恢复问题,EJB集群能够解决你这种对容错性极高的要求,但是采取此重量解决方案之前。可以根据CAP在一致性 可用性 容错性三者之间做平衡。

很多情况下没有必要这么复杂,将一个事务方法分散到两台机器上执行,搞复杂了,一般是在一台机器内执行领域对象的原子方法,如果这台机器正常关闭,你可以的destroy中设置暂停等待,等待方法执行完毕,这样整个JEE服务器关闭活动会暂停;如果是非正常关闭,属于失败恢复。



[该贴被admin于2010-05-11 10:42修改过]

谢谢banq的肯定及对问题的解答。
对于问题1,针对恶意事故情况,你所说的备份等失败容错方案,是不是指的是“双机热备”这样的方案。我这里有个小疑问,就是“双机热备”的方案,是不是可以用2台服务器的分布式缓存,然后在做一个web层的集群来代替?

2010年05月08日 09:15 "banq"的内容
前面理解基本对头,呵呵,挺不错,很有悟性的,很多人其实还是浑浑噩噩纯粹完成功能,被具体框架产品牵着,而不是从业务和架构设计去选择框架,甚至对框架灵活性提出自己要求。比如Hibernate懒加载其实武断干预了领域对象的生命周期,见这个贴我的想 ...

--见这个贴我的想,链接还是链接到本贴,是不是链接错了?

2010年05月08日 08:47 "ahcen001"的内容
随着对领域对象生命周期的了解,也解开了看示例项目时产生的一些疑惑。现在看的一些示例项目,发现领域对象里往往都有同步方法,而自己以前写的项目,根本没有用到同步功能。现在分析起来,是因为我以前的项目中,领域对象都是贫血的,没方法需要同步,而且领 ...

呵呵,楼主说的有道理。这里面同步不同步,其实我的理解是涉及了两方面的内容:第一方面就是领域对象的业务完整性,也就说在领域的状态不能因为并发情况造成破坏。而另外一个层面其实就是事务和并发分开处理,以前我们都是靠数据库的ACID事物来实现完整性和事务并发处理,比如设计数据库隔离级别为,读取未提交,读取已提交等等。而采用领域对象其实就是通过内存锁保证完整性和并发控制,从而更好的满足高性能和伸缩性这样的非功能性需求。因此领域对象在能很好的描述业务的同时,也会对非功能性需求有一定的提高。

领域对象 都缓存,那领域对象很多,内存受得了吗?

领域对象中的数据是不同的,但领域方法 可以单例哟。

2010年05月27日 12:01 "shangtang004"的内容
领域对象 都缓存,那领域对象很多,内存受得了吗? ...

还没有好好的想过内存的占用问题。我用过的缓存组件都定义内存容量,也会有对缓存对象淘汰的方案,应该内存不是问题吧。

粗粗看了看,楼主至多否定的只是jsf(ADF)+spring+hibernate的架构,而不是题目所说的“传统三层架构”。
一件事,自己觉得做得不够理想,可能是工具的原因,也可能是自己没做好。就算是工具原因吧,也是自己的原因:自己没选好工具。

我刚学Hibernate,Spring,Struts,刚刚听说三层,随便照找了个例子配置了一下,dao,manager,action三层。仿那个discuz论坛呢?不过,还是直接用不过hql方便,dao,manger怎么看怎么用着难受。

学习了。

我基本不同意楼主的看法。尽管我也在重复楼主之前重复的问题,也有一样的困惑。

首先,完全面向对象的软件工程在国内的中小型公司里很难生存,原因是多方便的,如程序员本身的素质、项目工期等等。
其次,完全面向对象的软件工程本身也不见得能解决你的问题。
第三,框架本身只是个工具而已,它并没有错。错在使用它的人。

我一直觉得现行的开发模式与经典理论有很多冲突,但我个人觉得现行的开发模式是困难中前行的积极向上的人,而不是困在经典理论中无病呻吟的人。对于三层中的manager膨胀问题,我觉得不是框架的错,而且你自己没有把它的责任分析清楚,或分解清楚。

面向对象理论我个人认为就是责任分析理论,和三层框架没有多大点关系,和贫血也没有多大点关系。不是说软件工程非得把领域模型描述的完全一致就OK了,就OO了。贫血中要贫得好,贫得省力、省时,维护起来成本又低也不失好的解决办法,同样也值得业内人的尊敬,而不是因为写了几句不OO的语句,大家都嗤之以鼻!