论坛首页 Java企业应用论坛

架构更新

浏览 12214 次
锁定老帖子 主题:架构更新
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2005-03-16  
这个帖子聊聊我们最近开发项目的技术架构,希望大家多提宝贵意见。

【总的思想】
1、以业务层(领域模型)为核心
2、高内聚、低耦合

【特点】
1、将DAO接口置于业务层;
2、在数据访问层实现DAO接口,PO服务于DAO接口

【分层】
显示层-业务层-数据访问层(下面简称数据层)
业务层:细分为事务层-操作层-实体层-DAO接口,DAO接口进一步分为读接口和写接口
数据层:DAO实现+PO
显示层:这个比较简单,用MVC框架,然后调用业务层即可

【业务层与数据层的关系】
数据层服务于业务层,业务层定义数据访问的接口(DAO接口),数据层来实现,数据层的PO是为了持久化的目的设计的,这也是PO唯一的存在价值,PO不属于业务层范畴,不具备反映业务逻辑的职责,它的活动范围被严格限制在数据层之内,换句话说,无论是业务层还是显示层都不能引用PO。

是否设计PO与所使用的持久化技术有关,如果业务实体能够直接拿来持久化,而且性能没有问题,那么就不需要单独设计PO。总之PO属于数据层的实现细节,业务层的设计可以不考虑PO的任何问题,只管提DAO接口就是了。当然DAO接口只能用业务实体做参数,而不能有PO。

【业务层的设计原则】
事务层:包含事务的业务逻辑。必须定义接口。如果用Spring,则在此层绑定事务
操作层:不包含事务的业务逻辑。必须定义接口
实体层:包含简单的、操作自身和聚合边界内其它实体的业务逻辑。接口定义不是必须的。可变的、不稳定的业务逻辑通过在操作层、事务层应用策略模式等方式解决。
DAO接口:接口的语义仅限于反映实体的持久化操作,比如create、update、delete、findByXXX,DAO接口不能带有业务语义。不遵守这个原则会造成业务逻辑与数据逻辑的混淆,不符合高内聚的最高原则

【依赖关系】
总的依赖方向:显示层->业务层<-数据层
显示层可以引用:事务层、操作层、实体层和读的DAO接口,不可以引用写的DAO接口
事务层可以引用:事务层、操作层、实体层和读写的DAO接口
操作层可以引用:操作层、读的DAO接口和实体层
实体层不可以引用其它任何层的接口,包括DAO,实体的操作限于其聚合边界内

显示层还有一个有趣的技术就是,用Struts的话,将业务实体直接放到FormBean里面作为它的一个JavaBean属性,而不是根据页面定义一大堆String属性,页面通过x.x.x访问对象图的方式绑定数据,这样可以大大简化显示层的开发。(可能很多人都是这么用的,只是我们才刚刚醒悟过来)。至于有人提出的业务层一变,显示层会跟着变的问题,这就看你想让谁依赖谁了,你总要确定一个依赖方向,不可能谁也不依赖谁(变通的办法是再加一层,然后都依赖它,如果你愿意的话……),最开始就讲了,架构总的原则是以业务层为核心,这就决定了其它层都必定是依赖与业务层的,我实在是想不出为什么不能依赖业务层。

关于Service,我不太喜欢这个概念(还有Facade),它真的能起到应有的作用吗?它究竟是让接口变简单了呢还是增加了系统整体的复杂程度?jdk那么复杂,也没什么Service层啊!我们用jdk或者Hibernate的时候就看它的api和reference,那么显示层用业务层的时候,也去看业务层的api和reference不结了?
   发表时间:2005-03-16  
补充,包结构如下(按字母序):
###.daoimpl
###.daoimpl.po
###.domain.dao.readers
###.domain.dao.writers
###.domain.entities
###.domain.operations
###.domain.transactions
###.web
0 请登录后投票
   发表时间:2005-03-17  
谢谢分享
请问一下这个项目是刚开始,还是到什么程度了
还有
------------------------------
业务层:细分为事务层-操作层-实体层-DAO接口,DAO接口进一步分为读接口和写接口
-------------------------------
这会不会太复杂了点..
能不能举个比较简单的例子,从显示层到数据层一层一层下来

谢谢
0 请登录后投票
   发表时间:2005-03-17  
项目进度:第一个两周迭代即将结束

