详解命令模式本质及其在高复杂调用中的应用案例—履约ERP架构总结

简介: 一、前言本文是基于作者在库存协同相关系统设计中使用命令模式并获得很好成果的分享想法而撰写的。命令模式是一种设计模式,总结了在特定场景下的最佳设计实践,它是一种间接经验。为了将这种间接经验变为我们可以使用的直接经验,我们需要做到两点:看清模式本质和灵活运用。模式本质:掌握一个设计模式的关键在于发现其核心关注点。每个模式都有一个关注点,例如命令模式的关注点是调用过程,而策略模式和状态模式的类图看起来相

一、前言

本文是基于作者在库存协同相关系统设计中使用命令模式并获得很好成果的分享想法而撰写的。命令模式是一种设计模式,总结了在特定场景下的最佳设计实践,它是一种间接经验。为了将这种间接经验变为我们可以使用的直接经验,我们需要做到两点:看清模式本质和灵活运用。

模式本质掌握一个设计模式的关键在于发现其核心关注点。每个模式都有一个关注点,例如命令模式的关注点是调用过程,而策略模式和状态模式的类图看起来相似,但它们的运作机制却完全不同:前者关注外部引起的算法变化,后者则关注内部状态的转变变化。通过找到模式的本质关注点,才能真正掌握它。

灵活运用在实际应用中,设计模式的实现方式可能会与教科书上的类图略有不同甚至完全不同。因此,我们不能一味地套用模式,而应该根据实际需求进行量身定制和改进。这意味着我们需要深入了解模式本质,然后根据自己的需求来适当调整模式的用法。有时,你可能会意外地创造出一种新的模式。

针对以上两点,本文首先特别地会从OOA(面向对象分析)的角度去介绍命令模式的模式本质(2.1. 封装调用),然后列举命令模式的不同玩法及其中的原理用作展示该模式的运用灵活性(3. 灵活运用)。最后一个真实的电商履约系统中的库存调用需求为案例(4. 应用案例),这个案例刚需要隔离的变化点是调用,非常适合命令模式,所以我认为会更佳让文章有可读性,废话不多,进入正题。

二、模式本质

2.1 封装调用

直接的调用:命令模式在设计模式的分类中属于行为型模式,它关注的是一种对象之间的调用行为,不管如何,调用行为必然涉及两个角色,他们就是:调用者(Invoker) 和 被调用者(Receiver),基本上他们对应的就是两个类对象,然后一个Invoker静态编码依赖Receiver进行调用。如果在不考虑其他因素的情况下,这两者的行为关系可以描述为下图:

然后我们来说一下上面的调用特点:

  • 上面的调用只有2个对象, 调用者(Invoker) 和  被调用者(Receiver);
  • 其中 调用者(Invoker) 是代码静态依赖  被调用者(Receiver)的;
  • 调用在上面是一个请求过程,这个过程在图中用虚线圈表示;

对象化调用:命令模式就是在以上场景下,把上图中虚线圈调用这个调用过程给显式化、抽象化、实例化。本来只是一个调用的过程,我们把它(这个过程)刻画出来封装为一个具体的对象(Concrete Command),这个对象就是命令对象。调用方将会利用命令对象这个代理来帮忙调用被调用方,所以以上的调用过程就变为了下图所示:

    

这样一来,调用方和被调用方就没有了直接的耦合关系,也就是说他们 “解耦” 了,概括一下这个对象的特点:

  • 我们多了一个新的具体 命令对象(Concrete Command) 对象的职责就是代表调用请求;
  • 命令对象持有被调用方Receiver的引用和请求参数,并知道如何执行,被调用方可以被参数化设置到命令对象中;
  • 静态代码依赖变成了 调用者(Invoker)依赖 命令对象(Concrete Command), 命令对象(Concrete Command)依赖 被调用者(Receiver)

