该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-03-25
robbin 写道 Martin Fowler的POEAA是2002年出版的,写作时间估计是2001年底,那时候Hibernate刚刚诞生,JDO1.0标准都没有出来。 现在时代已经完全不同了,O/R Mapping已经如此流行,而且像Spring这种可以进行完善的数据库资源管理和容器事务控制的轻量级容器也出现了,Marin的的书中提出的概念和解决方案并不完全适用于现在的流行框架了。我到建议大家去看without EJB,更加具有实用价值。
研究研究也好,至少知道ORM都做了什么事情。 |
|
返回顶楼 | |
发表时间:2005-03-25
当看到一个service里面既包含业务方法,又看到save或find这样的方法时,
一般都会说这个类集合了业务逻辑和持续化逻辑的职责,应该分开。可是大家 也说好多项目建模出来就是一堆crud,难道这样的service不正好反映吗? 比方说客户说我想查询每天新到的订单,然后...,那么这个find 本身就是一个业务活动,这个活动可能涉及到持续化手段。至于他是否可以做 这个活动,这个活动的事务等等应该放在这个service之上的应用层 贫血的分析模型必然出来贫血的设计模型。 |
|
返回顶楼 | |
发表时间:2005-03-25
sevenbamboos 写道 当看到一个service里面既包含业务方法,又看到save或find这样的方法时,
一般都会说这个类集合了业务逻辑和持续化逻辑的职责,应该分开。可是大家 也说好多项目建模出来就是一堆crud,难道这样的service不正好反映吗? 比方说客户说我想查询每天新到的订单,然后...,那么这个find 本身就是一个业务活动,这个活动可能涉及到持续化手段。至于他是否可以做 这个活动,这个活动的事务等等应该放在这个service之上的应用层 贫血的分析模型必然出来贫血的设计模型。 sevenbamboos已经领悟了《分析模式》? 我也隐约有种怀疑,Martin Fowler的RichDomainModel是否应该是和他所使用的分析模式相配合的? 本人能力所限,目前尚不能完全看懂《分析模式》,sevenbamboos能否说说? |
|
返回顶楼 | |
发表时间:2005-03-25
引用 Criteria的条件,我认为最好不要作为DAO接口的参数,正如你说的太复杂了不好测试。
接口参数使用criteria,dao的实现绑定在hibernate了.同样的还有传hql,或者sql也是这种情况。 |
|
返回顶楼 | |
发表时间:2005-03-25
ddd.bmp这副图是用什么方法画出来的?n多调查?
|
|
返回顶楼 | |
发表时间:2005-03-27
robbin 写道 既然大家都统一了观点,那么就有了一个很好的讨论问题的基础了。Martin Fowler的Domain Model,或者说我们的第二种模型难道是完美无缺的吗?当然不是,接下来我就要分析一下它的不足,以及可能的解决办法,而这些都来源于我个人的实践探索。
在第二种模型中,我们可以清楚的把这4个类分为三层: 1、实体类层,即Item,带有domain logic的domain object 2、DAO层,即ItemDao和ItemDaoHibernateImpl,抽象持久化操作的接口和实现类 3、业务逻辑层,即ItemManager,接受容器事务控制,向Web层提供统一的服务调用 在这三层中我们大家可以看到,domain object和DAO都是非常稳定的层,其实原因也很简单,因为domain object是映射数据库字段的,数据库字段不会频繁变动,所以domain object也相对稳定,而面向数据库持久化编程的DAO层也不过就是CRUD而已,不会有更多的花样,所以也很稳定。 问题就在于这个充当business workflow facade的业务逻辑对象,它的变动是相当频繁的。业务逻辑对象通常都是无状态的、受事务控制的、Singleton类,我们可以考察一下业务逻辑对象都有哪几类业务逻辑方法: 第一类:DAO接口方法的代理,就是上面例子中的loadItemById方法和findAll方法。 ItemManager之所以要代理这种类,目的有两个:向Web层提供统一的服务调用入口点和给持久化方法增加事务控制功能。这两点都很容易理解,你不能既给Web层程序员提供xxxManager,也给他提供xxxDao,所以你需要用xxxManager封装xxxDao,在这里,充当了一个简单代理功能;而事务控制也是持久化方法必须的,事务可能需要跨越多个DAO方法调用,所以必须放在业务逻辑层,而不能放在DAO层。 但是必须看到,对于一个典型的web应用来说,绝大多数的业务逻辑都是简单的CRUD逻辑,所以这种情况下,针对每个DAO方法,xxxManager都需要提供一个对应的封装方法,这不但是非常枯燥的,也是令人感觉非常不好的。 第二类:domain logic的方法代理。就是上面例子中placeBid方法。虽然Item已经有了placeBid方法,但是ItemManager仍然需要封装一下Item的placeBid,然后再提供一个简单封装之后的代理方法。 这和第一种情况类似,其原因也一样,也是为了给Web层提供一个统一的服务调用入口点和给隐式的持久化动作提供事务控制。 同样,和第一种情况一样,针对每个domain logic方法,xxxManager都需要提供一个对应的封装方法,同样是枯燥的,令人不爽的。 第三类:需要多个domain object和DAO参与协作的business workflow。这种情况是业务逻辑对象真正应该完成的职责。 在这个简单的例子中,没有涉及到这种情况,不过大家都可以想像的出来这种应用场景,因此不必举例说明了。 通过上面的分析可以看出,只有第三类业务逻辑方法才是业务逻辑对象真正应该承担的职责,而前两类业务逻辑方法都是“无奈之举”,不得不为之的事情,不但枯燥,而且令人沮丧。 分析完了业务逻辑对象,我们再回头看一下domain object,我们要仔细考察一下domain logic的话,会发现domain logic也分为两类: 第一类:需要持久层框架隐式的实现透明持久化的domain logic,例如Item的placeBid方法中的这一句: this.getBids();.add(newBid);; 上面已经着重提到,虽然这仅仅只是一个Java集合的添加新元素的操作,但是实际上通过事务的控制,会潜在的触发两条SQL:一条是insert一条记录到bid表,一条是更新item表相应的记录。如果我们让Item脱离Hibernate进行单元测试,它就是一个单纯的Java集合操作,如果我们把他加入到Hibernate框架中,他就会潜在的触发两条SQL,这就是隐式的依赖于持久化的domain logic。 特别请注意的一点是:在没有Hibernate/JDO这类可以实现“透明的持久化”工具出现之前,这类domain logic是无法实现的。 对于这一类domain logic,业务逻辑对象必须提供相应的封装方法,以实现事务控制。 第二类:完全不依赖持久化的domain logic,例如readonly例子中的Topic,如下: class Topic { boolean isAllowReply() { Calendar dueDate = Calendar.getInstance(); dueDate.setTime(lastUpdatedTime); dueDate.add(Calendar.DATE, forum.timeToLive); Date now = new Date(); return now.after(dueDate.getTime()); } } 注意这个isAllowReply方法,他和持久化完全不发生一丁点关系。在实际的开发中,我们同样会遇到很多这种不需要持久化的业务逻辑(主要发生在日期运算、数值运算和枚举运算方面),这种domain logic不管脱离不脱离所在的框架,它的行为都是一致的。对于这种domain logic,业务逻辑层并不需要提供封装方法,它可以适用于任何场合。 robbin 写道 业务逻辑对象通常都是无状态的、受事务控制的、Singleton类
不太同意Robbin的看法 业务逻辑应该是有状态的才对,没有状态如何执行企业逻辑? 而且业务逻辑应该是和事务无关的,对事物透明的。如果关心事务,就不应该是企业逻辑了。 业务逻辑非要是singleton的吗?不同意,不知道为何有此一说。 基本同意第二种模型,但XXXManager不应该属于业务逻辑,按照Domain Driven的看法,应该是基于业务逻辑的Service层,但不是业务逻辑。事务应该在这一层组织,或者在更上一层的Facade层组织。 Domain Object执行企业逻辑,并保存业务逻辑的状态,这是Domain Driven Design所推荐的做法。如何保存,不应该和具体的技术相关。 Domain Object为什么不能够引用Dao层的接口?我和Robbin的看法不同。我觉得是可以的,至于Dao是用Hibernate还是其他的技术实现,没有关系,不同的接口实现而已,Domain Object不关心。 |
|
返回顶楼 | |
发表时间:2005-03-29
同意第二种模型。我觉得Domain Object中不应该放入依赖其他服务的业务逻辑,如DAO(依赖数据库连接)、JMS(依赖JMS Server)或者更多的通过JNDI获取的服务。因为通常我都希望它可以穿透各个层进行传递,也希望它可以被序列化进行服务器之间的传递。
|
|
返回顶楼 | |
发表时间:2005-03-29
推荐大家都看看 DDD 中,是怎样描述 Domain Object 和相关的实现方案吧。
第二种模式,在复杂业务中,会逐渐蜕化为所谓的 贫血模式 。 |
|
返回顶楼 | |
发表时间:2005-03-29
七彩狼 写道 推荐大家都看看 DDD 中,是怎样描述 Domain Object 和相关的实现方案吧。
第二种模式,在复杂业务中,会逐渐蜕化为所谓的 贫血模式 。 第二种模式中如果不依赖其他服务,实际可以加入的业务逻辑实在有限。在复杂业务中可能确实会如你所说蜕化为贫血模式。能不能略微简单的描述一下DDD中对Domain Object的描述和实现方案呢? 我一直采用的是贫血模式,不过觉得它还是挺方便的,可以在各个层次传递,在服务器之间传递,让我在开发过程中省了很多事。 |
|
返回顶楼 | |
发表时间:2005-03-31
很久不来,讨论很热烈。但是看到Robbin的总结后有点晕,老马的意思被误解了好多,我来简单说一下:
1. Tansaction Script Organizes business logic by procedures where each procedure handles a single request from the presentation. 它的特点是making calls directly to the database or through a thin database wrapper. 跟Robbin总结的模型1相去甚远。 2. Domain Model An object model of the domain that incorporates both behavior and data. Domain Model 分两种: 2.1 Simple Domain Model (Active Record) 它的特点是POJO和TABLE STRUCTURE一一对应,建模based on数据库设计。Hibernate走的就是这个路子,但老马说它贫血,意思就是指它只有data,没有behavior。 在这种模式下,POJO自己的CRUD操作都应该放在自己的类里面。其他复杂的业务逻辑会放到外面的Service layer。 2.2 Rich Domain Model (Data Mapper) 它的特点是POJO和TABLE STRUCTURE并不一一对应,建模天马行空,完全取决于业务逻辑。domain object和table之间的mapping,由Data Mapper完成,domain object不用管数据表是何等结构,甚至不用管Data Mapper怎么操作。 当然,这种domain object也是要data和behavior并存的。 以上是我的粗浅理解,有不当之处大家讨论。今天下班已经晚了20分钟,明天若有空也贴贴代码吧。:) |
|
返回顶楼 | |