业务层的细分不是必须要“一层一层的”下来,可以看看“业务层设计原则”和“依赖关系”,应该不难理解的。
0 请登录后投票
   发表时间:2005-03-17  
领域模型特别吸引人了,期待一些详细的结构
0 请登录后投票
   发表时间:2005-03-17  
提二个意见:
1.实体的操作不必一定要限于其聚合边界内,这样子对实体业务方法的限制太大,且没有明显的好处.
2.服务层还是要有的.
0 请登录后投票
   发表时间:2005-03-18  
引用
总的依赖方向:显示层->业务层<-数据层

如果是按分层设计的话,这个依赖方向是有错误的。如果是按严格的分层,上层只能依赖下层(比如OSI7层模型)。如果要灵活性的话,上层可以引用下面任何一层的接口。上面的依赖方向,不具备分层的特征。
引用
数据层服务于业务层,业务层定义数据访问的接口(DAO接口),数据层来实现,数据层的PO是为了持久化的目的设计的,这也是PO唯一的存在价值,PO不属于业务层范畴,不具备反映业务逻辑的职责,它的活动范围被严格限制在数据层之内,换句话说,无论是业务层还是显示层都不能引用PO。

业务层为什么不能够访问PO?PO描述的应该是Domain的实体,应该是Domain Logic的一部分,为什么要强行的分开呢,还要Plus一次转换?
业务层引用PO是符合分层的概念的啊?
0 请登录后投票
   发表时间:2005-03-18  
多谢大家的关注!

聚合边界的问题:举个例子吧,有两个聚合A和B,A里面的对象引用了一个B的root对象的临时实例,在聚合A之内,可以任意操作,也可以获取B的root的临时实例的数据,但是A的对象对B的root的临时实例的任何修改都不会有任何持久化的效果,原因很简单,因为那是个临时实例。为什么一定是临时实例?如果不是临时实例,确定聚合边界还有什么意义?聚合边界就是从需求的角度出发,一个事务多数情况下是围绕一个聚合展开的,通过DAO访问确定聚合边界内对象的完整,同时获取必要的其它聚合的临时对象引用,任何聚合边界以外的对象都被看作是临时的。即使是需要操作多个聚合,协调多个聚合的任务也不应该是实体层的责任,而是事务层的责任。

服务层:我一直觉得服务层是在画蛇添足,原因前面已经讲过了,如果你觉得必要,请说出一个充分的理由。不要说理由是可以用简单接口封装复杂模型,该有的接口都已经有了,事务也保证是完整的,只不过是分散在它们应该在的类或者接口上,封装考虑的是职责,强行再加一层就破坏了这个职责。找想要的接口方便一点的目的也很难说,系统复杂到一定程度以后,从一堆各自提供了数十个方法的Service接口去找想要的方法也不是件容易的事。

上层只能依赖下层:说的是业务层和数据层之间的关系,本来业务逻辑应该是访问数据逻辑的不错,为什么一定要反过来呢,不是我们要跟教科书较劲,而是需要这么做,原因就是最开始提到的,以业务层为核心,数据层服务于业务层,如何做到这一点呢,这就用到了Bob大叔的《敏捷》里面提到的依赖倒置,将DAO接口放到业务层里面,看看那本书,就不难理解这个了。书中提到了这样的观点,不是上层要依赖下层,而是下层要依赖上层。我认为这个观点也有问题,不是上层一定要依赖下层,也不是下层一定要依赖上层,而应该是,我们想让谁依赖谁,就让谁依赖谁,在方向不满足要求的时候,就是DIP派上用场的时候。教科书说的东西有它的道理,怎么用还是自己需要仔细考虑的。

PO与业务实体的关系:就像上面所讲,数据层是服务与业务层的,数据层的最高目标就是实现业务层定义的DAO接口,而PO是为了满足这个目的、根据实际采用的数据访问技术而设计的,比如用Hibernate的话,可能在很多情况下,不需要设计多余的PO,直接拿业务实体做O/R mapping,但这也不一定,有的时候出于性能的考虑,单独设计PO,或者干脆用JDBC。这些都是数据层内部的细节,业务层根本不必关心。这就是分层的好处。
0 请登录后投票
   发表时间:2005-03-18  