隔离调用变化:把调用封装起来后,我们解开了调用者(Invoker)和被调用者(Receiver)依赖,那么就可以轻易的允许调用发生变化,这一点很重要,我们很多时候封装调用都因为调用本身在未来容易发生变化,那么调用可能会有哪些变化呢?以下列举从被调用方、调用方、调用本身三个不同的角度各列举一些变化:

  • 调用方变化:不同场景下, 调用者(Invoker)需要调用不同的  被调用者(Receiver);
  • 被调用方变化:被调用对象的方法发生变化,例如换了一个新版本API;
  • 调用的本身变化:请求需要延迟执行,或者一次调用突然需要调用2个 被调用者(Receiver);

使得调用的变化可以做到开闭原则的方式很简单,我们给命令对象实现一个命令接口 (Command),让调用者代码只依赖命令接口,这个接口的每一个具体的命令对象代表了一种变化,真正执行的具体命令是可以在运行态确定的,调用方不再依赖具体的调用命令:

提出变化的角色:如果读者对控制反转比较熟悉的话,那么就很自然知道上述中要实现隔离变化的代码编写,其实就是面向超类型编程。面向超类型编程是把设置实例的控制权交给了依赖关系中的最外层(也就是细节层),在命令模式中,我们把这一层称之为客户端(Client)。换句话说,把调用者(Invoker)、 被调用者(Receiver)和命令对象(Concrete Command)隔离开为互相独立的组件后,自然也需要一个角色去组织起来,道理很简单,积木也是需要有人搭才能千变万化,想变成怎样就是客户端(Client)的需求了。

现在我们又多了一个角色客户端(Client),整个命令模式的基本参与者都全了,可以整体看一下命令模式的类图。

要掌握命令模式,我们还得懂得如何灵活变化去运用它,下面我们看看命令模式的一些基本玩法。上面是一个比较标准的命令模式的类图,上图中可能大家会比较疑惑的一个点就是是客户端(Client)并没有依赖调用者(Invoker)。其实这段依赖不是必须的,

三、灵活运用

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. —— From GoF

设计模式提出者“四人帮”对命令模式的总结非常精辟:参数化请求、记录请求、队列化请求,还可以支持撤销操作。另外还有一种说法是:回调请求就是命令模式的面向对象版。可见在命令对象上面做文章,可以衍生出多种玩法,本章将会详细列举常见的几种。

3.1 组装命令

命令模式最大优点是解开调用者和被调用者(接收方)的耦合,因此我们可以轻易在一个调用中更换调用请求的接收方,而解开后的组装方式有两种:

  1. 第一是在Client角色中进行静态代码编写;
  2. 第二种是实现配置化在运行时动态组装;

其中第二种动态组装和拓展点的实现原理是一样的,会根据动态的参数来决定具体执行的命令。我把这种组装的原理描述为下图:

目前有不少框架有类似拓展点的功能的实现,如TMF,IFBF,COLA等。利用拓展点的功能帮忙,我们可以轻松实现以上的方案,当然我建议自己在项目种开发一套,因为你随时都有可能面对框架做不到的变态需求。

3.2 记录命令

如果我们把调用记录下来,我们就能完成很多不可思议的事情。调用能被记录下的信息主要有:调用的顺序、调用的出入参数、调用的状态等。有了历史记录,就可以对调用的历史执行进行回放或者倒退。下面分别介绍 命令撤销 和 命令日志 的两种回放玩法。

命令撤销:命令撤销是发生在命令簇(4.3节有介绍,表示关联关系的命令组)中的一种需求。其实它的原理非常简单,因为命令基本会改变状态,我们给命令接口一个反向的恢复状态的方法(如下面的undo),并且把调用过的命令对象都记录下来,就可以在一个撤销按钮中完成状态恢复操作。(读者可以参考Head First Design Parrtern命令模式的撤销操作)

  • 调用者具有多个命令插槽,每个插槽可以设置一个命令实例,共同形成一个命令簇(是个List);
  • 调用者维护一个栈,其实是命令插槽的调用历史栈,每一次调用者执行命令就把该命令入栈顶;
  • 用户需要倒退命令的时候,将栈顶命令退栈,并执行undo方法,利用栈后进先出的特点完成了历史状态回退功能;

