该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-03-26
dada 写道 一直有一个疑问,持久化逻辑转移到domain object后,难道不会产生large class,对代码的维护和修改造成影响?
在项目中实际上会存在一些职责特别重的类(表),整个业务链都是以它们为起点开始驱动俄。service分层可以分担它们的职责,而domain model如何解决这个问题? 这个感觉可以通过Class Normalization去解决, 就像关系模型需要通过Decomposition从1NF获得2NF,再到3NF, 进一步还可以有4NF, BCNF等. Large Class也应通过类似的过程Decomposition进而优化结构, 尽量拆分. 如果说业务逻辑都集中到一张表上去的话, 相应的用Service对这么一张表进行操作也是会很复杂的. |
|
返回顶楼 | |
发表时间:2007-03-26
downpour 写道 很久以来,我一直反对Domain Model解决一切Logic问题。一个典型的业务场景是一个顺序的Process,这个Process对外表现就是Service Logic的接口。任何一个Process可以很简单,也可以很复杂,简单的Process或许只需要改变某个Domain Object的状态就行了,复杂的Process需要肢解成许多小的Process,并将他们组合起来完成整个逻辑,这个肢解的过程是递归的。这样一个过程是无法直接用Domain Logic来完成而需要一种“外力”来定义,那就是Service Logic. 这个从系统整体架构分析, 得出的结论是一致的. Domain Model是用来封装系统内部状态, 再现问题实质的. 另外对于Service, 宽泛的讲, 本质上任何用户界面和公开API都可以认为是系统对外提供的一种Service. 一个JSP页面也可以认为是一个Service, 它照样有 输入(Form Fields), 有 处理逻辑(Scriptlet), 也有 输出(结果页面). 用个代码的例子来说明, 比如响应用户操作, 删除一个Worker对象: ... if("delete".equals(request.getParameter("action"))) { long id = Long.parseLong(request.getParameter("id")); tob.get(id).die(); } ... 以上就是Service Logic. 而要检查被删除的Worker如果还有被分配的任务就不能删除的逻辑, 应该作为Domain Object的行为来实现: public class Worker extends TheObject { ... @Override protected void killingNotify() throws ObjectDependedException { if(this.tasks.size() > 0) throw new ObjectDependedException("This worker still has work assignment!"); } ... } 以上就是Domain Logic, 不应放在Service里实现. |
|
返回顶楼 | |
发表时间:2007-03-26
firebody 写道 要么全盘entity话。事务边界在外面基础设施作,比如filter,intercepter 或者action。
要么 servce 和 rich domain model并存。 我选择先一步步走的方式。 先 service 和 entity并存。 service充当事务和逻辑的组合者。 entity上增加了CRUD的功能,可以使得原本 “贫血模型“中的显得“臃肿“的Service的代码分离很多出去到 Entity中,但是这个分离 我觉得应该有几个指导原则,不然就有点混乱了,有几个指导原则可以给大家作为参考: * entity 尽量不需要依赖外围Service组件 * 因为Entity增加了Query,Update的功能,那么原来很多只能在Service上作的代码可以相应作为Entity的领域逻辑 而 移入到 Entity的领域方法中 ,使得 Service的代码大大减少,整个功能的逻辑很好的分离到Service 和entity层中。 * 属于Entity的逻辑代码 应该 有这么几个特征: # 依赖于本身的或者别的实体的状态 # 依赖于本身的或者别的实体的查找结果 (Query) # 修改实体的状态 (包括数据库的状态) # 如果一个复杂功能需要几个外部组件的完成,Service 负责这些外部组件的调用。 rich domain model的目的是什么? 我想最主要还是为了稳定,能灵活的应对各种变化吧. 1.那么业务是多变的,所以domain model不要含有业务逻辑.但是domain model又恰恰是为了解决业务问题,没有业务问题,也不会有domain model,所以rich domain model要面对的是提取或是抽象出让它对付的业务问题. 引用 依赖于本身的或者别的实体的状态
我把firebody说的实体的状态理解成为我说的抽象出让它对付的业务问题 2.rich domain model依赖于数据库,orm吗?我想为了rich的目的,让它去吧,没什么了不起.至少不会比依赖于业务的细节严重 3.如果按照TDD的经典做法,抽象出业务问题的过程,应该是先把业务逻辑写到domain model去,然后臭味->重构,臭味->重构....直到臭味很少很少 |
|
返回顶楼 | |
发表时间:2007-03-26
nihongye 写道 firebody 写道 要么全盘entity话。事务边界在外面基础设施作,比如filter,intercepter 或者action。
要么 servce 和 rich domain model并存。 我选择先一步步走的方式。 先 service 和 entity并存。 service充当事务和逻辑的组合者。 entity上增加了CRUD的功能,可以使得原本 “贫血模型“中的显得“臃肿“的Service的代码分离很多出去到 Entity中,但是这个分离 我觉得应该有几个指导原则,不然就有点混乱了,有几个指导原则可以给大家作为参考: * entity 尽量不需要依赖外围Service组件 * 因为Entity增加了Query,Update的功能,那么原来很多只能在Service上作的代码可以相应作为Entity的领域逻辑 而 移入到 Entity的领域方法中 ,使得 Service的代码大大减少,整个功能的逻辑很好的分离到Service 和entity层中。 * 属于Entity的逻辑代码 应该 有这么几个特征: # 依赖于本身的或者别的实体的状态 # 依赖于本身的或者别的实体的查找结果 (Query) # 修改实体的状态 (包括数据库的状态) # 如果一个复杂功能需要几个外部组件的完成,Service 负责这些外部组件的调用。 rich domain model的目的是什么? 我想最主要还是为了稳定,能灵活的应对各种变化吧. 1.那么业务是多变的,所以domain model不要含有业务逻辑.但是domain model又恰恰是为了解决业务问题,没有业务问题,也不会有domain model,所以rich domain model要面对的是提取或是抽象出让它对付的业务问题. 引用 依赖于本身的或者别的实体的状态
我把firebody说的实体的状态理解成为我说的抽象出让它对付的业务问题 2.rich domain model依赖于数据库,orm吗?我想为了rich的目的,让它去吧,没什么了不起.至少不会比依赖于业务的细节严重 3.如果按照TDD的经典做法,抽象出业务问题的过程,应该是先把业务逻辑写到domain model去,然后臭味->重构,臭味->重构....直到臭味很少很少 TDD得时候,这样进地进行很自然而然的,因为 测试时候的事务是统一管理的。 当然,也不阻碍针对 Service的测试。 |
|
返回顶楼 | |
发表时间:2007-03-27
complystill 写道 downpour 写道 很久以来,我一直反对Domain Model解决一切Logic问题。一个典型的业务场景是一个顺序的Process,这个Process对外表现就是Service Logic的接口。任何一个Process可以很简单,也可以很复杂,简单的Process或许只需要改变某个Domain Object的状态就行了,复杂的Process需要肢解成许多小的Process,并将他们组合起来完成整个逻辑,这个肢解的过程是递归的。这样一个过程是无法直接用Domain Logic来完成而需要一种“外力”来定义,那就是Service Logic. 这个从系统整体架构分析, 得出的结论是一致的. Domain Model是用来封装系统内部状态, 再现问题实质的. 另外对于Service, 宽泛的讲, 本质上任何用户界面和公开API都可以认为是系统对外提供的一种Service. 一个JSP页面也可以认为是一个Service, 它照样有 输入(Form Fields), 有 处理逻辑(Scriptlet), 也有 输出(结果页面). 用个代码的例子来说明, 比如响应用户操作, 删除一个Worker对象: ... if("delete".equals(request.getParameter("action"))) { long id = Long.parseLong(request.getParameter("id")); tob.get(id).die(); } ... 以上就是Service Logic. 而要检查被删除的Worker如果还有被分配的任务就不能删除的逻辑, 应该作为Domain Object的行为来实现: public class Worker extends TheObject { ... @Override protected void killingNotify() throws ObjectDependedException { if(this.tasks.size() > 0) throw new ObjectDependedException("This worker still has work assignment!"); } ... } 以上就是Domain Logic, 不应放在Service里实现. 你的观点应该和我差不多,都将“是否归结为系统内部状态”作为划分Service Logic和Domain Logic的标准之一,但是对于你上面的代码,我无法苟同。 "要检查被删除的Worker如果还有被分配的任务就不能删除的逻辑, 应该作为Domain Object的行为来实现 ", 你将他封装在Domain Logic中,我认为这个完全不符合标准。或许这个例子比较极端,我想请问,如果在现实的业务场景中,出现的情况稍微变化:"要检查被删除的Worker如果还有被分配的,日期大于某天,且状态介于未提交到已提交之间的任务,就不能被删除",那么你的代码又如何?是不是打算循环你的this.tasks来解决? 如果你打算循环tasks来解决,那么我也无话可说,不过对于我来说,我可能会采用在Service Logic中调用一次数据库查询来解决问题,不仅效率高,而且逻辑清晰。 |
|
返回顶楼 | |
发表时间:2007-03-27
jianfeng008cn 写道 downpour 写道 Entity绝对不能依赖Service组件这一点是最重要的前提。不 过我对于"依赖于本身的或者别的实体的查找结果 (Query)"这一点还有疑问。 按照我的理解,针对关系数据库的查询是一个平面的东西,回想在SQL编辑器里面输入一句SQL语句,执行,无论语句多复杂,最终得到的都是一个平面的表格形式的返回列表。 而页面的显示呢,其实也是一个平面的表格形式的内容。 所以,为什么一定要将这两个原本表现形式就完全一致的东西非要弄到一个“立体”的对象中来呢? ORM在这里难道不是在绕弯路吗? 所以我个人认为还是应该出现一个类似于查询引擎的东西, 绕过所有的Java Object, 直接将页面显示与数据库Mapping起来。 没有人说要让“Entity绝对不能依赖Service组件”吧, ORM能处理大部分的普通需求,再甚保留对native sql的支持不是已经满足你的要求了,你说的查询引擎要作到什么样的效果呢,能否具体点, 目前的很多通用查询工具难道都不能满足吗,不存在所谓的平面问题吧, 就看你自己怎么想了,三维地图也是显示在你的平面显示器上的哦, 查询方面的OO本来就是为了复用吧,有复用就可以看做有引擎了哦,呵呵 Entity绝对不能依赖Service组件是我的观点,因为我认为Service组件存在的意义就是定义各种Process来规划各种Entity状态的维护顺序。如果Entity对Service组件形成了依赖,那么必然造成双向依赖,这个是我无法接受的。 另外对于查询来说,我想你还没有明白我的意思,我不是说ORM做的不好。而是我在想这样一个问题,为什么对于数据库的查询要经过ORM这样一个过程?无论是hibernate的hql或者native sql,他们作为引擎时,都会将查询的结果映射成Java世界中的对象,即使是iBatis这样的SQL Map工具,也一样无法摆脱这一俗套。 我承认,在解决了lazy load的问题之后,orm的查询方式更加具备扩展性。但是事实上呢?这种扩展性是以效率为代价而产生的。回想数据在数据库中的表现形式和在页面或者报表中的表现形式,他们是极其一致的,为什么不考虑类似微软的dataTable的方式来解决这个问题呢?绕过所有的对象映射,恢复这些数据的本来面目。 |
|
返回顶楼 | |
发表时间:2007-03-27
downpour 写道 jianfeng008cn 写道 downpour 写道 Entity绝对不能依赖Service组件这一点是最重要的前提。不 过我对于"依赖于本身的或者别的实体的查找结果 (Query)"这一点还有疑问。 按照我的理解,针对关系数据库的查询是一个平面的东西,回想在SQL编辑器里面输入一句SQL语句,执行,无论语句多复杂,最终得到的都是一个平面的表格形式的返回列表。 而页面的显示呢,其实也是一个平面的表格形式的内容。 所以,为什么一定要将这两个原本表现形式就完全一致的东西非要弄到一个“立体”的对象中来呢? ORM在这里难道不是在绕弯路吗? 所以我个人认为还是应该出现一个类似于查询引擎的东西, 绕过所有的Java Object, 直接将页面显示与数据库Mapping起来。 没有人说要让“Entity绝对不能依赖Service组件”吧, ORM能处理大部分的普通需求,再甚保留对native sql的支持不是已经满足你的要求了,你说的查询引擎要作到什么样的效果呢,能否具体点, 目前的很多通用查询工具难道都不能满足吗,不存在所谓的平面问题吧, 就看你自己怎么想了,三维地图也是显示在你的平面显示器上的哦, 查询方面的OO本来就是为了复用吧,有复用就可以看做有引擎了哦,呵呵 Entity绝对不能依赖Service组件是我的观点,因为我认为Service组件存在的意义就是定义各种Process来规划各种Entity状态的维护顺序。如果Entity对Service组件形成了依赖,那么必然造成双向依赖,这个是我无法接受的。 另外对于查询来说,我想你还没有明白我的意思,我不是说ORM做的不好。而是我在想这样一个问题,为什么对于数据库的查询要经过ORM这样一个过程?无论是hibernate的hql或者native sql,他们作为引擎时,都会将查询的结果映射成Java世界中的对象,即使是iBatis这样的SQL Map工具,也一样无法摆脱这一俗套。 我承认,在解决了lazy load的问题之后,orm的查询方式更加具备扩展性。但是事实上呢?这种扩展性是以效率为代价而产生的。回想数据在数据库中的表现形式和在页面或者报表中的表现形式,他们是极其一致的,为什么不考虑类似微软的dataTable的方式来解决这个问题呢?绕过所有的对象映射,恢复这些数据的本来面目。 没有人说要让“Entity绝对不能依赖Service组件”吧 这句打错了 呵呵 应该是:没有人说要让“Entity依赖Service组件”吧。 你的查询的意思我想我还是明白的,对象的方式只是提供了大部分的普通情况下的使用而已,他带来的是好处,而不是带来了效率上的问题,最主要的是你有选择的权利, 你说的ms的dataset这样的的东西,jdbc本身也不是禁止你这样用,虽然不尽相同, 实际上大量的复杂统计报表大家还是使用这样的方式的吧,所以我说你这个问题也是有点奇怪的。 揣摩你的意思,我感觉你是在这方面感觉有点烦了,ms的技术工具等是很统一, 对比着来看的确很多地方是ms的方便,甚至连选择都不用你做了, 这个问题发发这方面的牢骚我肯定没意见的 |
|
返回顶楼 | |
发表时间:2007-03-27
youcai 写道 partech 写道 RichDomainModel并不是现在才冒出来的。无论采用什么OO语言,有没有框架支持RichDomainModel,都可以实现。
问题在于看待对象的方式,以及如何理解它们的协作,这才是根本问题。 这里讨论的就是实现的问题,由于框架和语言本身的特点,导致实现上难易程度不一致,因而需要设计不同的RichDomainModel。 至于你说的根本问题那是另一个话题。 如此说来,我认为该倒过来,DomainModel为基础,各种语言需要来适配它,而不是Domain来适配不同的语言框架,因为DomainModel是基础,所以不会出现不同的DomainModel。实际上,我从来不认为不同语言和框架会对DomainModel产生重大影响,总可以找到方法来解决棘手的问题。 |
|
返回顶楼 | |
发表时间:2007-03-27
nihongye 写道 firebody 写道 要么全盘entity话。事务边界在外面基础设施作,比如filter,intercepter 或者action。
要么 servce 和 rich domain model并存。 我选择先一步步走的方式。 先 service 和 entity并存。 service充当事务和逻辑的组合者。 entity上增加了CRUD的功能,可以使得原本 “贫血模型“中的显得“臃肿“的Service的代码分离很多出去到 Entity中,但是这个分离 我觉得应该有几个指导原则,不然就有点混乱了,有几个指导原则可以给大家作为参考: * entity 尽量不需要依赖外围Service组件 * 因为Entity增加了Query,Update的功能,那么原来很多只能在Service上作的代码可以相应作为Entity的领域逻辑 而 移入到 Entity的领域方法中 ,使得 Service的代码大大减少,整个功能的逻辑很好的分离到Service 和entity层中。 * 属于Entity的逻辑代码 应该 有这么几个特征: # 依赖于本身的或者别的实体的状态 # 依赖于本身的或者别的实体的查找结果 (Query) # 修改实体的状态 (包括数据库的状态) # 如果一个复杂功能需要几个外部组件的完成,Service 负责这些外部组件的调用。 rich domain model的目的是什么? 我想最主要还是为了稳定,能灵活的应对各种变化吧. 1.那么业务是多变的,所以domain model不要含有业务逻辑.但是domain model又恰恰是为了解决业务问题,没有业务问题,也不会有domain model,所以rich domain model要面对的是提取或是抽象出让它对付的业务问题. 引用 依赖于本身的或者别的实体的状态
我把firebody说的实体的状态理解成为我说的抽象出让它对付的业务问题 2.rich domain model依赖于数据库,orm吗?我想为了rich的目的,让它去吧,没什么了不起.至少不会比依赖于业务的细节严重 3.如果按照TDD的经典做法,抽象出业务问题的过程,应该是先把业务逻辑写到domain model去,然后臭味->重构,臭味->重构....直到臭味很少很少 说道DomainModel里的持久化对象,也就是上面说的entity,我更愿意叫它DomainObject,entity这个词容易让人思路狭隘,业务逻辑是基础,持久化不过是一个扩展而已。DomainObject不一定非得持久化。但持久化的目标对象总是DomainObject。 其实DomainObject也可以组合业务逻辑,如果你就把它就看作这个概念的话。 Service主要不是负责组合业务逻辑的,正如complystill所说,Service是个外观。就像是联系人/翻译官一样,可以完全不知道该如何处理问题,只需要向别人传递就可以了,沟通才是它的主要职责。 |
|
返回顶楼 | |
发表时间:2007-03-27
看到一堆的代码就很头大,lz干么不搞个精简的UML图呢,如果要看仔细的,可以直接看代码
|
|
返回顶楼 | |