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

本人的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);
            }
        }
    }
}

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

分享到:
评论
30 楼 unsid 2010-03-02  
这个例子不好啊,我实在没明白为什么domain object要调用services,这属于逆向调用了,在这个例子里既然获取所有学生的职责在class类里,为什么获取一个指定学生的职责却在services里?并且,就算在services里,那么services也是通过调用domain实现业务的,终归到底还是domain之间的调用。。

我相信肯定存在domain调用services的情况,但是这个例子实在让人费解阿
29 楼 apache.smack 2010-03-02  
<p>我觉得LZ这样做挺好的。一直认为领域模型应该脱离具体的技术实现。每次都依赖DAO总觉得有点别扭</p>
28 楼 魔力猫咪 2010-01-29  
donkey_lin 写道
楼主可以看一下最新Jdon framework,里面已经有Domain Event的实现,而且是异步的。

我知道。其实最早看到这个模式就是在那里。我文章里虽然只写了同步的,但是其实我框架本身同时支持同步和异步。
27 楼 donkey_lin 2010-01-28  
楼主可以看一下最新Jdon framework,里面已经有Domain Event的实现,而且是异步的。
26 楼 e3002 2010-01-28  
个人觉得,你这个框架好不好应该从方面去说:比如可维护性、解耦等等。这样大家能和自己使用现状有所比较,自认能体会到其好坏。
25 楼 myreligion 2010-01-28  
域对象分为贫血模型和富血模型,您的设计应该属于后者。对于贫血模型,对于getStudentSize()这类lazy load的东西,我在guzz中设计的模型是“自定义属性”。

class T{

public int getStudentSize() ;

}

然后在领域对象映射文件中配置字段加载属性自定义加载方式loader="xxxxx.xxxx"。基本上,域对象设计不变(还是简单的POJO)。由loader负责这类特殊内容的自定义读取,感觉比DomainEvent的方式清晰一些。您看是否有参考价值。



24 楼 魔力猫咪 2010-01-28  
jansel 写道
首先,LZ的想法是很不错的。在目前的WEB开发中(注意,我强调的是WEB),Model基本上都是贫血的,里面只有数据以及自身内部数据的处理。其他操作都交给Service来处理。

LZ提到的问题,我的理解是,本来应该属于Model自身的数据处理,但是要考虑到性能,所以需要懒加载。但是,懒加载也有解决不了的问题,比如返回班级里面学生的个数。如果使用Hibernate的懒加载,那么是需要把所有的学生读取出来然后取一个size,这里就存在一个性能问题了。当然,不知道我有没有理解LZ的意思啊。

其次,LZ的做法让人感觉耦合了。其实,耦合看你怎么看了,LZ的是API耦合。假设,我们使用Interceptor去实现,难道就没有耦合了吗?那只不过不是API耦合而已,是数据耦合,因为你同样要识别getStudentSize这个方法。

另外,Model里面耦合Service这个肯定不是上上策(当然,不能说是错误)。

LZ继续加油吧。

多谢。我这个DomainEvent本身还是处于一个试验性质,肯定有很多设计不合理的地方。大家对我文章批评了这么多(我觉得主要是举的例子不合适),我会仔细思考如何改进DomainEvent框架,看能否进一步解耦。大家还请多多对我的框架本身多多试用多多批评。BAC框架0.2除了DomainEvent之外,还有验证码等组件,希望大家对此多提意见。
23 楼 jansel 2010-01-28  
首先,LZ的想法是很不错的。在目前的WEB开发中(注意,我强调的是WEB),Model基本上都是贫血的,里面只有数据以及自身内部数据的处理。其他操作都交给Service来处理。

LZ提到的问题,我的理解是,本来应该属于Model自身的数据处理,但是要考虑到性能,所以需要懒加载。但是,懒加载也有解决不了的问题,比如返回班级里面学生的个数。如果使用Hibernate的懒加载,那么是需要把所有的学生读取出来然后取一个size,这里就存在一个性能问题了。当然,不知道我有没有理解LZ的意思啊。

