闽公网安备 35020302035485号


由标识定义,而不依赖它的所有属性和职责,并在整个生命周期中有连续性。这句话在初看的时候非常晦涩,简单来说,就是一个标识没变的对象,在其他自身属性发生变化后,它依然是它,那么它就是实体;以上图为例,一个商品的商品id没变,即使它的标题改了,图片改了,优惠信息变了,它发生了翻天覆地的变化,但它依然是它,它有唯一的标识来表明它还是它,只是一些属性发生了变化;
通过这种方式来识别实体的目的,是因为领域中的关键对象,通常并不由它们的属性定义,而是由可见的/不可见的标识来定义,且有完整的生命周期,在这个周期内它如何变化,它都依然是它;通过这种方式识别出实体这种领域关键对象,也是领域驱动设计和数据驱动设计最大的差别,数据驱动设计是先识别出我们需要哪些数据表,然后将这些数据表映射为对象模型;而领域驱动设计是先通过业务模型识别出实体,再将实体映射为所需要的数据表。

仓库是可持久化的领域对象和真实物理存储操作之间的媒介,随意的数据库查询会破坏领域对象的封装,所以需要抽象出仓库这种类型,它定义领域对象的获取和持久化方法,具体实现不由领域层感知;至于具体用了什么存储,如何写入和查询,是否使用缓存,这些逻辑统一封装在仓库的实现层,对于后续迁移存储、增删缓存,都可以做到不侵蚀业务领域。
我们的模型中依赖外部查询获取的商品模型,这个模型中有商品标题、商品图片、店铺名称这几个信息,那么我们需要在Domain层定义一个商品类ItemInfo,包含这几个属性,然后在Domain层定义一个获取ItemInfo对象的服务接口,比如叫ItemFacade,方法是getItemInfo;接下来我们需要在Infrastructure层实现Domain层定义的这个接口,比如具体的实现是依赖Ic的接口,将ItemDO转换为Domain层的ItemInfo;可以发现,这样的设计让Domain对商品信息的获取源是无感的,当我们的能力需要部署到海外,或者IC某一天进行了重大改革,需要对模型进行大改,那么我们只需要重新实现Infrastructrue中ItemFacadeImpl即可。这个思想其实就是依赖倒置的思想,稳定不应该依赖变化,变化应该依赖稳定;因为第三方的变化方向是无法把控的,它的变化不应该侵入到我们的领域知识内部。



public interface AbilityNode<C extends Context> {
/**
* 执行一个渲染节点
* @param context 执行上下文
* @return 是否还需要继续往下执行
*/
boolean execute(C context);
基于这个接口,我们可以实现非常多原子能力节点。java中,这些能力节点作为bean由spring容器统一管理,运行时取出即用。在一个业务场景下,我们的能力点往往会非常多,那么我们就需要对他们进行基于业务场景的阶段划分,并分门管理;比如我们在前台投放场的实践中,按照召回、补全、过滤、排序、渲染,划分了五个阶段,每一个能力点被归类到其中一个阶段中进行管理。public abstract class AbilityChain<C extends Context<?>> {
@Resource
private ThreadPool threadPool;
public abstract C initContext(Request request);
public Response execute(Request request) {
//获取渲染上下文
C ctx;
try {
ctx = this.initContext(request);
if (ctx == null || ctx.getOrder() == null || CollectionUtils.isEmpty(ctx.getOrder().getNodeNames())) {
return null;
}
} catch (Throwable t) {
log.error("{} catch an exception when getRenderContext, request={}, e="
, this.getClass().getName(), JSON.toJSONString(requestItem), t);
return null;
}
try {
//执行所有节点
for (List<String> nodes : ctx.getOrder().getNodeNames()) {
List<AbilityNode<C>> renderNodes = renderNodeContainer.getAbilityNodes(nodes);
boolean isContinue = true;
if (renderNodes.size() > 1) {
// 堆代码 duidaima.com
//并发执行多个节点
isContinue = this.concurrentExecute(renderNodes, ctx);
} else if (renderNodes.size() == 1){
isContinue = this.serialExecute(renderNodes, ctx);
}
if (!isContinue) {
break;
}
}
return ctx.getResponse();
} catch (Throwable t) {
log.error("RenderChain.execute catch an exception, e=", t);
return null;
}
}
/**
* 并发执行多个node,如果不需要继续进行了则返回false,否则返回true
*/
private boolean concurrentExecute(List<AbilityNode<C>> nodes, C ctx) {
if (CollectionUtils.isEmpty(nodes)) {
return true;
}
long start = System.currentTimeMillis();
Set<Boolean> isContinue = Sets.newConcurrentHashSet();
List<Runnable> nodeFuncList = nodes.stream()
.filter(Objects::nonNull)
.map(node -> (Runnable)() -> isContinue.add(this.executePerNode(node, ctx)))
.collect(Collectors.toList());
linkThreadPool.concurrentExecuteWaitFinish(nodeFuncList);
//没有node认为不继续,就是要继续
return !isContinue.contains(false);
}
/**
* 串行执行多个node,如果不需要继续进行了则返回false,否则返回true
*/
private boolean serialExecute(List<AbilityNode<C>> nodes, C ctx) {
if (CollectionUtils.isEmpty(nodes)) {
return true;
}
for (AbilityNode<C> node : nodes) {
if (node == null) {
continue;
}
boolean isContinue = this.executePerNode(node, ctx);
if (!isContinue) {
//不再继续执行了
return false;
}
}
return true;
}
/**
* 执行单个渲染节点
*/
private boolean executePerNode(AbilityNode<C> node, C context) {
if (node == null || context == null) {
return false;
}
try {
boolean isContinue = node.execute(context);
if (!isContinue) {
context.track("return false, stop render!");
}
return isContinue;
} catch (Throwable t) {
log.error("{} catch an exception, e=", nodeClazz, t);
throw t;
}
}
}
其中流程协议为:[
[
"Ability1"
],
[
"Ability3",
"Ability4",
"Ability6"
],
[
"Ability5"
]
]
其中Ablitiy3 4 6表示需要并发执行,Ability1、3/4/6、5表示需要串行执行。

