JdonFramework模型驱动快速开发

      JdonFramework已经发展成为领域取得设计DDD框架,常驻内存In-memory的领域模型Domain Model通过领域事件Domain Events驱动技术实现各种功能,正如基因DNA是生命各种活动功能的核心一样,实现了以领域模型而不是数据表为核心的新的模型驱动开发架构MDD。

      2009年推出Domain Model + In-memory  + Events.
      2001年Martin fowler的LMAX架构 推荐In-memory + Event Sourcing架构。

      下面谈一下如何快速使用JF来实现模型驱动开发主要步骤,主要针对Jdonframework 6.4版本,该版本引入了业界最快的并发框架Disruptor

将模型对象保持在内存中

      在开始DomainEvents开始之前,必须保证领域模型在内存中,分两步:
      1. 使用@Model标注你的领域模型,如User是从需求中分析出的一个领域模型类:
@Model
public class User {
   private String userId;
   private String name;
     ....
}
      当领域模型类使用@Model标注后,意味着这个模型将会驻留在内存中in memory,缺省Jdon框架支持的是Ehcache. 你也可以设置为Key-value store或其他内存数据网格IMDG,hazelcast替代JF缺省的ehcache

      2. 虽然第一步标注了@Model,但是模型不会自动驻留内存,还需要你在自己的应用代码中显式表明一下,方法是:在仓储Respository层,也就是封装了数据库SQL操作的层面,因为我们的领域模型一般都是从数据库获得,在仓储层进行组装,将一个数据表数据转换成领域对象,那么在这个转换性质的类和方法上要加一个元注释@Introduce(“modelCache”) 和@Around。

cache

      完整源码见:com.jdon.sample.test.domain.onecase.repository.RepositoryImp

      这一步确保领域对象每次加载在内存都是唯一的,In-memory。这对于后面使用domain events是必须关键重要步骤

Domain Events开发步骤

      Domain Events自6.4版本以后(2011.9),有两种使用方式,一种是6.3版本以前的使用JDK并发包futuretask实现的方式;一种是6.4版本以后新增引入最快并发框架Disruptor的@Consuner方式。

      领域事件是一种异步模式 + 生产者-消费者模式。分为主题topic和Queue队列两种。领域模型是生产者;JF的消费者有两种:
      (1).@Send => @Consumer;可以实现1:N多个,内部机制使用号称最快的并发框架Disruptor实现。适合轻量;小任务;原子性;无状态。
      (2).@Send => @Componet;只能1:1,直接使用普通组件类作为消费者,使用jdk future机制,适合大而繁重的任务,有状态,单例。

     

      创建开源并发框架Disruptor的LMAX团队测试发现通常的并发Actor模型是有瓶颈的,所以他们采取Disruptor这样无锁框架,采取了配合多核CPU高速缓冲策略,而其他策略包括JDK一些带锁都是有性能陷阱的: JVM伪共享

      下面谈一下这种新的开发方式:
      第一步:消息发送者的开发,与6.3版本以前类似:
      首先:使用 @Model 和 @Introduce(“message”)标注实体类。
      然后:使用 @Send(“mytopic”) 标注该实体中的发送方法。
     

源码见框架下载包中的com.jdon.sample.test.domain.onecase.DomainEvent

      约束规则:@Introduce(“message”)中“message”值表示引入JF配置aspect.xml中消息拦截器: om.jdon.domain.message.MessageInterceptor,通过该配置让该模型类成为消息生产者或发送者。
      @ Send(“mytopic”):中的“mytopic”是消息topic名称,可自己取,但是和消费者的标注@Consumer(“mytopic”)或@Compnent(“mytopic”)是一致的,表示生产者将消息发往这个topic中;
      在@send标注的方法中,你还需要将你要传送给消息消费者使用的数据打包进入DomainMessag对象,该方法的返回类型必须是DomainMessag.

      第二步:事件的消费者开发,分两种:@Consumer和@Component。

     主要谈一下@Consumer方式: 标注消费者@Consumer("mytopic");消费者类必须实现接口      com.jdon.domain.message.DomainEventHandler。

     源码见com.jdon.sample.test.domain.onecase.MyDomainEventHandler

     如果有多个消费者订阅了同一个主题Topic,那么这些消费者之间的运行顺序是按照他们的类名字母先后的。如AEventHandler先于BEventHandler先于CEvent…等。
     消费者的主要内容写在onEvent中,可通过event.getDomainMessage()获得生产者你要传递的数据。

客户端调用

     JdonFramework客户端可以是在Web的Servlet或Jsp中调用,也可以在Application调用,如下:

 

ContainerSetupScript css = new ContainerSetupScript();
css.prepare("com.jdon.jdonframework.xml", da);
AppUtil appUtill = new AppUtil("com.jdon.jdonframework.xml");

IServiceSample serviceSample = (IServiceSample) appUtil.getService("serviceSample");
String res = (String) serviceSample.eventPointEntry("hello");
Assert.assertEquals(res, "eventMessage=hello");

 

     源码见:com.jdon.SampleAppTest

DCI架构实现

     DCI:数据Data, 场景Context, 交互Interactions是由MVC发明者Trygve Reenskaug发明的。 见 DCI架构是什么? DCI让我们的核心模型更加简单,只有数据和基本行为。业务逻辑等交互行为在角色模型中,在运行时的场景中,将角色的交互行为注射到模型中。

     领域事件应该属于一种业务行为,它是DCI中I交互,而交互行为应该在角色模型中,所以,领域事件的生产者应该是角色。实现领域事件的同时也实现了DCI。只不过两种角度有些不同。

     为更清楚说明DCI,下面以JdonFramework案例说明。

     领域模型是DCI的Data,只有数据和基本行为,更简单,但注意不是只有setter/getter的贫血模型。如下:

 

@Model
public class UserModel {

     private String userId;
     private String name;

     @Inject
     private ComputerRole computerRole;

 

 

     Domain Events事件或消息的生产者也就是DCI中的角色Role,比如我们有一个专门进行计数计算的角色,实际上真正计算核心因为要使用关系数据库等底层技术架构,并不真正在此实现,而是依托消息消费者@Consumer实现,那么消息生产者可以看出是一个接口,消费者看成是接口的实现:

 

@Introduce("message")
public class ComputerRole {

     @Send("computeCount")
     public DomainMessage computeCount(UserModel user) {
          return new DomainMessage(user);
     }

     @Send("saveUser")
     public DomainMessage save(UserModel user) {
          return new DomainMessage(user);
     }

}

 

DCI第三个元素是场景Context,在这个场景下,ComputeRole将被注入到模型UserModel中,实现计数计算的业务功能:

public class ComputeContext {

    private DomainMessage ageAsyncResult;

     public void preloadData(UserModel user) {
          if (ageAsyncResult == null)
               ageAsyncResult = user.getUserDomainEvents().computeCount(user);
     }

     public int loadCountNow(UserModel user) {
          preloadData(user);
          return (Integer) ageAsyncResult.getEventResult();
     }

     public int loadCountByAsync(UserModel user) {
          if (ageAsyncResult == null)
               ageAsyncResult = user.getUserDomainEvents().computeCount(user);
          else if (ageAsyncResult != null)
               return (Integer) ageAsyncResult.getEventResult();
          return -1;

     }

}

 

剩余的其他步骤见:JdonFramework快速指南(Step By Step

更多见JdonFramework文档

相关文章:

不变性immutablity设计

DDD DCI和领域事件

无堵塞的并发编程

领域驱动开发

Spring 和 AspectJ实现领域驱动设计

Martin Fowler推荐的领域模型in-memory架构:LMAX架构