`
wolfweis
  • 浏览: 20540 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类

领域驱动(DDD)充血模式下,domain 与 Service以及Repository的解耦---DOMAIN EVENT

    博客分类:
  • DDD
阅读更多
    思考充血模式下,domain object 对 repository 的依赖的解耦方法。实际上在HTML,java swing 等中都已经给出的解决方法,就是 event。
    想到解耦在GOF设计模式中最熟悉的就是 observer模式和command模式,其实两者区别不大。所以以observer模式来实现。 在java.util中就已经有了observer的实现,但是太简单了,不太适合这种复杂的domain,特别是在有多种不同的事件触发的情况.所以决定重写Observable.
public abstract class DomainObservable implements java.io.Serializable {

	private HashMap<String, Collection> observerMap = new HashMap<String, Collection>();

	public HashMap<String, Collection> getObserverMap() {
		return observerMap;
	}

	public void setObserverMap(HashMap<String, Collection> observerMap) {
		if(observerMap != null)
		{
			this.observerMap = observerMap;
		}
	}

	public void addObserver(String eventType,DomainObserver domainObserver) {
		if (domainObserver == null)
		{
			throw new NullPointerException();
		}
		
		if(observerMap.containsKey(eventType))
		{
			observerMap.get(eventType).add(domainObserver);
		}
		else
		{
			Collection<DomainObserver> observers = new ArrayList<DomainObserver>();
			observers.add(domainObserver);
			observerMap.put(eventType, observers);
		}
	}

    public void deleteObserver(String eventType,DomainObserver o) {
        Collection<DomainObserver> observers = observerMap.get(eventType);
        if(observers != null)
        {
        	observers.remove(o);
        }
    }
    
    public void deleteObservers() {
    	observerMap.clear();
    }
    
    public void notifyObservers(String eventType)
    {
    	this.notifyObservers(eventType, null);
    }
    
    public void notifyObservers(String eventType,Object arg) {
    	Collection<DomainObserver> observers = observerMap.get(eventType);
    	if(observers != null)
    	{
    		Iterator<DomainObserver> ita = observers.iterator();
    		while(ita.hasNext())
    		{
    			DomainObserver domainObserver = ita.next();
    			ObserverValueObject observerValueObject = domainObserver.getObserverValueObject();
    			if(observerValueObject != null)
    			{
    				observerValueObject.init(this,arg);
    			}
    			domainObserver.update(this,eventType,arg);
    		}
    	}
    }    
}

    这样在domain object中可以发出不同类型的event,observer也可以针对不同的event来进行观察.
    在来看observer interface,如下
public interface DomainObserver {
	
	
	/**
	 * 当被观察者对象发生变化引起事件的时候,调用
	 * @param demainObservable 被观察者
	 * @param eventType 被观察者事件类型
	 * @param arg 被观察者传递的值
	 */
	public void update(DomainObservable demainObservable,String eventType,Object arg);
}

    对于observer,我定义成为了去完成某项功能的client。为了完成功能,需要两个重要方面,服务和数据。服务好说 IOC一下就可以了。但是对于数据,就需要做一些操作了,目的是为了让observer与observable解耦,同时对observer进行依赖注入。所以怎样从domain object 中抽取出 observer所需要的数据,就成了observer value object的任务了。
看如下value object 接口:
public interface ObserverValueObject extends java.io.Serializable{

	/**
	 * 根据被观察者生成观察者所需要的值对象
	 * @param domainObservable
	 * @param arg
	 */
	public void init(DomainObservable domainObservable,Object arg);
	
}

    然后对observer interface 进行改进
public interface DomainObserver {
	
	/**
	 * 返回观察者的值对象
	 * @return
	 */
	public ObserverValueObject getObserverValueObject();
	
	/**
	 * 当被观察者对象发生变化引起事件的时候,调用
	 * @param demainObservable 被观察者
	 * @param eventType 被观察者事件类型
	 * @param arg 被观察者传递的值
	 */
	public void update(DomainObservable demainObservable,String eventType,Object arg);
}

    另外,某些情况下,被观察者需要知道观察者做事情后的结果,所以需要给出一些回调方法,如下 3种回调
 /**
     * 回掉 具体方法 获取返回值
     * @param eventType
     * @param methodName
     * @param args
     * @return
     * @throws Throwable 
     */
    public Object callbackDomainObserver(String eventType,String methodName,Object[] args)
    {
    	Object result = null;
    	boolean isFindMethod = false;
    	Collection<DomainObserver> observers = observerMap.get(eventType);
    	if(observers != null)
    	{
    		Iterator<DomainObserver> ita = observers.iterator();
    		while(ita.hasNext())
    		{
    			DomainObserver domainObserver = ita.next();
    			
    			/**
    			 * 构建参数对象CLASS
    			 */
    			Class[] parameterTypes = null;
    			if(args != null)
    			{
    				parameterTypes = new Class[args.length];
    				for(int i = 0;i<args.length;i++)
    				{
    					parameterTypes[i] = args[i].getClass();
    				}
    			}
    			
    			Method method = null;
				
    			/**
    			 * 取得相同名称的方法 如果没有 就是NULL
    			 */
				try {
					method = domainObserver.getClass().getMethod(methodName, parameterTypes);
				} catch (Exception e) {
				}
				
				if(method != null)
				{
					try {
						result = method.invoke(domainObserver, args);
						isFindMethod = true;
						break;
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
						if(e.getTargetException() instanceof SystemException)//如果是系统业务异常
						{
							throw (SystemException)e.getTargetException();
						}
						else//否则是程序错误异常
						{
							throw new RuntimeException(e.getTargetException());
						}
					}
				}
    		}
    	}
    	
    	if(isFindMethod)
    	{
    		return result;
    	}
    	else
    	{
    		throw new RuntimeException("System Observer Error:Domain Object("+this.getClass().getName()+") needs Observer to observe event of "+eventType+" and Need CallBack method "+methodName);
    	}
    }

public Collection<DomainObserver> callbackDomainObserver(String eventType)
    {
    	return observerMap.get(eventType);
    }

public Collection<ObserverValueObject> callbackObserverValueObject(String eventType)
    {
    	Collection<DomainObserver> observers = observerMap.get(eventType);
    	if(observers != null)
    	{
    		Collection<ObserverValueObject> valueObjects = new ArrayList<ObserverValueObject>();
    		Iterator<DomainObserver> ita = observers.iterator();
    		while(ita.hasNext())
    		{
    			valueObjects.add(ita.next().getObserverValueObject());
    		}
    		
    		return valueObjects;
    	}
    	else
    	{
    		return null;
    	}
    }

    在我的项目中,第一种回调(回调方法)用的比较多,但这里有一些耦合。本来想再利用一种KEY-VALUE配置的方式来解耦,但是想想,这样做起来,返回代码阅读困难度增加不少,就放弃了。这里算是一个小的不足,看各位有没有更改好的解决办法。

     到了这里,大部分代码工作已经完成,下面就需要整合了。其实里面最关键也最重要的就是怎么样把observer注入到 被观察者中。
     首先配置observer value object 和 observer --- spring 里面很好配置,注意:有些observer可以直接配置成 单例,但是有些需要给出回调方法的observer 不能配置成 单例 。
     然后是把observer注入到被观察者中。利用aspectJ(spring 里面已经整合过)的LTW功能注入。
     下面给出个例子吧
domain object
@Configurable
public class BrandDomain extends DomainObservable{
         private String brandno;
	
	private String brandName;

         public String getBrandName() {
		return brandName;
	}

	public void setBrandName(String brandName) {
		this.brandName = brandName;
	}

	public String getBrandno() {
		return brandno;
	}

	public void setBrandno(String brandno) {
		this.brandno = brandno;
	}
	
	public void addBrandDomain(BrandValueObject valueObject)
	{
		if(!this.checkSameObjectExist(valueObject.getBrandno()))
		{
			this.setBrandno(valueObject.getBrandno());
                     this.setBrandName(valueObject.getBrandName());
                    this.notifyObservers("save");
		}
		else
		{
			throw new BusinessException("品牌编码已经存在");
		}
	}
	
	public void editBrandDomain(BrandValueObject valueObject)
	{
		this.setBrandName(valueObject.getBrandName());		this.notifyObservers("update");
	}

	protected boolean checkSameObjectExist(Object keyword)
	{
		this.notifyObservers("checkSameObject", keyword);
		return (Boolean) this.callbackDomanObserver("checkSameObject", "isExistSameObject", null);
	}

	public void deleteBrand()
	{
		this.notifyObservers("delete");
	}
}


配置文件
	<bean class="cn.com.dbSale.server.business.domain.basisDomain.productDomain.brandDomain.BrandDomain" scope="prototype" >
		<property name="observerMap">
			<map key-type="java.lang.String" value-type="java.util.Collection">
				<entry key="save">
					<list value-type="java.util.ArrayList">
						<ref bean="saveObjectObserver"/>
					</list>
				</entry>
				<entry key="update">
					<list value-type="java.util.ArrayList">
						<ref bean="updateObjectObserver"/>
					</list>
				</entry>
				<entry key="delete">
					<list value-type="java.util.ArrayList">
						<ref bean="deleteObjectObserver"/>
					</list>
				</entry>
				<entry key="checkSameObject">
					<list value-type="java.util.ArrayList">
						<ref bean="checkSameObjectObserver"/>
					</list>
				</entry>
			</map>
		</property>
	</bean>
	<bean id="saveObjectObserver" class="cn.com.dbSale.server.business.observer.observerInterface.SaveObjectObserver">
		<property name="repositoryInterface">
			<ref local="repositoryInterface"/>
		</property>
	</bean>
	
	<bean id="updateObjectObserver" class="cn.com.dbSale.server.business.observer.observerInterface.UpdateObjectObserver">
		<property name="repositoryInterface">
			<ref local="repositoryInterface"/>
		</property>
	</bean>
	
	<bean id="deleteObjectObserver" class="cn.com.dbSale.server.business.observer.observerInterface.DeleteObjectObserver">
		<property name="repositoryInterface">
			<ref local="repositoryInterface"/>
		</property>
	</bean>
	
	<bean id="checkSameObjectObserver" class="cn.com.dbSale.server.business.observer.observerInterface.CheckSameObjectObserver"  scope="prototype">
		<property name="repositoryInterface">
			<ref local="repositoryInterface"/>
		</property>
	</bean>

     以上是一个最简单的例子,还算是表达出了基本意思。
      对于复杂的流程可以通过发布事件 达到流程可配置.
      增、删、改 这3个观察者 可以适注入到所有DOMAIN 对象中。
      另外observer value object 的用法,没有给出,各位可以自己试试

     整个DOMAIN EVENT 的源文件 都在附件里面,代码不多。
      本人第一次网上发文章,如有不足,多多关照!
      还有,如果各位需要observer 与 被观察者 异步操作,可以修改 AbstractObservable 代码 实现异步观察
1
0
分享到:
评论
2 楼 wolfweis 2010-06-13  
狂放不羁 写道
看来楼主对DDD也有兴趣,欢迎到jdon.com讨论。Jdon目前也采用DomainEvent实现了模型和技术组件的解耦,底层功过CGLIB实现。

呵呵,谢谢,我会去的
1 楼 狂放不羁 2010-06-13  
看来楼主对DDD也有兴趣,欢迎到jdon.com讨论。Jdon目前也采用DomainEvent实现了模型和技术组件的解耦,底层功过CGLIB实现。

相关推荐

    maven-repository-metadata-3.0.jar

    maven-repository-metadata-3.0.jar

    PyPI 官网下载 | service_repository-0.1.0-py3-none-any.whl

    《PyPI官网下载:service_repository-0.1.0-py3-none-any.whl》 在Python的世界里,PyPI(Python Package Index)是官方的软件仓库,它为开发者提供了发布和分享Python软件包的平台。PyPI上的资源广泛且多样化,...

    DDD领域驱动设计day01.pdf

    领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法论,由Eric Evans在其同名著作中提出,旨在帮助开发者更好地理解和处理复杂的业务逻辑,通过深入挖掘领域知识来构建高质量的软件系统。DDD的核心是...

    repository.xbmc-addons-chinese-1.2.1.zip

    标题“repository.xbmc-addons-chinese-1.2.1.zip”揭示了这是一个与XBMC(Xbox Media Center)相关的软件资源包,版本号为1.2.1,并且是针对中文用户定制的。XBMC是一款开源的媒体中心软件,后来更名为Kodi,允许...

    repository.xbmc-addons-chinese-2.0.0.zip

    标题中的"repository.xbmc-addons-chinese-2.0.0.zip"是一个针对KODI媒体中心的中文插件库的压缩包,版本号为2.0.0。这个压缩包包含了多款专为中国用户设计的KODI插件,旨在提升KODI在中文环境下的用户体验和功能...

    repository.xbmc-addons-chinese-2.0.1.zip亲测可用kodi中文插件库下载

    Repository文件通常包含元数据、插件的安装包以及更新信息,使得用户能通过Kodi的界面方便地安装和更新这些插件。 为了充分利用这个中文插件库,用户需要在Kodi中安装它。首先,用户需要将“repository.xbmc-addons...

    C#-DDD领域驱动设计-曹建

    《C#-DDD领域驱动设计-曹建》是关于使用C#编程语言实践领域驱动设计(Domain-Driven Design,简称DDD)的一个项目。领域驱动设计是一种软件开发方法,它强调以业务领域为中心进行软件设计,将复杂的业务逻辑转化为可...

    smart-lottery抽奖系统基于COLA架构采用DDD领域驱动中四层架构

    【标题】"smart-lottery抽奖系统基于COLA架构采用DDD领域驱动中四层架构" 描述了这个项目的核心设计原则和技术栈。首先,我们要理解COLA架构和DDD(领域驱动设计)的概念。 COLA(Clean Architecture On Layers,...

    base-repository-authorization-service-源码.rar

    《深入解析base-repository-authorization-service源码》 在当今的软件开发中,授权服务扮演着至关重要的角色,它确保了系统的安全性和用户访问权限的正确管理。"base-repository-authorization-service-源码.rar...

    DDD2019领域驱动设计大会PPT全套

    领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法,它强调通过深入理解和建模业务领域来驱动软件的设计和开发。DDD2019领域驱动设计大会是一次专门探讨这一主题的会议,旨在帮助开发者更好地应用...

    DDD领域驱动设计-Nop简易原型

    领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法,它强调以业务领域为中心进行系统设计,将复杂的业务逻辑转化为清晰的模型,从而提高软件的可读性、可维护性和可扩展性。NopCommerce是一款开源的...

    DDD领域驱动设计初探(2):仓储Repository(上) - 文章 - 伯乐在线1

    仓储Repository在领域驱动设计(DDD)中扮演着重要的角色,它是连接业务逻辑层(领域层)与数据访问层(基础设施层)的关键组件。仓储的主要目的是抽象出数据访问的细节,使得领域模型可以专注于业务规则和逻辑,而...

    c# DDD(领域驱动设计)理论结合实践.rar

    领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法论,旨在通过将业务领域模型作为软件开发的核心,来解决复杂系统的设计和实现问题。在C#开发中,DDD可以帮助开发者更好地理解和处理复杂的业务逻辑...

    领域驱动设计(Domain_Driven_Design_C_Sharp)

    在《领域驱动设计(Domain_Driven_Design_C_Sharp)》这本书中,作者深入探讨了如何在C#环境下实践DDD。 1. **领域模型**:领域模型是DDD的核心,它代表了业务领域的概念、规则和流程。在C#中,我们可以通过定义...

    elasticsearch-repository-oss-6.7.0.zip

    综上所述,"elasticsearch-repository-oss-6.7.0"插件利用了上述库,实现了Elasticsearch与阿里云OSS的无缝集成,提供了一种可靠的数据备份和恢复解决方案。在实际部署中,用户需要正确配置Elasticsearch的插件和OSS...

    Repository-UOW-Sample-master_C#_

    标题中的"Repository-UOW-Sample-master_C#"表明这是一个关于C#编程语言的示例项目,主要涉及Repository模式和Unit of Work(工作单元)模式。Repository模式是设计模式中的一种,常用于数据访问层,它为业务逻辑层...

    基于DDD领域驱动设计通用后台权限系统开发

    领域驱动设计(Domain-Driven Design,简称DDD)是一种软件开发方法,强调以业务领域为中心进行系统设计,将复杂的业务逻辑转化为清晰的模型。在“基于DDD领域驱动设计通用后台权限系统”中,我们首先需要理解DDD的...

    archiva-scheduler-repository-api-2.0.0-sources.jar

    标签:archiva-scheduler-repository-api-2.0.0-sources.jar,archiva,scheduler,repository,api,2.0.0,sources,jar包下载,依赖包

Global site tag (gtag.js) - Google Analytics