精华帖 (0) :: 良好帖 (14) :: 新手帖 (1) :: 隐藏帖 (3)
|
|
---|---|
作者 | 正文 |
发表时间:2010-01-26
最后修改:2010-01-26
0.2版在修正了0.1版错误的基础新增了验证码组件、DomainEvent框架和工具类。地址是:http://code.google.com/p/basicaidedcomponent/,欢迎大家前往下载试用。同时我在Google注册了一个论坛http://groups.google.com/group/bac_china/,不过访问总是时灵时不灵的。 说完了框架发布,我在这里谈一下0.2中比较特别的DomainEvent框架。这也是希望大家不要投新手帖。在这里发布自有框架,总会被贴上几个新手帖。一个弄不好就被丢到新手区了。 DomainEvent框架是为了解决实体与服务的交互而提出来的。在我们日常开发中,领域对象和Service的交互有时候需要领域对象调用Service。 比如我们有一个班级类,班级里有List保存学生对象。现在要求有一个统计学生的功能。如果完全按照领域驱动设计,这个功能应该是班级类自己的工作。如果所有的对象都在内存中,倒也没什么,直接统计一下List里学生对象数量就是。 public class Clazz { //学生 List students; public int getStudentSize() { return students.size(); } } 但是实际上这些对象都是保存在数据库中的。我们需要使用ORM工具获取Clazz,而一般学生列表是延迟加载的。在读取students列表时,ORM再加载学生实例。如果我们需要学生列表数据也罢了,但是如果我们只是要一个学生数量,那么将所有的学生对象加载就是非常不合算的事情。也许有人说写查询语句直接让数据库统计一下就是,比如下面: select count(s) from student s where s.clazz.id = ? 但是问题是如何调用呢?总不能写成下面这种代码 public class Clazz { //学生 List students; //一个学生Service对象,用于处理学生相关业务 StudentService service; public int getStudentSize() { return service.getStudentSize(this.id); } } 很明显,实体和服务产生了紧耦合。先不说我们如何把StudentService注入Clazz的实例中,就是可以注入,产生的紧耦合也使得Clazz和StudentService绑到了一块。如果写成单独的Dao或者Service方法,那么就可能造成Clazz只是一个纯粹的持久化对象,没有了任何业务。 2008年Udi Dahan在其博客How to create fully encapsulated Domain Models一文中也提出这个问题。一个实体模型中的方法参数依赖服务或者Repository,那么就会造成实体和服务的紧耦合。 2009年6月14日作者在征询很多意见后,再次在其博客Domain Events – Salvation提出了Domain Event的解决方案。采用了事件/消息模型对实体和服务进行解耦。 定义一个事件接口 public interface DomainEvent<T> { /** * 获得发生Event的对象 * @return Event的对象 * @since 0.2 */ public T getSource(); /** * 获得Event类型 * @return Event类型 * @since 0.2 */ public String getType(); } 然后定义监听器接口 public interface DomainListener { /** * 处理事件 * @param event 事件 * @since 0.2 */ public void handler(DomainEvent event); } 现在我们可以实现一个Listenrer,把查询学生人数的代码放到监听器里。 public class StudentListener implements DomainListener { StudentService service; public void handler(DomainEvent event) { if (event.getType().equlas("getStudentSize") { int size = service.getStudentSize(event.getSource().getId()); event.getSource().setStudentSize(size); } } } 然后Clazz只要在需要查询人数的时候发出一个事件。 public class CLazz{ int studentSize; public void getStudentSize() { DomainEventManager.disptcher(new DefaultDomainEvent(this, "getStudentSize")); } } //一个简单的DomainEvent实现 public class DefaultDomainEvent implements DomainEvent { private Object object; private String type; public DefaultDomainEvent(Object object, String type) { this.object = object; this.type = type; this.sync = sync; } public Object getSource() { return object; } public String getType() { return type; } } 监听器在收到事件后就会自动调用代码进行查询,并把查询结果放到Clazz中。这里只进行一些简单描述。大家想看完整的DomainEvent代码,可以下载我的BAC框架。 disptcher会将事件交给事件管理器,事件管理器是一个Command模式,它调用Listener对事件进行处理。 public class DomainEventManager{ /** * 已注册的监听器集合 */ private final static Map<Class, List<DomainListener>> listenerMap = new ConcurrentHashMap<Class, List<DomainListener>>(); /** * 处理事件 * @param event * @since 0.2 */ public static void dispatch(DomainEvent event) { //如果存在该类的监听器 if (hasClassListener(event.getSource().getClass())) { List<DomainListener> listenerList = getClassListeners(event.getSource().getClass()); //循环监听器,这是一个典型的Command模式 for (DomainListener listener : listenerList) { listener.handler(event); } } } } 这样实体和服务就分开了。不会造成实体和服务的紧耦合。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-01-27
最后修改:2010-01-27
简单问题复杂化,而且这样Class也没什么东西呀,还不是调用别的类完成,只是Service、DAO还是DomainEvent名字的区别而已。
我见过一个,在Class中调用DAO,就是这样 Service->Class->DAO |
|
返回顶楼 | |
发表时间:2010-01-27
最后修改:2010-01-27
mycybyb 写道 简单问题复杂化,而且这样Class也没什么东西呀,还不是调用别的类完成,只是Service、DAO还是DomainEvent名字的区别而已。
我见过一个,在Class中调用DAO,就是这样 Service->Class->DAO Class里面怎么没东西呢?getStudentSize这个方法在领域模型中是属于Class的。所有对此的访问应该是基于对Class的访问。如果你觉得我简单问题复杂化,我不太明白。可能我举的例子不太合适吧。有些业务Class自己能判断,有些业务需要调用其他Service。DomainEvent解决的就是如何调用的问题。 Class中调用DAO很明显是一个错误的结构,二者紧耦合了。而且Dao要在什么时候注入Class?这些都会增加复杂性。 我也承认我这里的代码还比较初级。有些DomainEvent框架已经做到了通过注解来调用监听器了。 |
|
返回顶楼 | |
发表时间:2010-01-27
魔力猫咪 写道 Class里面怎么没东西呢? 不好意思,这个我理解错了 |
|
返回顶楼 | |
发表时间:2010-01-27
魔力猫咪 写道 Class中调用DAO很明显是一个错误的结构,二者紧耦合了。如果Class被分布处理、WebService调用、串行化等等会如何呢?而且Dao要在什么时候注入Class?这些都会增加复杂性。 我也承认我这里的代码还比较初级。有些DomainEvent框架已经做到了通过注解来调用监听器了。 我倒认为是个很好的方法。而且这个DAO也不一定要注入。我对领域模型不是很熟,好像有些什么工厂和仓库之类的东西负责类的创建和数据访问。 |
|
返回顶楼 | |
发表时间:2010-01-27
Domain Event算是我框架里一个亮点,不过这个现在还更多处于理论论证。0.2版除了这个,别的组件也希望大家多多试用多多批评。特别是部分组件经过测试,我认为已经可以在日常开发中使用了。
|
|
返回顶楼 | |
发表时间:2010-01-27
实体和服务解耦了, 不过都和你的Event紧耦合了。
相当于A耦合B变成了A耦合C, C耦合B。 我不确定我说的对不对,不过感觉这样的方式是简单问题复杂化了。 解耦和事件的应用在你这个例子中看不到好处,需要放到复杂业务的背景下, 而且不是你这种实体和服务的方式。 我想查询student的数量, 无论是list.size还是直接sql count一下是最直接的方式, 你解的藕没有太大意义。 在复杂系统中, 如果用Event, 最好用Annotation放到方法外, 而不是方法内显示raise一个event, 而具体的listner也是如此, listen的方法最多就用annotation修饰下就行, 如果单纯为了listen这个写一个代码似乎没有必要。 个人感觉interceptor处理annotation的方式会比你目前这种解耦方式好一些。 说的不对不用理会, 交流交流。 |
|
返回顶楼 | |
发表时间:2010-01-27
|
|
返回顶楼 | |
发表时间:2010-01-27
DomainListener这个东东在哪里注册的
个人感觉领域模型不一定要写服务方法吧,完全可以分成两个类来处理,一个实体,一个服务,大家觉得呢 |
|
返回顶楼 | |
发表时间:2010-01-27
还有一个就是这种情况下,事务怎么处理?
|
|
返回顶楼 | |