`
魔力猫咪
  • 浏览: 107062 次
  • 来自: 北京
社区版块
存档分类
最新评论

本人的BAC框架发布0.2版,兼谈DomainEvent模式

阅读更多
经过半个多月的开发,我的BAC框架正式发布0.2版。本来预计是春节前发布,但是最近比较闲,用于开发的时间也增加了很多,框架提前完成。
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);
            }
        }
    }
}

这样实体和服务就分开了。不会造成实体和服务的紧耦合。

分享到:
评论
10 楼 liuyu220 2010-01-27  
 int studentSize;  
  
   public void getStudentSize() {  
       DomainEventManager.disptcher(new DefaultDomainEvent(this, "getStudentSize"));  
   }  

这段代码和DefaultDomainEvent紧耦合了。楼主是怎么处理Event与实体之间的解耦呢?
9 楼 berlou 2010-01-27  
还有一个就是这种情况下,事务怎么处理?
8 楼 xiayh1002 2010-01-27  
DomainListener这个东东在哪里注册的

个人感觉领域模型不一定要写服务方法吧,完全可以分成两个类来处理,一个实体,一个服务,大家觉得呢
7 楼 超级潜水员 2010-01-27  
提醒一下楼主,使用https即可访问group,


https://groups.google.com/group/bac_china/
6 楼 berlou 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的方式会比你目前这种解耦方式好一些。
说的不对不用理会, 交流交流。
5 楼 魔力猫咪 2010-01-27  
Domain Event算是我框架里一个亮点,不过这个现在还更多处于理论论证。0.2版除了这个,别的组件也希望大家多多试用多多批评。特别是部分组件经过测试,我认为已经可以在日常开发中使用了。
4 楼 mycybyb 2010-01-27  
魔力猫咪 写道

Class中调用DAO很明显是一个错误的结构,二者紧耦合了。如果Class被分布处理、WebService调用、串行化等等会如何呢?而且Dao要在什么时候注入Class?这些都会增加复杂性。
我也承认我这里的代码还比较初级。有些DomainEvent框架已经做到了通过注解来调用监听器了。


我倒认为是个很好的方法。而且这个DAO也不一定要注入。我对领域模型不是很熟,好像有些什么工厂和仓库之类的东西负责类的创建和数据访问。
3 楼 mycybyb 2010-01-27  
魔力猫咪 写道

Class里面怎么没东西呢?

不好意思,这个我理解错了
2 楼 魔力猫咪 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框架已经做到了通过注解来调用监听器了。
1 楼 mycybyb 2010-01-27  
简单问题复杂化,而且这样Class也没什么东西呀,还不是调用别的类完成,只是Service、DAO还是DomainEvent名字的区别而已。

我见过一个,在Class中调用DAO,就是这样
Service->Class->DAO

相关推荐

    Bac-to-bac表达系统中文版说明书.pdf

    **Bac-to-Bac杆状病毒表达系统**是一种高效便捷的重组蛋白表达工具,尤其适用于在昆虫细胞中实现目的基因的高水平表达。该系统的核心原理是利用杆状病毒的Tn7转座子机制,将含有目的基因的pFastBac捐赠质粒插入到E. ...

    Bac-to-Bac杆状病毒表达系统.doc

    Bac-to-Bac 杆状病毒表达系统 Bac-to-Bac 杆状病毒表达系统是一种快速有效的方法,用于产生重组杆状病毒。该系统基于让已经转入杆状病的质粒(杆粒)的位点特意转座子的表达框的质粒在 Ecoli 中扩增。 该系统的...

    Android statusBar添加bac

    Android statusBar添加bac

    Bac_to_bac表达系统中文版说明书.pdf

    **Bac-to-Bac杆状病毒表达系统**是一种高效、快速的重组病毒生成工具,尤其适用于在昆虫细胞中实现目标基因的高水平表达。该系统基于Tn7位点特异性转座子机制,大大简化了重组病毒的构建过程,减少了传统同源重组...

    Bac_to_bac表达系统中文版说明书.doc

    【Bac-to-Bac杆状病毒表达系统】是一种高效、快速的重组病毒生成技术,尤其适用于在昆虫细胞中实现目标基因的高水平表达。该系统基于Tn7位点特异性转座子机制,大大简化了传统同源重组过程,缩短了从克隆到重组病毒...

    Bac-to-bac表达系统中文版说明书模板.doc

    【Bac-to-Bac杆状病毒表达系统】是一种在昆虫细胞中高效表达目的基因的生物技术,主要用于蛋白质功能研究和生产。该系统基于Tn7位点特异性转座子的机制,简化了重组杆状病毒的生成过程,提高了实验效率。 **系统...

    bac.rar_bac

    【华南理工大学2008年计算机类研究生入学上机试题】是本次讨论的主题,这个压缩文件"BAC.rar_bac"包含了一份重要的文档——"bac.doc",很显然,它是华南理工大学2008年计算机科学与技术或相关专业研究生入学考试的...

    第三章电子商务交易模式和框架结构.pptx

    还有B2F(Business to Family)、G2G(Government to Government)、B2B2C(Business to Business to Consumer)、B4C(Business for Consumer)和BAC(Business Assisted Commerce)等新型电子商务模式,它们在...

    Elco-宜科 条码阅读器-BAC 48产品手册.pdf

    Elco-宜科 条码阅读器-BAC 48产品手册pdf,Elco-宜科 条码阅读器-BAC 48产品手册

    迅饶BACnet 网关BAC1002-ARM

    ### 迅饶BACnet 网关BAC1002-ARM #### 一、产品概述 迅饶BACnet 网关BAC1002-ARM是一款高性能、多功能的网关设备,专门用于实现不同通信协议之间的转换,特别是将各种设备连接到BACnetIP网络中。该网关适用于楼宇...

    惠普业务可用性中心(HP BAC)

    ### 惠普业务可用性中心(HP BAC)知识点详解 #### 一、概述 惠普业务可用性中心(HP Business Availability Center,简称HP BAC)是一款强大的业务可用性管理解决方案,它专为Windows和Solaris操作系统设计,旨在...

    基于Spring Boot框架的秒杀系统45a7fafd9e1b96bac5afd6c32b0e30eb.zip

    基于Spring Boot框架的秒杀系统 项目简介 本项目是一个基于Spring Boot框架的秒杀系统,主要用于学习秒杀、多并发、性能提升等方面的知识。项目整合了支付宝支付功能,并采用了多种技术栈来优化系统性能和用户...

    BACnet IP网关BAC1022-ARM

    软件特点: -绿色免安装,支持中英文; -支持60种协议同时转为BACnet IP服务器; -支持WEB浏览,查看,设置,进行监视和控制; -支持线性转换,取位,高低字节转换; -配置软件X2BACnet配置工程方便,1000个点20分钟内...

    BAC-S-MOD-M配置软件使用说明.pdf

    在详细解读“BAC-S-MOD-M配置软件使用说明.pdf”文档内容后,我们可以提炼以下IT知识点: ### BAC-S-MOD-M配置软件功能及应用 1. **软件目的:** 该配置软件主要用于对BAC-S-MOD-M网关进行参数配置,包括网关的IP...

    Elco-宜科 条码阅读器-BAC500产品手册.pdf

    Elco-宜科 条码阅读器-BAC500产品手册pdf,Elco-宜科 条码阅读器-BAC500产品手册

    基于框架的Linux安全增强开发技术.pdf

    本文主要探讨了基于框架的Linux安全增强开发技术,通过分析Linux Security Module (LSM) 和Rule Set Based Access Control (RS-BAC) 实现的Generalized Framework of Access Control (GFAC) 框架,以提高Linux操作...

Global site tag (gtag.js) - Google Analytics