命令日志:我们看一个利用函数式编程的不变性来恢复数据的原理,即在不同的时间或者空间节点中只要执行相同的事件函数调用,就一定会达到一个相同的状态。这个原理被广泛应用于很多系统中,例如数据库系统使用binLog事件和数据快照进行备份恢复,Redis使用RDB快照和AOF事件进行数据恢复,Excel文档数据保存等都是经典的应用场景。

具体来说,对于大型的数据结构而言,我们难以每时每刻都快速存储下它的状态,为了完成记录每时每刻的状态,我们可以通过上次检查点(CheckPoint)之后的所有操作命令(包括参数)都记录下来,当我们需要恢复数据结构的状态的时候,只需从检查点开始按顺序应用这些操作即可。

3.3 组合命令

如果一个命令设计的本身,就是用作封装其他不同命令的组织,我们称这个命令为宏命令。宏命令是一个通用的命令实现类,它类似一种组合模式,持有其他命令的引用,并依次执行他们。因此宏命令最大的作用是我们可以在一次调用中,调用多个命令,宏命令代码如下:

public class MacroCommand implements Command {
  
  Command[] commands;
  
  public MacroCommand(Command[] commands){
    this.commands = commands;
  }
  
  public void execute(){
    for(int i = 0; i < commands.length; i++){
      commands[i].execute();
    }
  }
  
}

特别注意:特别注意这个模式的特点是宏命令完全是不需要特殊开发的,它是一个可以组合其他命令的容器命令,也等同于普通的命令。只要对3.1节中命令组装工厂进行适当改造,我们也可以把宏命令用在动态的命令组装上,他可以让调用者在一次调用中完成多个调用请求。4.1 节,我们将会把宏命令用到实际的例子中。

3.4 异步命令

如果场景允许,我们可以把同步命令设计为异步命令。对于调用者和被调用者而言,一般同步的都是强依赖,而异步则是弱依赖,因此这个解耦很大程度上是一个设计问题。同时我们也应该注意到,异步命令设计有着极大的好处也有自身的缺点。

  • 同步调用需要阻塞流程,异步调用不阻塞,并立即返回对性能友好;
  • 异步具有削峰填谷的能力,可以堆积命令调用,用时间换计算资源;
  • 同步编程简单,异步编程比较复杂;
  • 同步的程序状态追求强一致性,异步的程序状态追求最终一致性;

队列调度:异步命令对象本质上打包了运算块(参数+接受者+一组操作),所以它可以被传来传去,并延迟执行。例如客户端/调用者可以设置好命令submit到某个队列中(具体实现产品可能是一种中间件,或者定时器),让其他调度者(不同线程)从队列中获取命令执行。

回调通知:在异步编程中,如果我们想要得到异步的调用结果,我们可以设置一个回调函数,让异步过程执行完毕后通知发起异步的一方。如果这个回调函数设计为接口,并在接口的实现中封装真正的接受者和其操作,这正好就是命令对象的用法,只是看问题的角度不同,所以可以认为命令模式是回调的面向对象的版本。

四、应用案例

介绍完上面的命令模式本质和基础玩法后,这节将介绍一个近场履约中台域的真实案例。该案例应用到了以上提到的组装命令、宏命令、队列化请求等玩法。近场履约域是一个可以承接多个商家的近场零售商品货物的配置需求,其中对商品的物流操作的调度管理有多个通用的节点,包括创单、出库、揽收、运送和妥投等,而这些节点需要去驱动库存的信息流变化,下面从出库节点的【仓单打包出库】服务作为的例子介绍履约与库存的关系。

4.1  识别调用变化

例子:我们用一个设计良好的领域服务组件开始介绍,如下图所示:假设我们有一个「仓单打包出库」的通用领域服务,完成仓单出库履约系统要做的事情有:

1、设置状态:设置仓单为出库状态;

2、记录数据:记录真实的出库数量;

3、操作库存:把占用的业务库存正式扣减掉;

