由标识定义,而不依赖它的所有属性和职责,并在整个生命周期中有连续性。这句话在初看的时候非常晦涩,简单来说,就是一个标识没变的对象,在其他自身属性发生变化后,它依然是它,那么它就是实体;以上图为例,一个商品的商品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表示需要串行执行。