原文 http://www.jdon.com/jivejdon/thread/37289/15
在Evans DDD 实现过程中,经常会碰到实体和服务Service以及Repository交互过程,这个交互过程的实现是一个难点,也是容易造成失血贫血模型的主要途径。
因为实体的业务方法需要和服务或Reposirtoy打交道,如果把这个业务方法放入服务,就容易造成实体的贫血;但是如果把服务注射到实体中,也非常丑陋。这里提出一个中间处理模式:Domain Event,领域事件模式,这个模式也曾经被MF在文章Domain Event 专门章节提到。
2008年Udi Dahan在其博客How to create fully encapsulated Domain Models 一文中也提出这个问题,引起大家重视。
Udi Dahan的案例是游戏购物车:对于商品放入购物车有三个规则:
1. 只有三个游戏才能加入购物车
2. 购物车中的总数不能超过10.
3. 如果该客户报失丢失了自己的租金会员,没有游戏可以被添加
前面两个规则可以在实体模型中容易实现,但是第三个条件需要和服务打交道了。
class TradeInCart{ Account Account{get;} LineItem Add( Game game, IRepository<QueueHistory> repository, LoggingService service); ValidationResult CanAdd( Game game, IRepository<QueueHistory> repository, LoggingService service); IList<LineItems> LineItems{get;} } |
有一个号称是DDD 的框架:bastion,其作者的博客有些内容比较有意思,注意加粗部分
A domain should be ever-expanding, it knows no boundaries. Changes in requirements should lead to additions, not changes to the domain.
A domain should always be live, always active.
A domain should be able to execute business processes dynamically. As business processes change continually, a dynamic domain model is a better way to support them than a process model, which is static.
A domain should have no infrastructural dependencies (e.g. persistence, authentication, logging). Instead, it is surrounded by adapters listening to events happening in the domain. Adapters handle these events by calling on external services. As these services are independent of the domain, they can be made generic and therefore reusable between domains.
A domain should be modelled using the time-inversion pattern and the active-passive pattern. That is: start modelling behaviour at the end of the process (in the spirit of demand chain management), and model passive objects in the real world as active ones in the domain model.
原文地址:http://www.blog.dannynet.net/archives/125
>it is surrounded by adapters listening to events happening in the domain. Adapters handle these events by calling on external services.
非常棒,这里adapters listening实际就是Observer模式,监听者模式和观察者模式原理基本一样,可以理解为同一模式。
监 听者模式和事件模式是紧密联系的。通过监听模式引入,可以将领域模型和服务以前其他底层的一些操作进行松耦合,从另外一个角度来说,注射IOC模式对于解 决聚合性质的耦合比较擅长,对于非常活跃的事件模式,则GOF的行为模式中各个模式值得借鉴,其中包括Command模式和观察者模式。
由此也可以看出,用好DDD 的基础是GoF设计模式。
bastion这个开源DDD 框架虽然很简单,但是它对我的启发很大,特别是它将观察模式固化到Domain这个核心类中,用来辅助领域模型和外界的事件交互,通过Event和Message来实现模型和服务等外界交互,这个想法和我在Jdon框架中的异步 观察者模式有异曲同工之妙:
bastion的Domain中事件触发方法:
protected <T extends DomainMessage> T notifyInternal(T message) { List<Adapter> messageAdapters = adapters.get(message.getClass()); if (messageAdapters != null ) { for (Adapter<? super DomainMessage> adapter : messageAdapters) { adapter.handle(message); //激活每个监听者的handle方法 } } return message; } |
private void notifyObservers(Subscribed subscribed) { if (subscriptionObservable != null ) { Object[] args = new Object[] { subscribed }; subscriptionObservable.notifyObservers(args); //激活每个观察者 } } |
两者区别之处是:bastion将之鲜明整入Domain这个核心类中,而Jdon框架则没有如此显式和Domain挂钩,看来Jdon框架可以跨出这一步,因为这个Domain Event是非常重要的普遍的一个DDD 中解决方案。
>何时注册那些监听器?ACTION生成的时候?
bastion中是在threadlocal中开始注册的,也就是一个请求开始时,就是当前这个实体对象被创建时,将监听器注册到其中,因为一般实体对象都一直活着,在缓存 内存中,因此,这个实体对象以后还是可以继续加入新的监听者的。
我准备在Jdon框架中让监听器注册由框架自动完成,而不是现在由应用者自己完成,这样,会更方便。
如果框架能解决那最好,现有SPRING能实现自动注册吗?多个实体能共有一个注册吗?
其实可以在Jdon框架或Spring框架中直接使用bastion框架,bastion框架主要是让除了领域模型以外的组件模型成为其卫星,就象太阳是核心,其他都绕着太阳转。
补充:DDD的事件顺序图如下:
[该贴被banq于2009-10-15 09:30修改过]
bastion框架集成SPRING,有例子吗?
>bastion框架集成SPRING
应该可以,Spring其实是和Domain Model无关的技术框架,以Domain Model观点看来,Domain Model就是与计算机概念无关的,而Spring属于那种和计算机有关的概念。
bastion框架是计算机概念和领域模型的结合部位,所以,两者能够使用。
我目前发现JavAte比Bastion更加全面,对DOmain Events处理也更加丰富,可见:
JavAte
bastion框架中的适配监听器的触发机存在这样的问题:
领 域消息事件与适配监听器是一对多的关系,按照现在的设计,同一个消息事件可能会触发多个适配监听器,不甚合理。例如,A、B模块需要在同一个查询消息事件 下绑定各自的查询适配监听器,A模块的发送的查询消息事件同时会触发B模块的查询服务。是否需要为在同一消息事件下绑定的不同适配监听器设置标示符,从而 能够和领域消息事件中存储标示符匹配?
是的,我从Jdonframework 6.2开发中也感觉这个问题,原来设置观察者模式是一对多,结果发现使用变得复杂,现在改为一对一,简单。