其次,LZ的做法让人感觉耦合了。其实,耦合看你怎么看了,LZ的是API耦合。假设,我们使用Interceptor去实现,难道就没有耦合了吗?那只不过不是API耦合而已,是数据耦合,因为你同样要识别getStudentSize这个方法。

另外,Model里面耦合Service这个肯定不是上上策(当然,不能说是错误)。

LZ继续加油吧。
22 楼 shoushou2001 2010-01-28  
感觉有点类似适配器,在实体与服务之间加入了一个接口,达到解藕的目的。
楼主有没有DomainEvent更详细的说明。
21 楼 lonelybug 2010-01-27  
首先以LZ的例子说一下,班机有多少学生上课,本身,一个班机和学生,本身应该是两个domain model,因为很简单,学生和班级在学校整个这个context下都是拥有全局identity的。

而,一个班机拥有多少学生,本身可以把学生的统计情况作成Value Object,里面包含Student number还有其他统计情况即可。

再说回来你本身问题,一个Domain Aggregate Root本身就是第一次创建于Factory或者从Repository取得,也就是说Dao要在这两个里面进行注入,而一个Domain Aggregate Root对象应该是在通过Repository的Save之后才拥有Dao的访问权利。

而对于Repository的Dao注入,当然是在类的创建时候了--constructor。那个文章,我也看了,属于没有实际应用经验的杞人忧天罢了,记住了,不要太崇洋媚外了,不一定用英文写的文章就一定会使多高深的,就一定要去盲目追从!
20 楼 Arden 2010-01-27  
这种方式唯一的好处就是可以把CRUD中的CUD等数据库的操作异步执行~~
19 楼 Arden 2010-01-27  
另外给你推荐一下:
http://axonframework.googlecode.com

CQRS框架~~~
18 楼 Arden 2010-01-27  
我感觉真的没啥优势,反而越来越复杂~~
17 楼 topcode 2010-01-27  
单纯为了节偶的话代价是否有点大
16 楼 berlou 2010-01-27  
魔力猫咪 写道
berlou 写道
还有一个就是这种情况下,事务怎么处理?

实体本身不负责事务,事务由监听器处理。


如果同时有3个listener监听一个事件的时候呢?

你这个地方可以考虑一下不用observer这种模式。


15 楼 魔力猫咪 2010-01-27  
berlou 写道
还有一个就是这种情况下,事务怎么处理?

实体本身不负责事务,事务由监听器处理。
14 楼 魔力猫咪 2010-01-27  
可能是我写作水平太差,举的例子可能也不是很合适。我承认我目前的实现更多还处于试验性质,还很不成熟。而且因为只是进行说明,所以代码并不完整。完整代码可以下载我的框架看源代码。
对这个模式是否合适大家可以看一下我链接的最早提出该方案的作者的博客。然后再继续讨论这个模式是否合适。
13 楼 魔力猫咪 2010-01-27  
超级潜水员 写道
提醒一下楼主,使用https即可访问group,


https://groups.google.com/group/bac_china/

我知道使用https可以访问。不过即使是https也是时通时不通的。
12 楼 hatedance 2010-01-27  
别为了解耦而解耦。
把getStudentSize(Clazz clz)方法定义在ClassService上不也挺好理解?
一个班级的人数的确是可以自查,也可以让第三方机构来统计吧?
11 楼 kaipingk 2010-01-27  
什么飞机哦,越搞越复杂。。。还用到这么多模式。。。。

相关推荐

    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年计算机科学与技术或相关专业研究生入学考试的...

    HP BAC与SiteScope集成

    ### HP BAC与SiteScope集成详解 #### 一、HP SiteScope 安装与配置 HP SiteScope是一款功能强大的性能监控工具,适用于多种环境,包括Web应用、数据库、服务器等。其安装流程主要包括: 1. **查找安装文件**:...

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

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

    迅饶BACnet 网关BAC1002-ARM

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

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

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

    惠普业务可用性中心(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