该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-03-26
downpour 写道 很久以来,我一直反对Domain Model解决一切Logic问题。一个典型的业务场景是一个顺序的Process,这个Process对外表现就是Service Logic的接口。任何一个Process可以很简单,也可以很复杂,简单的Process或许只需要改变某个Domain Object的状态就行了,复杂的Process需要肢解成许多小的Process,并将他们组合起来完成整个逻辑,这个肢解的过程是递归的。这样一个过程是无法直接用Domain Logic来完成而需要一种“外力”来定义,那就是Service Logic.
如果一个操作牵涉到多个Domain Object,ROR是把这些代码写在Controller里面,而常见的Java Web Framework是写在所谓的Service里面,Controller或者Service就应该是你所谓的“外力” ROR的Controller除了写这些逻辑以外,它还负责了web flow的控制,直接根据各种逻辑代码计算完毕的结果,返回到不同的View。 在Java Web Framework里面,推荐的是Service对于web一无所知,然后由薄薄的一层Web Action来调用Service,根据Service的结果,然后返回到不同的View。 Java这样的做法层次清楚,扩展性强,但是代码行数会比较多,大部分Action代码只是简单的组装用户界面传递过来的数据,然后丢给Service而已。convention over configuration是一个趋势,比如从struts1 -> webwork (struts2),利用ognl省去了FormBean到Entity的数据组装代码。以后流行的Java Web Framework肯定会再省去web flow control这块,比如说用URL命名规则来替代(REST很有前途)。 Service还是需要保留的,Rich Domain Model不是要把所有的逻辑都写在Entity上,firebody前面提到的几点原则很不错: 引用 * entity 尽量不需要依赖外围Service组件 * 因为Entity增加了Query,Update的功能,那么原来很多只能在Service上作的代码可以相应作为Entity的领域逻辑而 移入到 Entity的领域方法中 ,使得 Service的代码大大减少,整个功能的逻辑很好的分离到Service 和entity层中。 * 属于Entity的逻辑代码 应该 有这么几个特征: # 依赖于本身的或者别的实体的状态 # 依赖于本身的或者别的实体的查找结果 (Query) # 修改实体的状态 (包括数据库的状态) 关于第一点,偶的意见是Entity“不能”依赖其他Service组件,就算在ROR里面,ActiveRecord也是不可能依赖于Controller的,偶个人严重抵制2个包的双向依赖。 |
|
返回顶楼 | |
发表时间:2007-03-26
一直有一个疑问,持久化逻辑转移到domain object后,难道不会产生large class,对代码的维护和修改造成影响?
在项目中实际上会存在一些职责特别重的类(表),整个业务链都是以它们为起点开始驱动俄。service分层可以分担它们的职责,而domain model如何解决这个问题? |
|
返回顶楼 | |
发表时间:2007-03-26
Readonly 写道 downpour 写道 很久以来,我一直反对Domain Model解决一切Logic问题。一个典型的业务场景是一个顺序的Process,这个Process对外表现就是Service Logic的接口。任何一个Process可以很简单,也可以很复杂,简单的Process或许只需要改变某个Domain Object的状态就行了,复杂的Process需要肢解成许多小的Process,并将他们组合起来完成整个逻辑,这个肢解的过程是递归的。这样一个过程是无法直接用Domain Logic来完成而需要一种“外力”来定义,那就是Service Logic.
如果一个操作牵涉到多个Domain Object,ROR是把这些代码写在Controller里面,而常见的Java Web Framework是写在所谓的Service里面,Controller或者Service就应该是你所谓的“外力” ROR的Controller除了写这些逻辑以外,它还负责了web flow的控制,直接根据各种逻辑代码计算完毕的结果,返回到不同的View。 在Java Web Framework里面,推荐的是Service对于web一无所知,然后由薄薄的一层Web Action来调用Service,根据Service的结果,然后返回到不同的View。 Java这样的做法层次清楚,扩展性强,但是代码行数会比较多,大部分Action代码只是简单的组装用户界面传递过来的数据,然后丢给Service而已。convention over configuration是一个趋势,比如从struts1 -> webwork (struts2),利用ognl省去了FormBean到Entity的数据组装代码。以后流行的Java Web Framework肯定会再省去web flow control这块,比如说用URL命名规则来替代(REST很有前途)。 Service还是需要保留的,Rich Domain Model不是要把所有的逻辑都写在Entity上,firebody前面提到的几点原则很不错: 引用 * entity 尽量不需要依赖外围Service组件 * 因为Entity增加了Query,Update的功能,那么原来很多只能在Service上作的代码可以相应作为Entity的领域逻辑而 移入到 Entity的领域方法中 ,使得 Service的代码大大减少,整个功能的逻辑很好的分离到Service 和entity层中。 * 属于Entity的逻辑代码 应该 有这么几个特征: # 依赖于本身的或者别的实体的状态 # 依赖于本身的或者别的实体的查找结果 (Query) # 修改实体的状态 (包括数据库的状态) 关于第一点,偶的意见是Entity“不能”依赖其他Service组件,就算在ROR里面,ActiveRecord也是不可能依赖于Controller的,偶个人严重抵制2个包的双向依赖。 几乎每句话都说到我心里去了。 Entity绝对不能依赖Service组件这一点是最重要的前提。不过我对于"依赖于本身的或者别的实体的查找结果 (Query)"这一点还有疑问。按照我的理解,针对关系数据库的查询是一个平面的东西,回想在SQL编辑器里面输入一句SQL语句,执行,无论语句多复杂,最终得到的都是一个平面的表格形式的返回列表。而页面的显示呢,其实也是一个平面的表格形式的内容。所以,为什么一定要将这两个原本表现形式就完全一致的东西非要弄到一个“立体”的对象中来呢?ORM在这里难道不是在绕弯路吗? 所以我个人认为还是应该出现一个类似于查询引擎的东西,绕过所有的Java Object,直接将页面显示与数据库Mapping起来。 |
|
返回顶楼 | |
发表时间:2007-03-26
dada 写道 一直有一个疑问,持久化逻辑转移到domain object后,难道不会产生large class,对代码的维护和修改造成影响?
在项目中实际上会存在一些职责特别重的类(表),整个业务链都是以它们为起点开始驱动俄。service分层可以分担它们的职责,而domain model如何解决这个问题? 即使 不使用 Rich domain model,这个问题我想还是存在的。 对于这样的庞大的业务实体类,同样有几个重构原则可以作为大家的参考: * 精简业务方法代码: 业务方法的逻辑是否大部分在处理当前对象的状态? 如果否,其中很多逻辑依赖于别的对象的状态, 可以考虑将这些代码移到 相应对象上。 * 分解业务方法到合适的类里面: 这是一个比较大的重构,需要慎重的考虑,不过处理得好的话,往往能够收到奇效,下面是一些指导的意见 # 分离一些行为到独立的业务类,这些独立的业务类并不作为entity,而仅仅作为分离业务逻辑代码的部分。形似 UserHelper,OrderHelper 之类的冬冬。 代码的例子如下: Class User{ public int calcAmount(){ return this.userHelper.calcAmount(this);} } 或者利用合适的隐喻,用一个“支付中心'来代表这些“计算支付“的细节 ,然后分离部分逻辑到“支付中心”去 payCenter.calcOrderAmount(order) . 如果为了方便外围层的合理调用,可以考虑这样的delegate 行为: order.calcAmount( this.payCenter.calcOrderAmount(this); } # 如果实体保存的状态过于庞大,并且因为保存太庞大的数据导致负责的职责过多, 可以考虑将实体分解为合适的 几个 “一对一“关联的实体对象或者 Embbeded也行。 这里可能还要考虑是否可以分解一个大表的结构的问题,如果不想分解,那么可以考虑 Embedded的映射。 分解状态到另外实体的依据应该参照业务方法所依赖的对象状态,并结合业务的角度来进行 合适的分解。 一样可以通过delegate行为达到合理里的行为,比如: order.calcAmount ( return this.orderState.calcAmount() ); |
|
返回顶楼 | |
发表时间:2007-03-26
引用 Service还是需要保留的,Rich Domain Model不是要把所有的逻辑都写在Entity上,firebody前面提到的几点原则很不错 “Rich Domain Model不是要把所有的逻辑都写在Entity” 没有人会这么想的吧, 相对于目前大部分的service只是简单crud操作, 把这些操作都放在rdo上我觉得没什么不妥当啊, 我感觉firebody说的entity自身依赖的说法, 更多的应该是针对区分哪些crud方法是否为static, 说白了就是减少持久层这块代码重复造成的逻辑冗余, 另外一些逻辑如发送邮件等等service当然需要更高层次的service来调用。 尽量使rdo的作用变大,特别吸收持久层相关的一些功能,这里在逻辑上和目前的主流做法没什么区别, 所谓的依赖和现在的情况也应该一致。 上面readonly的一段话观点与现在的主流做法没啥不同嘛. firebody的几个原则让我想到更多的是, 哪些方法该是 static 的. |
|
返回顶楼 | |
发表时间:2007-03-26
引用 如果一个操作牵涉到多个Domain Object,ROR是把这些代码写在Controller里面,而常见的Java Web Framework是写在所谓的Service里面,Controller或者Service就应该是你所谓的“外力” ROR的Controller除了写这些逻辑以外,它还负责了web flow的控制,直接根据各种逻辑代码计算完毕的结果,返回到不同的View。 在Java Web Framework里面,推荐的是Service对于web一无所知,然后由薄薄的一层Web Action来调用Service,根据Service的结果,然后返回到不同的View。 Java这样的做法层次清楚,扩展性强,但是代码行数会比较多,大部分Action代码只是简单的组装用户界面传递过来的数据,然后丢给Service而已。convention over configuration是一个趋势,比如从struts1 -> webwork (struts2),利用ognl省去了FormBean到Entity的数据组装代码。以后流行的Java Web Framework肯定会再省去web flow control这块,比如说用URL命名规则来替代(REST很有前途)。 Service还是需要保留的,Rich Domain Model不是要把所有的逻辑都写在Entity上,firebody前面提到的几点原则很不错: 关于第一点,偶的意见是Entity“不能”依赖其他Service组件,就算在ROR里面,ActiveRecord也是不可能依赖于Controller的,偶个人严重抵制2个包的双向依赖。 对于RoR来说,多个domain model的协作,如果有明显的业务逻辑操作,一般是在app/models/目录下面自己创建model对象,把这些协作按照逻辑划分到各自的model里面去,不应该写在controller里面。 对于web flow的控制,Java标准的做法是写在配置文件里面,例如xwork.xml,而RoR是CoC。对于动态的view,webwork是通过在Action代码里面设置变量,在xwork里面使用${...}使用这个变量计算view,而RoR是在controller代码里面指定url routing。由于RoR的url routing机制非常强大和灵活,所以RoR控制web flow方便而且灵活,扩展性也很好,在这项功能上面,RoR是把Java框架远远甩开了。 BTW:我个人是非常欣赏RoR的URL Routing功能的。 对于rich domain model的问题,我也同意readonly的观点,还是要有service层,对于entity,还是不能形成双向依赖。之所以不看好Java的rich domain model,无非两点原因: 1、IoC容器的能力限制了domain的rich能力 2、Java语法实现rich domain会导致domain model代码膨胀太大,表达能力变差。 当然也有人说不需要IoC容器,我用aspectj一样rich domain。用aspectj确实可以,aspectj从某种意义上来说,根本就改变了Java语言的语法,所以当然可以做到,只不过在缺乏必要设施的情况下,用aspectj的代价更高。 |
|
返回顶楼 | |
发表时间:2007-03-26
downpour 写道 Entity绝对不能依赖Service组件这一点是最重要的前提。不 过我对于"依赖于本身的或者别的实体的查找结果 (Query)"这一点还有疑问。 按照我的理解,针对关系数据库的查询是一个平面的东西,回想在SQL编辑器里面输入一句SQL语句,执行,无论语句多复杂,最终得到的都是一个平面的表格形式的返回列表。 而页面的显示呢,其实也是一个平面的表格形式的内容。 所以,为什么一定要将这两个原本表现形式就完全一致的东西非要弄到一个“立体”的对象中来呢? ORM在这里难道不是在绕弯路吗? 所以我个人认为还是应该出现一个类似于查询引擎的东西, 绕过所有的Java Object, 直接将页面显示与数据库Mapping起来。 没有人说要让“Entity绝对不能依赖Service组件”吧, ORM能处理大部分的普通需求,再甚保留对native sql的支持不是已经满足你的要求了,你说的查询引擎要作到什么样的效果呢,能否具体点, 目前的很多通用查询工具难道都不能满足吗,不存在所谓的平面问题吧, 就看你自己怎么想了,三维地图也是显示在你的平面显示器上的哦, 查询方面的OO本来就是为了复用吧,有复用就可以看做有引擎了哦,呵呵 |
|
返回顶楼 | |
发表时间:2007-03-26
complystill 写道 软件系统架构设计的进一步发展会引发出外部可执行逻辑进入本系统Domain Model, 在本地直接与Domain Object进行密集的交互, 然后回写信息的模式. 这有别于预先定义好一系列Service接口作为协议合同的老路, 属于目前还没有的一种软件架构, 我研究时把这种模式叫做 Hosting Based Interfacing - 基于东道的接合方式. 怎么感觉概念上有点类似 rest ,提供基础功能供调用,而不是提供菜单供点菜。 |
|
返回顶楼 | |
发表时间:2007-03-26
RichDomainModel并不是现在才冒出来的。无论采用什么OO语言,有没有框架支持RichDomainModel,都可以实现。
问题在于看待对象的方式,以及如何理解它们的协作,这才是根本问题。 |
|
返回顶楼 | |
发表时间:2007-03-26
partech 写道 RichDomainModel并不是现在才冒出来的。无论采用什么OO语言,有没有框架支持RichDomainModel,都可以实现。
问题在于看待对象的方式,以及如何理解它们的协作,这才是根本问题。 这里讨论的就是实现的问题,由于框架和语言本身的特点,导致实现上难易程度不一致,因而需要设计不同的RichDomainModel。 至于你说的根本问题那是另一个话题。 |
|
返回顶楼 | |