4、推进履约单;推动对应的履约单状态;

库存调用的变化:现在有两个不同的业务身份(淘宝商家、外部商家),他们在「仓单打包出库」操作库存是调用不同的库存系统的,如果是直接硬代码编写(大家熟知的if大法),就需要每一次不同的业务身份变更,都要变更这个领域服务。这样的系统是无法维护的,因为履约域具有数百个业务身份,而且所对接的库存系统也是多种多样。

结合上图所示,一个类似这种「仓单打包出库」服务的库存操作步骤可能存在哪些变化点呢?

  1. 被调用者变化:针对一个履约域的领域服务(如上面的【仓单打包出库】),不同的业务身份都可能调用不同的库存系统;
  2. 调用者变化:针对同一个库存系统,它可能会被履约域中的其他领域服务调用,如【缺货处理】、【妥投处理】等;
  3. 调用参数变化:针对同一个库存系统,不止有扣减,还可以有其他调用类型和参数,例如占用、释放、加在途等等;
  4. 调用过程变化:上面库存的调用,可是需要一次性调用不同的库存系统组合,2个或者3个都有可能;

4.3 实现命令组装

我们意识到了调用的变化,就需要完全的解开库存系统和履约系统的耦合。首先设计一个库存系统代理接口Inventory Receiver,接口的每一个实例都是被调用者(Receiver),它用作封装库存系统提供的API接口,并代表着一个库存系统的一种调用类型。而调用者就是履约域的各个节点,然后,我们再把调用的过程实例化为命令(Command),整体做成了一个完成的命令模式。

调用配置化实现:设计为命令模式后,【履约单创建】领域服务成为一个通用的服务,每一步都抽象出一个稳定的步骤,远离具体细节的变化。其中第三步:操作库存,则调用的是库存命令组装服务。这个组装服务实现了根据不同业务身份创建拥有不同Receiver的库存命令实例,整体一个调用变化为下图所示:

正如3.1节提到,目前也有很多框架都能轻松做到这种动态组装,当然手写一个这样的机制也不是什么难事。具体的运行机制很简单,我简单描述如下:

  • 右边一个通用的领域服务有4个步骤,其中第三步「操作库存」调用的是一个通用的 库存组装服务
  • 左边的实现A、B、C是封装了具体库存系统API的Receiver,他们都实现了接口: Inventory Receiver
  • config.xml是bean的装配配置文件,库存组装服务在运行时根据不同的商家身份,获取对应的Receiver实例设置到Concret Command中成为库存操作命令,并执行该命令;

组合调用:注意,上面的案例中,淘宝商家不仅要调用AIC系统,还需要调用IPM系统,这要求我们在一个Command接口的一次调用中实现AIC和IPM2个系统的调用,我们有两种实现方式:

  1. 方式1:在 Command的接口实现中,把调用AIC Receiver和调用IPM Receiver的代码都写完,提供一个臃肿的接口实现;
  2. 方式2:设计一个 宏命令模式实现(机制见3.2节介绍):
    • 把AIC Receiver和IPM Receiver 添加到到宏命令容器中;
    • 宏命令接口的逻辑就是依次调用宏命令容器里面的命令;

针对组合调用的需求,上面的方式1的接耦程度是不如方式2的,因为如果出现某个业务身份在该领域服务下仅仅只需调用AIC或者IPM的情况,或者又有需要在一个Command接口实现中调用3个库存系统,方式1都需要开发代码,而方式2仅仅配置即可,所以上面的实现2不仅可以代替方式1,还更具有灵活性,这就是松耦合的魅力。

4.2 识别命令簇

在大家熟知的《Head First Design Parttern》书籍中,里面例子重点介绍了一种「遥控器」的设计,遥控器具有多个插槽,也就是可以在一个调用者里面设置多个命令,形成一个命令簇。如果我们能在实际应用中发现这种调用组,而且他们具有关联关系,那么命令模式就可以把这种命令簇及其关联逻辑封装起来,用作应对软件变化。

库存命令簇:上面只给到一个领域服务,但是一个完整的履约域是具有多个状态节点的,而其中在一次履约过程中就有不少节点中的领域服务需要和库存系统交互,例如【履约单取消】、【仓单打包出库】、【缺货处理】、【妥投处理】、【退货回仓】等等,他们不仅有顺序关系,而且通常在设计编码中都有很多共同逻辑,这些有相关性的调用集合,就是一个命令簇。而且,更深一层考虑,他们本身就可以形成一个子领域,特点如下:

  • 业务身份:在库存调用子领域应该有独立的业务身份,并业务身份为维度组织命令的配置,形成关联命令簇;
  • 库存流程:一个业务订单完整的生命周期中,关联的库存的调用组必然按顺序调用,因此命令簇存在顺序关系,形成库存调用流程;
  • 库存跟踪:库存跟踪是综合了库存流程的实例化和数据化的结果,在做订单库存查询的时候非常需要,设计上不要把这个责任放在订单上;
  • 监控运维:考虑调用组和多业务身份,调用量会变得极大,那么系统异常问题、库存不足问题、库存查询问题等就需要用到系统自动化级别的监控运维手段,而命令簇也会是运维自动化的基本维度之一;

//命令簇与执行流程图

配置命令簇:如下表所示,每个业务身份都有自己的调用节点组,一个组形成了完整的调用流程。作为一个履约中台系统,经常会新加或者减少业务身份,因此配置化方式组织命令簇及库存调用流程就显得非常必要,当开展新业务的时候,只需一个新的组合配置即可支持,符合软件开发开闭原则,配置化具有以下特点:

  • 针对履约域的一个 节点(如【履约单创建】),不同的业务身份都可能调用不同的库存系统(下面的AIC\TIC\GSI);
  • 不同业务身份即使使用同一个库存系统(如AIC系统),他们的库存协调所需要的命令组合也可能是不一样的;
  • 针对同一个节点(如【履约单创建)和同一个库存系统,不同业务身份调用的参数也可能不一样,因此参数也可以配置化;

空命令(NoCommand):上表所示,一行代表一个命令簇,一个命令簇可能在某个节点(如「缺货处理」)是不做任何事的,这个时候我们可以用到NoCommand。NoCommand 对象是一个空对象的例子,当你不想返回一个对象的时候,空对象就很有用,如上图所示,「外单」的业务身份在「缺货处理」这个节点上,是不需要执行库存调用的,但返回null给调用者就会出现异常,所以这个时候我们可以把不调用任何库存系统的NoCommand实现返回。

库存的生命周期:我们以业务身份为维度组织了命令簇,实现了命令簇组织的配置化,并且以命令簇的顺序执行特性绘制了库存执行流程。但这些都是静态的配置信息,对于我们观察和掌控实际的运行情况没有太大帮助,为了掌控这些静态配置和流程的每一次执行状态,我们必须把他们实例化。实例化对于运维和日常排查问题帮助极大。

因为命令模式,我们实例化了库存调用,但对于命令簇的实例化还需要刻画一整个订单执行的生命周期。我们还差2和信息,生命周期的当前状态和生命周期的库存执行流水。我们可以用库存跟踪单刻画一个订单实例当前状态。用命令的一次执行结果作为库存的执行流水。并用库存跟踪单把业务履约单和命令对象、命令流水等库存的串联关联起来,串联的ER图如下:

库存跟踪单可以告诉我们的信息大概有以下几点:

  • 该订单命中了哪个业务身份配置;
  • 该订单库存生命周期有哪些调用组,调用的顺序是怎样的;
  • 该订单当前在执行哪个调用,该调用是被什么履约域事件触发的;
  • 该订单历史上执行过哪些调用(命令),调用的出入参是什么,是否成功等;

命令簇执行顺序:另一方面,库存的跟踪还包括保证正确的库存调用顺序,例如某个业务的调用组的执行流程如下:

假设以上的调用都是异步调用,通过接收履约域的标准异步消息进行,因为是异步,所以就有可能「退货」消息先到,然后「妥投」消息后到,发生这种情况,就会导致库存调用出现各种可能的问题。解决该方法也很简单,利用一个可以处理当前状态和事件的库存状态机+延迟执行就可以保证流程的正确执行。

4.3 识别调用边界

在上一节中,我们把范围拓展到了调用组合,并且围绕调用组挖掘了不少的库存需求,讨论了做一个库存协同系统的必要性。那么这个系统具体的功能和设计是怎么样的呢?它如何利用封装的命令去完成我们对库存管理的需求的呢?本小节即介绍这些内容,我们可以从中体会到封装调用的强大。

库存界限上下文:以命令簇为核心,我们发现了很多相关性的,界限上下文是DDD的提供的一种解空间战术,上面我们围绕调用组进行分析,发现库存调用其实是有一个子领域的。并且针对库存的调用还要考虑到监控、运维、管理等的模型设计和代码编写,考虑到这些耦合性后,我们为库存处理成立一个独立的界限上下文,因此我们可以在该上下文中对库存进行分析设计,并做成一个库存协同系统。

库存调用协同系统:库存调用作为履约系统的子域属于一个小分支,并不在核心流程内,即使不做库存操作,也不会影响一个履约主流程的运行,所以他属于弱依赖,而且除占用一个节点外,其他库存操作都可以接受弱一致性。再综合上面上下文中的大规模的配置化管理、库存的流程管理、库存的跟踪、监控、运维后,我们完全有理由为这个上下文建立一个独立部署的系统,其作用类似精卫、中间件的作用。

命令调用可视化:在一个调用如此庞大的系统,即使是在日志加加了trace日志作为跟踪,可能也容易造成混乱,让运维人员在库存查询时变得很痛苦,但如果我已经用库存跟踪单刻画了所有调用的状态信息,那么我基本可以由一个订单id,找到该库存跟踪单,并可以在数据库中查到该跟踪单的所有库存调用命令和调用流水。我们把这些数据可视化,将会节省运维和开发排查问题的大量时间。

调用监控运维:方法调用都需要面临一个调用失败的问题,失败的原因可能有业务异常、系统异常,这些异常可能是可以重试成功的,有些则无法重试成功,只能人工插入管理。在一些比较小的系统,或者调用量不大的系统,发生这样的问题次数不多,我们可以通过系统日志简单运维,但像履约域每日上百万,超千万级别的调用,量表到质变,管理运维就是一个新的问题了。

  • 调用状态记录:那么这些调用异常的状态应该记录在哪里?现在我们有一个很好的答案就是用 命令对象(Command),并可以把命令对象持久化到数据库中等待使用。
    • 我们记录调用的状态包括很多,调用的出入参数、命中的业务配置,触发调用的事件等等
  • 调用失败重试:对于系统宕机,网络异常等调用失败问题,我们可以通过重试一定次数来解决,而重试本身也是一个问题,从失败记录、批量发现、定时重试都可以通过一个统一的模块管理,和业务流程无关。
  • 调用异常报表:对于无法重试成功的异常调用,简单的日志告警容易使人疲劳,或许这个时候定期出一个调用异常报表更适合,在数据库中的命令对象离线化后,做这种事就很简单。
  • 调用数据订正:当我们发现库存调用问题后,我们很大情况下解决方案是需要订数据,然后重试,因为命令对象记录了调用参数,因此修改参数重试命令也是简单可行。

我们现在从调用的监控运维视角,看一下系统的运作流程:

新业务开发流程:现在我们回归业务,一个系统设计再厉害,如果在应变新需求的时候需要大动干戈的修改旧代码,那这个系统必然是一个设计失败的系统。所以我们现在讨论一下库存系统的新需求接入流程,用以评估系统的可用性,我们将分别从命令变化的几个方面的需求进行讨论:

  1. 新业务身份接入:履约中台系统,承接各种订单的近场履约服务,而业务总是有新模式,所以就会有新的业务身份,也可能会产生新的库存调用变化,但因为履约域、库存系统都是通用的,消息/接口也是标准的,所以新的业务身份,只需要加配置文件(配置调用组、流程、调静态用参数)即可运行。
  2. 新库存系统接入:如果新的业务身份需要调用的库存系统之前没接入过呢?对比新业务身份接入,我们只是缺少配置的Receiver而已,因此只需要创建新的Receiver实例,封装需要接入的库存系统API,即可完成需求。完全也是拓展化开发。
  3. 新调用节点的接入:一个新的业务身份要调用新的节点,或者某个旧的业务身份需要加一个新节点,因为履约域的系统是标准的消息,所以也不需要修改,配置即可。
  4. 新的调用系统的接入:如果除了履约域要接入外,其他系统也需要接入库存调用系统,那么就需要做一个防腐层,把新系统的消息转化为库存协同系统的标准消息即可。

库存系统的所有变化,无非就是以上几个方面,开发应对基本的新代码及新配置,做到了节点,Receiver的完全解耦复用,同时复用系统所有能力(如可视化、监控运维等)。而且最重要的是,即使新增平台的能力,也是所有业务可以享用的 。

五、结束语

命令模式的玩法其实熟悉之后,也就是那么几种形式,但应用场景是多种多样的,当发现一个应用场景主要变化点在调用的时候,我们就可以考虑是否可以把命令封装起来,然后在应对需求的时候,看能否通过记录命令、配置命令、组合命令、异步命令等方式进行设计编码。命令模式如此,其他设计模式也是如此。

另外,一个设计模式的应用范围也不要限制在一个独立部署的系统内,也可以拓展到系统之间的设计中,例如经典的观察者模式,在系统之间的应用就非常之关,本文案例中的命令模式也是跨系统的应用。其实甚至可以跨组织架构之间的应用。为什么不需要被系统所限制呢?因为其实系统与系统之间的调用本质依旧是组件和组件之间的调用,只是其中的边界和方式变了。