引用
聚合边界的问题:举个例子吧,有两个聚合A和B,A里面的对象引用了一个B的root对象的临时实例,在聚合A之内,可以任意操作,也可以获取B的 root的临时实例的数据,但是A的对象对B的root的临时实例的任何修改都不会有任何持久化的效果,原因很简单,因为那是个临时实例。为什么一定是临时实例?如果不是临时实例,确定聚合边界还有什么意义?聚合边界就是从需求的角度出发,一个事务多数情况下是围绕一个聚合展开的,通过DAO访问确定聚合边界内对象的完整,同时获取必要的其它聚合的临时对象引用,任何聚合边界以外的对象都被看作是临时的。即使是需要操作多个聚合,协调多个聚合的任务也不应该是实体层的责任,而是事务层的责任。

不是很清楚你要表达的意思,能再将详细一些吗?
引用
服务层:我一直觉得服务层是在画蛇添足,原因前面已经讲过了,如果你觉得必要,请说出一个充分的理由。不要说理由是可以用简单接口封装复杂模型,该有的接口都已经有了,事务也保证是完整的,只不过是分散在它们应该在的类或者接口上,封装考虑的是职责,强行再加一层就破坏了这个职责。找想要的接口方便一点的目的也很难说,系统复杂到一定程度以后,从一堆各自提供了数十个方法的Service接口去找想要的方法也不是件容易的事。

你说的理由不足以证明服务层是画蛇添足。任何一个Domain Model不会只是考虑一个应用,它考虑的是在这个在这个domain下的logic。在具体的一个应用里,这些Logic是需要组织起来完成某项工作的,Service就是这个组织者。Facade模式我想我不用再提了吧。另外一个在Service和Facade之上可以组织事务。封装考虑的仅仅是指责吗?没有数据,如何会有职责?
另外你所讲的都是如何实现,而不是如何抽象。
引用
我们想让谁依赖谁,就让谁依赖谁

我认为你说讲的不是很负责任。这样会导致双向依赖,这不算是设计。或许我想这不是你真正想表达的。能在解释一下吗?
既然你说上层不需要依赖下层,下层的方法有谁来驱动?
引用
显示层->业务层<-数据层

谁去调用数据层的方法去持久化业务数据?
我想是业务层把(从你的意思里推断出来的,不知对不对)这样就是双向依赖了。两层都无法独立
引用
PO与业务实体的关系:就像上面所讲,数据层是服务与业务层的,数据层的最高目标就是实现业务层定义的 DAO接口,而PO是为了满足这个目的、根据实际采用的数据访问技术而设计的,比如用Hibernate的话,可能在很多情况下,不需要设计多余的PO,直接拿业务实体做O/R mapping,但这也不一定,有的时候出于性能的考虑,单独设计PO,或者干脆用JDBC。这些都是数据层内部的细节,业务层根本不必关心。这就是分层的好处。

我像你还是没有分清楚什么是PO什么是业务实体。为什么会有PO?是因为有业务实体需要持久化!至于怎么持久化,按我的意思,就是随便你了,用Hibernate还是JDBC,和PO或者业务实体有关么?
0 请登录后投票
   发表时间:2005-03-18  
kewan 写道

引用
我们想让谁依赖谁,就让谁依赖谁

我认为你说讲的不是很负责任。这样会导致双向依赖,这不算是设计。或许我想这不是你真正想表达的。能在解释一下吗?
既然你说上层不需要依赖下层,下层的方法有谁来驱动?
引用
显示层->业务层<-数据层

谁去调用数据层的方法去持久化业务数据?
我想是业务层把(从你的意思里推断出来的,不知对不对)这样就是双向依赖了。两层都无法独立


他将你认为的DAO接口放置到业务层了,在他的眼里面,业务层只依赖于DAO接口,而不依赖于DAO层的实现。而DAO的实现当然实现的是业务层中的DAO接口。

这里的核心在于DAO接口的位置,你看bob的《敏捷》吧, :) 。
所以在他的架构里面,业务层和DAO层的关系不是你想的那样,而恰是相反。

不过楼主在执行思想的过程中,这思想还不够彻底。
业务的发起者在UI层吧,那业务接口没有放置到UI层,这就曾现出了“显示层->业务层<-数据层” 这样一个结果。 我觉得也挺好的。


DAO接口究竟该放在哪层,那是个概念的问题,“依赖于接口,不依赖于实现”,这才是一切的核心。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics