JdonFramework架构
JdonFramework是一个DDD领域驱动设计框架,Domain Model + In-memory + Events,常驻内存In-memory的领域模型Domain Model通过领域事件Domain Events驱动技术实现各种功能,正如基因DNA是生命各种活动功能的核心一样,实现了以领域模型而不是数据表为核心的新的模型驱动开发架构MDD。
没有领域设计的坏设计
以订单为例子,如果不采取DDD设计,而是通常朴素的数据表库设计,将订单设计为订单数据表,那么带来的问题是:
将实体的职责分离到不同限定场景,比如订单中有OrderItemId, OrderId, ProductId 和 Qty,这是合乎逻辑的最初订单,后来有 MinDeliveryQty 和 PendingQty字段,是和订单交货有关,这其实是两个概念,订单和订单的交货,但是我们把这些字段都混合在一个类中了。
混淆在一起的问题就是将来难以应付变化,因为实体的职责是各自变化的。 领域不是把实体看成铁板一块,一开始就把它分解到各种场景。下订单和订单交货交付是两个场景,它们应该有彼此独立的接口,由实体来实现。这就能够让实体和很多场景打交道,而彼此不影响,这就是组合模型composite model的一个关键优点。
在数据库中它们是一个,也就是说,从ER模型上看,它们是一个整体,但是从domain model领域模型角度看,它们是分离的。
下图是基于Jdon框架开发的JiveJdon中领域模型,中间的领域模型可能是继承等关系组成的对象群,或者成为聚合群Aggregation,但是对应的数据表可能是一张表,由此可见,领域模型能够更准确反映业务需求。
领域驱动设计DDD
当我们接到一个新项目时,使用UML工具,通过面向对象DDD的分析设计方法将其变成领域模型图,如下:
这是一个典型的DDD建模图,这个模型图可以直接和Java代码对应,比如其中Cargo模型的代码如下,两者是完全一一对应,可以使用together等建模工具直接转换,Jdon框架的@Model就是针对Cargo这样模型,将其运行在Java平台中,:
package ship; @Model
public class Cargo { private String id;
private ship.DeliveryHistory lnkDeliveryHistory; private ship.DeliverySpec lnkDeliverySpec;
public Cargo(String trackingId, DeliverySpec deliverySpec) { this.id = trackingId; this.lnkDeliverySpec = deliverySpec; }
public void changeDestination(final Location newDestination) { lnkDeliverySpec.setDestination(newDestination); }
//跟踪货物位置
public Location lastKnownLocation() { final HandlingEvent lastEvent = this.getLnkDeliveryHistory().lastEvent(); if (lastEvent != null) { return lastEvent.getLocation(); } else { return null; } }
//当货物在运输抵达目的地点时 public boolean hasArrived() { return lnkDeliverySpec.getDestination().equals(lastKnownLocation()); }
//跟踪顾客货物的关键装卸事件 public boolean isUnloadedAtDestination() { for (HandlingEvent event : this.getLnkDeliveryHistory().eventsOrderedByCompletionTime()) { if (HandlingEvent.Type.UNLOAD.equals(event.getType()) &&hasArrived()) { return true; } } return false; }
…… }
当领域模型Cargo出来以后,下一步就是使用Jdon框架来将其运行起来,因为Jdon框架分为领域模型和组件技术等两个部分,Cargo无疑属于@Model模型架构,我们只要给模型加上@Model,就能让Cargo的对象生活在内存缓存中。
@Model
public class Cargo { }
面向DDD的事件驱动架构
为了更好地突出应用需求为主,Jdon框架采取面向DDD的主要设计思想,主要特点是常驻内存in-memory的领域模型向技术架构发送事件消息,驱动技术架构为之服务,如同人体的DNS是人体各种活动的主要控制者和最高司令,领域模型是一个软件系统的最高司令。
JF有五种模型组件,如下:
一. 实体聚合根对象元注释 @Model; 二. 服务Service 元注释 @Service; 三. 普通类组件构件 @Component;
四. 生产者Prodcuer-消费者模型 @send @Consumer; 五. 拦截器 @ Interceptor, 首先需要导入点 @Introduce; 所有都在 com.jdon.annotation.*包中。
这些模型组件其实有划分为两大类:业务和技术:
常驻内存@Model 领域模型,包括实体模型值对象和领域服务,与技术架构无关。相当于鱼;生存空间是缓冲器中
@Component 技术组件架构,用以支撑领域模型在计算机系统运行的支撑环境,相当于鱼生活的水。空间在Context container,例如ServletContext中。
两者以Domain Events模式交互方式:领域模型向技术组件发出异步命令。
实体模型
根据DDD方法,需求模型分为实体模型值对象和领域服务三种,实际需求经常被划分为不同的对象群,如Cargo对象群就是Cargo为根对象,后面聚合了一批与其联系非常紧密的子对象如值对象等,例如轿车为根对象,而马达则是轿车这个对象群中的一个实体子对象。
在Jdon框架中,实体根模型通常以@Model标识,并且要求有一个唯一的标识主键,你可以看成和数据表的主键类似,它是用来标识这个实体模型为唯一的标志,也可以使用Java对象的HashCode来标识。
Jdon框架是实体模型的主键标识有两个用处:
首先是用来缓存实体模型,目前缓冲器是使用EHcache,可以无缝整合分布式云缓存Terracotta来进行伸缩性设计。
只要标识@Model的实体,在表现层Jsp页面再次使用时,将自动直接从缓存中获得,因为在中间业务层和表现层之间Jdon框架存在一个缓存拦截器CacheInterceptor,见框架的aspect.xml中配置。
为了能够在业务层能够使用缓存中的模型,需要在业务层后面的持久层或Repository中手工加入缓存的Annotation标签,如下: