企业应用架构19:工作流人任务与版本演进
租赁申请需要店员审核,审核期间服务可能重启。后来流程增加主管复核,已经提交的旧申请是否也要补一道审核,不能只靠替换一个流程文件回答。等待中的实例、任务归属和流程版本都必须有持久记录。 本篇将两份 BPMN 模型部署到真实 Camunda 7.23 引擎,以 H2 文件数据库保存等待状态。第一次创建人任务后直接终止 JVM,再用新进程恢复,随后部署第二版,比较新旧实例的路径及普通代码的同一转换规则。 引擎承担等待和继续的执行 普通业务代码可以计算下一步是审核还是结束,但跨越数小时的人工等待还涉及任务持久化、恢复、查询和历史记录。工作流引擎把这些执行机制集中起来,领域规则仍需由应用明确提供。流程节点存在,不表示审批权限或租赁条件已经正确。 BPMN 是流程模型与表示规范;具体引擎只执行自己支持的元素和扩展。本实验使用开始事件、顺序流、人任务和结束事件,没有覆盖整个 BPMN 标准,也没有把能够解析 XML 等同于全部语义通过。OMG BPMN 2.0.2 Camunda 的 User Task 在流程到达时创建等待处理的任务,应用可以查询并完成任务。样本直接调用引擎 API 完成它...
企业应用架构18:Saga补偿与持久化恢复
设备预留已经提交,扣费却被拒绝,合同需要释放预留。释放也可能失败,甚至已经释放成功但协调器还没记录结果就退出。把这段逻辑写成一个 try-catch,只能处理仍在运行的调用栈,无法回答新进程该从哪一步继续。 本篇用三个独立 H2 数据库分别保存协调状态、库存操作和收费结果,主动在参与者提交后终止 JVM。恢复进程根据持久记录继续补偿,检查重复释放、迟到成功事件以及交付以后不能自动撤销的边界。 补偿是另一次业务操作 Saga 将长事务分成可分别提交的事务,并为需要撤销的步骤安排补偿。前面已经提交的结果不会因为后续某个进程抛异常而自动回滚,补偿也可能需要新的尝试。原始Sagas论文;Saga模式 设备预留的补偿可以是释放预留,但这并不等于时间倒流。预留期间其他合同可能已被拒绝,业务系统也可能产生审计或通知。实验只检验资源持有状态与释放次数,不声称所有外部观察都恢复到预留前。 协调器保存 R1、R2 的当前状态和每次迁移原因。库存数据库保存资源 held、releases 及操作编号;收费数据库保存操作编号和整数金额。三者通过各自 JDBC 连接提交,不共享一个能够整体回滚的数据库事...
企业应用架构17:Request-Reply关联与超时恢复
两个合同同时等待预留结果,第二个请求先收到答复,第一个请求在等待期限以后仍没有答复。按队列接收顺序配对,会把设备预留结果交给错误合同;把超时直接写成失败,则可能在设备已经预留以后再次发起预留。 本篇保留两条尚未完成的请求,使用真实消息通道逆序返回结果,并在请求进程退出后恢复关联状态。服务端已经提交而回复迟到的场景,通过查询持久化结果恢复,超时本身不承担判断业务成功与否的职责。 三种标识对应三种问题 Request-Reply 以一条请求和对应答复组织交互,Correlation Identifier 则让接收者判断答复属于哪次请求。这不要求答复按请求发送顺序到达,也不要求调用方始终占用一个线程等待。Request-Reply;Correlation Identifier 实验的合同编号为 R1、R2,请求关联编号为 C1、C2,发送消息编号为 M1、M2。合同编号用于业务查询,关联编号区分一次请求,消息编号识别一次业务报文。三者在这个最小样本里一一对应,却不应因此合并:同一合同可能经历多次查询或不同阶段的请求。 JMSCorrelationID 携带 C1 或 C2,消息正文携...
企业应用架构16:拆单路由与交付结果聚合
一份租赁合同包含相机与投影仪,两种设备由不同端点处理。相机完成消息到了两次,投影仪的结果一直没有到。如果聚合器只计算收到了几条消息,合同就可能在缺少一件设备时被标为全部完成。 本篇用真实 ActiveMQ 通道传递拆分任务和完成结果,H2 保存批次与明细。完成条件按合同、交付批次和设备任务判断,实验依次观察乱序、重复、缺项、超时及上一批次的迟到结果。 拆分之前固定完成条件 Splitter 把复合消息拆成可以独立处理的部分;Content-Based Router 根据内容选择通道;Aggregator 再按关联标识与完成条件收集结果。这里需要先定义“全部”,再决定如何拆分,否则最后一条到达的消息没有资格宣布整份合同完成。Splitter;Content-Based Router 实验在批次创建时保存预期任务数。合同 R1 的 B1 批次包含相机 E1 和投影仪 P1,预期为两项;合同 R2 的 B1 批次只有相机 E2,预期为一项。批次号可以在不同合同内重复,因此数据库主键由合同号和批次号共同组成。 每项任务也有独立标识,例如 R1/B1/E1。它既出现在持久化明细表,也作为...
企业应用架构15:Channel、Endpoint与Translator的真实消息边界
租赁合同引用设备 E1,维修系统识别的资产却叫 ASSET-E1。直接把合同对象序列化后发送,既暴露内部字段,也没有解决两套标识之间的关系。消息能够送达,只是集成的一部分;接收方还需要知道它属于哪种报文、该交给哪个端点,以及不能处理时保留在哪里。 本篇将设备维修申请放进真实 ActiveMQ 通道,经 TCP 交给独立消费进程,再将翻译结果写入 H2。消费者离线、broker 停启、未知版本、错误通道和毒消息分别运行,业务拒绝与基础设施重试保留不同证据。 通道与端点承担不同职责 EIP 的 Message Channel 讨论应用之间传输消息的通路;Message Endpoint 是应用与消息系统连接的边界;Message Translator 则处理报文格式差异。它们可以组合,但不能仅根据一条队列名称就认定转换规则已经存在。Message Channel;Message Translator 实验固定 maintenance.request 为维修申请通道。租赁方发送外部合同编号与设备引用,维修端点将 E1 翻译成 ASSET-E1,并生成维修申请文本。这个前缀规则仅服务于...
软件建模E08:用DMN真实执行资格规则与阈值变更
设备已经放行,距离预约开始还有一小时,是否允许进入下一步?如果政策要求至少提前一小时,答案是允许;政策改成两小时之后,同一输入就应被拒绝。用图表表达规则,必须能观察到这项变化影响实际求值结果。 本篇选择受限 DMN 决策表实验:两个输入、一个输出和三条规则。模型由真实 Camunda DMN 引擎解析执行,程序检查阈值边界、规则重叠和缺少输入的行为。样本没有启动完整流程平台,也没有调用租赁服务完成真实预订。 先确定需要执行的语义 一项租赁申请可以包含多种问题。领取、归还和通知需要描述先后动作;工作人员处理资料不完整的申请时,可能根据新材料选择后续工作;资格政策则根据输入条件得到判断。这些问题分别涉及流程、案例与决策建模,不应因为三者都能画图就互相替代。 当前问题只需要计算资格,没有等待、人工任务、消息关联或运行中的实例状态。因此实验选择 DMN 决策表,将操作顺序留在应用层。关于 BPMN 或 CMMN 的语义,本篇仅作范围区分,没有声称执行或验证过它们的引擎行为。OMG BPMN 2.0.2;OMG CMMN 1.1 模型使用 DMN 1.3 的 XML 命名空间。OMG 发...
软件建模E07:REA资源、事件与代理的交换语义
一笔租赁已经收取两百分钱,同时已经提供两小时设备使用服务。这两个事实有关联,但不能直接相加。四小时服务的承诺也不能因实际只提供两小时,就被改写成两小时。交换模型需要保存各自的资源、单位、参与方以及承诺与实际发生的联系。 REA 提供了一组观察经济交换的概念。本文依据原始研究辨认这些概念,再建立一个小型合成样本,检查部分履约、重复事件、超额履约和价格义务变化。脚本只是本篇受限解释的校验器,没有声明实现完整 REA 本体或会计系统。 原始概念与本篇的取舍 McCarthy 在一九八二年的论文中提出资源、经济事件、经济代理及其关系。作者网站提供原论文,并把后续扩展研究单独列出。Geerts 与 McCarthy 的扩展分析讨论了经济交换、对偶关系以及承诺等概念;承诺属于扩展部分,不能把所有后续机制都归到最初论文。作者资料与原始论文入口 资源关注具有经济价值的对象,事件描述经济变化,代理参与交换。对偶关系把一次交换中付出与获得的事件联系起来。扩展研究还区分已发生、已承诺与政策层面的可能或应当发生;这些区分有助于避免把愿望、义务和实际结果放进同一条记录。Geerts与McCarthy,扩...
软件建模E06:Feature Model与软件产品线
一套租赁软件同时提供门店自提、配送和跨境租赁,并不意味着每个交付版本都可以随意组合这些能力。配送需要地址,跨境需要配送与保险,取货方式又只能选择一种。只列出功能名称,无法解释为什么某个组合不能交付。 本篇把这些教学政策写成有限特性模型,枚举全部配置并检查反例。实验回答哪些产品组合被允许,不实现跨境业务,也不把一个特性名称视为已经交付的功能。 特性模型描述一族产品 FODA 原始报告通过领域分析寻找相关系统的共性与差异,并使用特性表达可选、必需及替代关系。它讨论的是一族系统的变化空间,不能只把一份待办事项表换个标题就称为产品线。Kang 等,FODA Feasibility Study,1990 租赁样本保留七个布尔变量:Rental、Booking、Pickup、Delivery、Address、Insurance、CrossBorder。Rental 是根,Booking 为根下面的必选能力。Booking 生效时,Pickup 与 Delivery 恰好选一个;Address、Insurance 与 CrossBorder 则可以按配置选择。 树状结构还不够。Deliver...
软件建模E05:本体、知识图谱与语义建模
租赁目录里只有一句“第二台相机属于相机类”,没有序列号。这可能表示资料还没收齐,也可能违反登记接口的必填规则。两种判断依赖不同问题:已有知识能够推出什么,以及当前数据是否满足交付要求。 本篇使用合成设备目录,实际运行 RDFLib、OWL-RL 与 pySHACL。实验既保存推导出的分类和关系,也保留缺字段的校验报告。它没有访问真实资产系统,更没有把图谱查询结果接入主线预订事务。 从关系事实到本体公理 RDF 图由主语、谓语、宾语三元组组成;命名资源使用 IRI,序列号这样的值可以使用字面量。这里的 Camera、Equipment、alice 和 camera1 都有独立标识,owns 表示主体拥有设备。名称的可读性便于人检查,机器依赖的仍是完整标识与关系。RDF 1.1 Concepts models/E05/rental.ttl 明确写出 camera1 是 Camera,Camera 是 Equipment 的子类,alice 拥有 camera1。数据没有显式写“camera1 是 Equipment”,也没有直接声明 alice 属于 Party。这给实验留下可以核...
深入 Play E01:Scala API 对照,Action、Future 与类型化请求
Java 方法返回 CompletionStage<play.mvc.Result>,Scala 方法返回 Action[A],其中异步处理函数产生 Future[play.api.mvc.Result]。两者能够提供相同的 HTTP 契约,但类型和组合方式并不相同。把一种 API 的 Result 直接赋给另一种,在本章的负例中会编译失败。 类型化请求还解决了另一个具体问题:身份信息如何经过 Action 组合进入异步计算。实验中的 Java 和 Scala 实现都切换到名为 api-worker 的线程,普通 ThreadLocal 没有随之传播,显式捕获的用户却保持正确。这个结果来自两个独立版本工程的真实生产 HTTP 请求。 固定两个组合,先比较 HTTP 契约 主对照固定 Play 3.0.6、Scala 2.13.15、sbt 1.10.7、JDK 21.0.11。另一个独立工程固定 Play 3.0.6 与 Scala 3.3.4,运行相同 Java、Scala 源码和同一套网络矩阵。Scala 3 构件实际包含 play_3-3.0.6、play-j...









