对领域驱动设计的初步认识(七)

10-12-11 flyzb
    DDD的方向无疑是正确的,但是看了Eric的DDD以及banq的jivejdon源码后不禁又有了一些疑问。

    "贫血模型"无疑是不对的,领域建模中的领域对象应该是有行为的。在jivejdon中看到的领域对象其实并没有多少行为,而大部分的领域行为都让Dao或者Repository占去了。这不仅让我有一种似曾相似的感觉,原来在service的那些方法以另外一种面貌出现在了Repository层中。从Dao或者Repository本来的字面意思上可以看出,Repository层仅仅是领域模型与数据库的隔离层,显然不应该包含领域逻辑的。

public class ForumServiceImp implements ForumService{
......
public void createForum(EventModel em) {
        Forum forum = (Forum)em.getModelIF();      
        logger.debug(" enter create Forum" );
        try {
            Long forumIDInt = sequenceDao.getNextId(Constants.FORUM);
            forum.setForumId(forumIDInt);
            
            //创建时间使用long字符串
            long dateTime = System.currentTimeMillis();
            forum.setCreationDate(Long.toString(dateTime));
            forum.setModifiedDate(Long.toString(dateTime));
            [u]forumDao.createForum(forum);[/u]
        } catch (Exception e) {
            logger.error(" createForum error: " + e);
           
        }
    }
....
}
<p>

    从以上代码可以看出,对于创建这个动作,Service并没有把它交给Forum对象,而是直接给了forumDao.在我看来,这是违背了DDD的本质的,应该由forum对象承接这个动作,然后才由领域对象再传给forumDao。

    也许有人说,CRUD可以由领域对象直接承接动作,那更复杂的场景应该怎么办呢。呵呵,其实答案很简单,因为这个问题的答案已经在问题中说了,就是“场景”。场景是让领域对象具有行为的实际生存条件,所以DCI才是DDD中对于领域对象行为建模的有益补充。

    我建议抛弃掉DDD中关于工厂的提法,只有这样才会让我们回归真实业务,那只是一种“创建场景”,而不仅仅是那所谓的“工厂模式”,因为复杂创建场景中还可能包含复杂的流程或者策略对象的东西。

public abstract class MessageDaoSql implements MessageDao {
......
public void createMessage(ForumMessage forumMessage) throws Exception {
		logger.debug("enter createTopicMessage for id:" + forumMessage.getMessageId());
		// differnce with createRpleyMessage: parentMessageID,

		String INSERT_MESSAGE = "INSERT INTO jiveMessage(messageID, threadID, forumID, "
				+ "userID, subject, body, modValue, rewardPoints, creationDate, modifiedDate) " + "VALUES(?,?,?,?,?,?,?,?,?,?)";
		List queryParams = new ArrayList();
		queryParams.add(forumMessage.getMessageId());
		queryParams.add(forumMessage.getForumThread().getThreadId());
		queryParams.add(forumMessage.getForum().getForumId());
		queryParams.add(forumMessage.getAccount().getUserId());
		MessageVO messageVO = forumMessage.getMessageVO();
		queryParams.add(messageVO.getSubject());
		queryParams.add(messageVO.getBody());
		queryParams.add(new Integer(0));
		queryParams.add(new Integer(messageVO.getRewardPoints()));

		long now = System.currentTimeMillis();
		String saveDateTime = ToolsUtil.dateToMillis(now);
		String displayDateTime = constants.getDateTimeDisp(saveDateTime);
		queryParams.add(saveDateTime);
		[u]forumMessage.setCreationDate(displayDateTime);[/u]

		queryParams.add(saveDateTime);
		[u]forumMessage.setModifiedDate(displayDateTime);[/u]

		try {
			jdbcTempSource.getJdbcTemp().operate(queryParams, INSERT_MESSAGE);
		} catch (Exception e) {
			logger.error(e);
			throw new Exception("messageId=" + forumMessage.getMessageId() + " happend " + e);
		}
	}
......
}
<p>

    以上“forumMessage.setCreationDate(displayDateTime);”部分应该属于领域业务逻辑,不应该出现在Dao中。

    另外,我也不太赞同DDD中关于聚合的做法,因为保证领域模型内部对象的一致性并不是只有通过层层封装才能做到。在我看来,这种层层封装并不是领域内部的业务场景的真实反应,反而造成领域内部逻辑的复杂和僵化。在jivejdon中,Dao、Factory、Builder、Kernel之间的命名关系一直让我困惑,至少感觉逻辑很沉重,与业务逻辑对应关系也不清爽。

    总结一下,DDD=Service+富领域的对象+基于事件的DCI+不含业务逻辑的仓储。

    呵呵,其实jivejdon有很多值得我学习的地方,希望大家共勉。