最后,本文及案例系统能顺利完成,依旧要感谢我的师兄萧方、主管三郎、家长养志的大力支持,作为一起开发该系统的成员,萧方师兄给予了我完全的方案设计权利,才让我有更大的发挥。三郎和养志作为履约域系统的把控人,有先见之名提出独立出库存ERP子域和系统的需求,才让履约域的库存接入做到“接得快”、“改的少”、“高可用”的目标成为可能。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
16天前
|
设计模式 Java API
Java 可扩展 API 设计:打造灵活的应用架构
【4月更文挑战第27天】设计可扩展的 API 是构建灵活、易于维护的应用程序架构的关键。Java 提供了丰富的工具和技术来实现这一目标,使开发者能够构建具有高度可扩展性的应用程序。
38 4
|
1天前
|
Cloud Native 安全 持续交付
构建未来:云原生架构在现代企业中的应用与挑战
【5月更文挑战第12天】 随着数字化转型的浪潮不断冲击传统IT架构,企业亟需灵活、高效且可扩展的技术解决方案以保持竞争力。云原生技术作为一种新兴的系统构建方式,以其独特的弹性、微服务和持续交付等特性,成为推动企业快速响应市场变化的关键因素。本文将深入探讨云原生架构的核心组件,分析其如何促进企业的敏捷性,以及在实施过程中可能遇到的挑战和解决策略,为企业采纳云原生技术提供参考。
|
2天前
|
Cloud Native Devops 持续交付
构建未来:云原生架构在现代企业中的应用与挑战
【5月更文挑战第11天】 随着数字化转型的深入,企业对技术的敏捷性、可扩展性和成本效益提出了更高的要求。云原生架构作为一种新兴的设计理念和实践方法,正逐渐成为推动企业技术革新的关键力量。本文将深入探讨云原生架构的核心组件,包括容器化、微服务、持续集成/持续交付(CI/CD)以及DevOps文化,并分析它们如何共同作用于企业的IT基础设施,实现灵活、高效的运营模式。同时,我们也将识别在采纳云原生技术时面临的主要挑战,并提出相应的解决策略,以帮助企业顺利过渡到云原生时代。
|
3天前
|
弹性计算 负载均衡 容灾
应用阿里云弹性计算:打造高可用性云服务器ECS架构
阿里云弹性计算助力构建高可用云服务器ECS架构,通过实例分布、负载均衡、弹性IP、数据备份及多可用区部署,确保业务连续稳定。自动容错和迁移功能进一步增强容灾能力,提供全方位高可用保障。
15 0
|
4天前
|
运维 Cloud Native 持续交付
构建未来:云原生架构在现代企业中的应用与挑战
【5月更文挑战第9天】 随着数字化转型的浪潮席卷全球,企业正迅速采纳云原生技术以实现敏捷性、可扩展性和弹性。本文深入探讨了云原生架构的关键组件,包括容器化、微服务、持续集成/持续部署(CI/CD)和DevOps文化,并分析了这些技术如何帮助企业加速产品上市时间,提高运营效率,并最终实现业务目标。同时,文章也识别了企业在采纳云原生实践中可能面临的挑战,如安全性考量、团队技能提升和复杂的网络管理,并提出了相应的解决方案和最佳实践。
|
7天前
|
监控 负载均衡 API
微服务架构在现代企业中的应用与挑战
微服务架构已成为现代企业构建灵活且可扩展软件系统的首选。然而,随着其应用的普及,企业也面临着一系列新的挑战。本篇文章将探讨微服务架构的优势、实施时遇到的问题以及解决这些问题的策略。
|
7天前
|
Kubernetes Cloud Native 持续交付
构建高效云原生应用:Kubernetes与微服务架构的融合
【5月更文挑战第6天】 在数字化转型的浪潮中,企业正迅速采纳云原生技术以实现敏捷性、可扩展性和弹性。本文深入探讨了如何利用Kubernetes这一领先的容器编排平台,结合微服务架构,构建和维护高效、可伸缩的云原生应用。通过分析现代软件设计原则和最佳实践,我们提出了一个综合指南,旨在帮助开发者和系统架构师优化云资源配置,提高部署流程的自动化水平,并确保系统的高可用性。
29 1
|
12天前
|
Cloud Native 安全 持续交付
构建未来:云原生架构在现代企业中的应用与挑战
【5月更文挑战第1天】 随着数字化转型的深入,云原生技术以其灵活性、可扩展性和敏捷性成为现代企业IT架构的核心。本文将探讨云原生架构的关键组件,包括容器化、微服务、持续集成/持续部署(CI/CD)以及DevOps实践,并分析它们如何共同塑造企业的运营模式。同时,文章还将讨论在采纳云原生过程中企业可能遇到的挑战,如安全性问题、技术复杂性以及组织文化的转变,并提出应对策略。
28 8
|
13天前
|
前端开发 JavaScript 安全
【TypeScript技术专栏】TypeScript在微前端架构中的应用
【4月更文挑战第30天】微前端架构通过拆分应用提升开发效率和降低维护成本,TypeScript作为静态类型语言,以其类型安全、代码智能提示和重构支持强化这一架构。在实践中,TypeScript定义公共接口确保跨微前端通信一致性,用于编写微前端以保证代码质量,且能无缝集成到构建流程中。在微前端架构中,TypeScript是保障正确性和可维护性的有力工具。
|
14天前
|
Cloud Native Devops 持续交付
构建未来应用:云原生架构在现代企业中的实践与挑战
【4月更文挑战第29天】 随着数字化转型的加速,企业正迅速转向云计算以支撑其业务敏捷性和创新。云原生技术,作为推动这一转型的关键因素,正在重新定义软件开发和运维模式。本文将深入探讨云原生架构的核心组件,包括容器化、微服务、持续集成/持续部署(CI/CD)以及DevOps文化,并分析这些技术如何帮助企业实现弹性、可扩展和高效的应用部署。同时,我们将讨论在采纳云原生实践中所面临的挑战,包括安全性、治理和人才缺口等问题。