[该贴被flyzb于2010-12-11 22:04修改过]

              

3
zjsong
2010-12-12 12:20
2010年12月11日 21:59 "flyzb"的内容
对于创建这个动作,Service并没有把它交给Forum对象,而是直接给了forumDao.在我看来,这是违背了DDD的本质的,应该由forum对象承接这个动作,然后才由领域对象再传给forumDao ...

我觉得这里挺有道理的,但做起来很难,如果把所有的领域知识都给了领域模型,那么模型会不会很大?

banq
2010-12-12 14:44
2010年12月11日 21:59 "flyzb"的内容
对于创建这个动作,Service并没有把它交给Forum对象,而是直接给了forumDao ...

严格来说,这里创建应该给ForumFactory,这个你从ForumThread或ForumMessage的创建可以看出,而Forum的创建因为简单,就没有增加工厂这一环节。其实这一环节相当重要。

DDD也是建议使用工厂从仓储中创建实体对象。

但是我个人不认可有实体对象自己创建自己(自己都没诞生,怎么生自己呢,万物皆有母),在Spring一些系统中,经常委托实体对象的一个静态创建方法来创建自己,这合理解释就是在简单情况下省略工厂。

banq
2010-12-12 14:52
2010年12月11日 21:59 "flyzb"的内容
我建议抛弃掉DDD中关于工厂的提法,只有这样才会让我们回归真实业务,那只是一种“创建场景”,而不仅仅是那所谓的“工厂模式”,因为复杂创建场景中还可能包含复杂的流程或者策略对象的东西。 ...

关键是要分清楚实体对象自身的创建和实体扮演角色后的场景创建这两个事情,这是两个不同的东东。

DDD中工厂是有关实体对象自身和相关子对象,也就是聚合体创建,不是关于场景创建。

DDD可以说只是停留在静态的结构特征阶段(数据库属于静态时代原始人),还没有进入动态行为方面领域,这方面倒是很欠缺一个类似DDD的经典学说,可惜现在函数式语言和动态类型正在发展,估计还没有成熟,领域事件和DCI属于这个领域相对成熟的。

在JiveJdon中主要是以领域事件为主,因此没有破坏原来DDD的静态结构特征,但是很显然,这使得整个架构应当动态性质应用比较力不从心,除非你使用工作流或规则引擎将应用中动态性质分离,这也是过去成熟的做法。

但是未来这些都可能被DCI或函数式语言新技术全部打乱,进入一个以动态为主的设计新时代。

flyzb
2010-12-12 16:05
    准确来讲,领域对象创建自身也是不太好的。不过我还是建议在DDD中加入Context层,由Service负责把外界响应传给Context层,然后由Context层负责与领域对象和仓储层打交道。这也就是说由Context层负责创建领域对象,其中可以根据具体情况考虑选择用工厂模式、策略或者工作流之类的东西,对于一致性可以用对象间的事件响应来保证。

    目前,我感觉DDD中对工厂和仓储的定位不清,导致大量的业务逻辑散落在仓储层里。仓储只是一个领域模型获取和存储数据的接口,是不应该涉及业务逻辑的,比如上面我指出的在仓储中修改对象的问题。

    如果业务逻辑不放在仓储里,那应该放到哪里呢?显然,DDD中缺少了对场景层的定位,而工厂只是其中的一种简单的“创建场景”而已。所以我对于场景是动态还是静态的并不是特别关心,但关注的是如何把这一层的内容从仓储层剥离出来一样,其实这与DDD当初把业务逻辑从Service层剥离出来的初衷是一样的。这也就是说既然Service层是薄的,那么仓储层也应该是薄的,而只有Context层才应该是厚的。

[该贴被flyzb于2010-12-12 16:21修改过]

猜你喜欢
7Go 1 2 3 4 ... 7 下一页