Spring Modulith 2.1:Agent 动作要落成可恢复的业务事件
Spring AI 2.0 让 Java 应用更容易构建 Agent,Spring Modulith 2.1 则提醒我们:Agent 的动作最终还是业务动作。业务动作需要事务、领域事件、Outbox、补偿、审计、模块测试和观测。
今天的核心结论很简单:不要让 Agent Tool 成为绕过业务系统的后门。让它调用受控命令,让命令发布领域事件,让事件发布注册表和 Outbox 管住后续副作用。这样 Agent 才能从“会调用工具”走向“能进入生产业务系统”。
分享日期:2026-06-17
主题:Java / Spring Modulith 2.1.0 / Spring Boot 4.1 / Spring AI 2.0 / Agent 动作治理 / Outbox / 事件发布注册表 / 可观测性
版本背景:截至 2026-06-17,Spring 官方参考文档显示 Spring Modulith2.1.0、Spring Boot4.1.0、Spring AI2.0.0均为 Stable 入口
1. 为什么今天值得关注
昨天我们看了 Spring AI 2.0 GA 的 Agent 治理边界:模型负责推理,应用负责执行,平台负责治理。今天更值得往下一层看:当 Agent 真的触发业务动作时,这个动作应该落在哪里?
一个成熟的 Java Agent 应用,不应该只把工具调用写成一段普通方法,然后在日志里留一句“模型调用成功”。只要动作会影响订单、库存、审批、支付、合同、工单、消息通知或外部 SaaS,它就必须进入业务系统熟悉的事务、事件、补偿、审计和观测链路。
Spring Modulith 2.1.0 正好把这个话题推到台前。官方文档显示,2.1.0 已经是稳定文档入口,并且在应用事件、事件发布注册表、事件外部化、Outbox、模块测试、Actuator 和 Micrometer 观测上形成了一条很完整的路线。
一句话:Spring AI 让 Agent 能调用工具,Spring Modulith 让这些工具调用变成可追踪、可恢复、可测试的业务事件。
2. 版本坐标:Agent 不能绕过领域模型
今天的技术组合可以这样理解:
- Spring AI 2.0.0:提供
ChatClient、Tool Calling、MCP、RAG、记忆和模型观测。 - Spring Modulith 2.1.0:提供应用模块边界、领域事件、事件发布注册表、事件外部化、Outbox、模块测试和模块观测。
- Spring Boot 4.1.0:提供自动配置、Actuator、Micrometer、事务、数据访问、消息、配置和运行底座。
- Spring Cloud:在多服务场景里负责 Gateway、Config、Vault、CircuitBreaker、Stream、Kubernetes 等分布式治理。
如果把 Agent 看成一个更聪明的 Controller,很容易写出这种结构:
用户问题 -> LLM -> Tool -> 直接改订单 / 调库存 / 发审批 / 发消息
这条链路短,但问题很明显:
- 工具调用和普通业务命令没有统一入口。
- 成功和失败只存在于一次模型交互里,缺少可恢复的业务状态。
- 外部消息发送失败时,很难知道是否应该重试。
- Agent 误判、重复调用或超时重试时,幂等边界不清楚。
- 审计只能看到“模型说了什么”,看不到“业务发生了什么”。
更稳的结构应该是:
用户问题 -> LLM -> Tool -> 业务命令 -> 领域事件 -> 监听器 / Outbox / 外部消息
Agent 的工具层只负责把模型意图翻译成受控业务命令。真正的业务状态变化仍然发生在领域服务里,并通过领域事件向其他模块或外部系统扩散。
3. Spring Modulith 2.1 这次最适合 Agent 的点
从 Agent 工程角度看,Spring Modulith 2.1.0 有五个点最值得关注:
- 模块边界验证:用
ApplicationModules.of(...).verify()检查模块之间是否存在循环依赖、非法内部包访问和未允许的模块依赖。 - 应用事件协作:模块之间优先通过领域事件交互,减少直接 Bean 调用造成的耦合。
- 事件发布注册表:事件发布会写入持久化记录,监听器成功后再标记完成,失败时可以保留待处理记录。
- Outbox 外部化:2.1 文档新增了通过 Namastack Outbox 和 JobRunr 支持更高级 Outbox 能力的说明。
- 生产级观测:
spring-modulith-starter-insight可以暴露模块结构 Actuator 端点,并对模块调用和事件发布注册 Micrometer 指标与 trace。
这套能力对 Agent 很关键。Agent 不稳定的地方不是“能不能调用 Java 方法”,而是“模型驱动的动作能不能在普通业务系统里被治理”。Spring Modulith 的价值,就是让 Agent 动作回到模块化单体和微服务都能理解的事件模型里。
4. 推荐架构:Agent 工具只发业务意图
flowchart LR
User[用户 / 业务系统] --> Gateway[Spring Cloud Gateway]
Gateway --> AgentApi[Spring Boot Agent API]
AgentApi --> ChatClient[Spring AI ChatClient]
ChatClient --> Tool[受控 Tool]
Tool --> Command[业务命令服务]
Command --> Aggregate[领域聚合]
Aggregate --> Event[领域事件]
Event --> Registry[(Event Publication Registry)]
Registry --> Listener[@ApplicationModuleListener]
Registry --> Outbox[Outbox]
Outbox --> Broker[Kafka / RabbitMQ / JMS]
Listener --> LocalModule[本地业务模块]
Broker --> External[外部服务 / 数据平台 / 通知系统]
Registry --> Metrics[Actuator / Micrometer / Trace]
这张图的核心原则是:
- Tool 不直接拼 SQL、不直接调用外部系统、不直接绕过权限模型。
- Tool 把模型意图转成业务命令,例如“申请退款”“创建审批草稿”“同步库存校验任务”。
- 业务命令服务负责鉴权、租户隔离、参数校验、幂等和事务。
- 领域事件表达已经发生或已经受理的业务事实。
- 事件监听器处理模块内或模块间协作。
- Outbox 负责把需要外部化的事件可靠发送到消息系统。
- 事件发布注册表负责记录哪些事件待处理、处理中、已完成或失败。
这比“Tool 里直接调用三个外部 API”慢一点,但可运维性完全不是一个级别。
5. 最小依赖组合
如果项目已经是 Spring Boot 应用,可以先引入 Spring Modulith BOM:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-bom</artifactId>
<version>2.1.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
按能力选择 starter:
<!-- 核心模块模型、事件注册表和 JDBC 持久化 -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-jdbc</artifactId>
</dependency>
<!-- 生产观测:Actuator + 模块结构 + Micrometer 观测 -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-insight</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 模块级集成测试 -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-test</artifactId>
<scope>test</scope>
</dependency>
如果要把事件发到消息中间件,再加对应外部化模块:
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-events-kafka</artifactId>
</dependency>
如果要启用 2.1 文档里提到的高级 Outbox 外部化,可以评估:
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-namastack</artifactId>
</dependency>
或:
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-jobrunr</artifactId>
</dependency>
普通项目不需要一开始全量引入。建议从 starter-jdbc + starter-insight + starter-test 开始,把事件记录、模块观测和模块测试先跑起来。
6. 建模:把 Agent 动作拆成命令和事件
先定义一个业务命令。注意,这不是模型输出的原始 JSON,而是应用内部已经校验过的命令对象:
import java.math.BigDecimal;
import java.util.UUID;
public record RefundApprovalCommand(
String tenantId,
String operatorId,
String orderId,
BigDecimal amount,
String reason,
UUID requestId) {
}
再定义一个领域事件。事件应该表达业务事实,而不是表达技术动作:
import java.math.BigDecimal;
import java.time.Instant;
import java.util.UUID;
import org.springframework.modulith.events.Externalized;
@Externalized("refund-approval-requested::#{#this.tenantId()}")
public record RefundApprovalRequested(
String tenantId,
String orderId,
BigDecimal amount,
String reason,
String requestedBy,
UUID requestId,
Instant occurredAt) {
}
这里的 @Externalized 表示这个事件可以被外部化到消息基础设施。refund-approval-requested 是逻辑目标,tenantId 可以作为路由键,方便下游按租户分区或过滤。
关键点:
- 命令可以失败,事件必须表达已经被系统接受或已经发生的事实。
requestId用于幂等,不要依赖模型“不会重复调用”。tenantId和requestedBy必须来自认证上下文,不要直接信任模型参数。- 事件里不要放完整提示词、敏感字段或超长文本。
7. Tool 层:不要直接执行高风险副作用
下面是一个故意克制的 Tool 示例。模型可以请求“申请退款”,但工具只创建受控业务命令,不直接退款:
import java.math.BigDecimal;
import java.util.UUID;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
class RefundAgentTools {
private final RefundApplicationService refunds;
private final CurrentUser currentUser;
RefundAgentTools(RefundApplicationService refunds, CurrentUser currentUser) {
this.refunds = refunds;
this.currentUser = currentUser;
}
@Tool(description = "Create a refund approval request for the current authenticated tenant. Do not use it for direct refund execution.")
RefundToolResult requestRefundApproval(
@ToolParam(description = "Business order id") String orderId,
@ToolParam(description = "Refund amount requested by the user") BigDecimal amount,
@ToolParam(description = "Short business reason") String reason) {
var command = new RefundApprovalCommand(
currentUser.tenantId(),
currentUser.userId(),
orderId,
amount,
reason,
UUID.randomUUID());
var request = refunds.requestApproval(command);
return new RefundToolResult(
request.requestId(),
"REFUND_APPROVAL_REQUESTED",
"Refund approval request has been created and is waiting for workflow confirmation.");
}
}
record RefundToolResult(UUID requestId, String status, String message) {
}
生产环境里,Tool 层应该遵守几条底线:
- 工具描述只说明适用场景,不替代权限校验。
- 高风险动作默认创建申请、草稿或待确认任务。
- 当前用户、租户、角色从安全上下文读取,不从模型参数读取。
- 每次工具调用都生成或接收幂等键。
- Tool 的返回值应该是业务状态,不应该泄露内部异常和敏感字段。
8. 应用服务:在事务里发布领域事件
业务应用服务负责真正的校验、持久化和事件发布:
import java.time.Clock;
import java.time.Instant;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
class RefundApplicationService {
private final RefundApprovalRepository approvals;
private final OrderPolicy orderPolicy;
private final ApplicationEventPublisher events;
private final Clock clock;
RefundApplicationService(
RefundApprovalRepository approvals,
OrderPolicy orderPolicy,
ApplicationEventPublisher events,
Clock clock) {
this.approvals = approvals;
this.orderPolicy = orderPolicy;
this.events = events;
this.clock = clock;
}
@Transactional
RefundApproval requestApproval(RefundApprovalCommand command) {
orderPolicy.checkRefundAllowed(command.tenantId(), command.operatorId(), command.orderId(), command.amount());
var approval = RefundApproval.requested(
command.tenantId(),
command.orderId(),
command.amount(),
command.reason(),
command.operatorId(),
command.requestId());
approvals.saveIfAbsent(approval);
events.publishEvent(new RefundApprovalRequested(
command.tenantId(),
command.orderId(),
command.amount(),
command.reason(),
command.operatorId(),
command.requestId(),
Instant.now(clock)));
return approval;
}
}
这个服务有几个重要边界:
@Transactional覆盖业务状态写入和事件发布注册。saveIfAbsent或唯一约束保证重复请求不会创建多条审批。- 事件发布发生在业务校验和状态变更之后。
- 下游监听器失败,不应该回滚这个“审批已创建”的事实。
Spring Modulith 的事件发布注册表会把监听器执行纳入可恢复记录。也就是说,不是“发了一个 Spring 事件就算完”,而是有持久化的事件发布状态可以管理。
9. 模块监听器:处理内部协作
模块内或模块间协作可以用 @ApplicationModuleListener:
import org.springframework.modulith.events.ApplicationModuleListener;
import org.springframework.stereotype.Component;
@Component
class RefundWorkflowListener {
private final WorkflowClient workflowClient;
RefundWorkflowListener(WorkflowClient workflowClient) {
this.workflowClient = workflowClient;
}
@ApplicationModuleListener
void on(RefundApprovalRequested event) {
workflowClient.createApprovalTask(
event.tenantId(),
event.requestId(),
event.orderId(),
event.amount(),
event.reason());
}
}
@ApplicationModuleListener 可以理解为 Spring Modulith 对常见异步事务监听模式的简化封装。它适合处理“主业务已经完成,后续动作可以异步执行”的场景,例如:
- 创建审批任务。
- 通知风控模块。
- 写入审计摘要。
- 推送站内信。
- 触发异步补数或缓存刷新。
但要注意,监听器仍然是业务代码。它需要幂等、超时、错误分类和可观测性,不应该因为是异步就放松治理。
10. Outbox:外部消息不能靠一次内存回调
Agent 动作经常需要通知外部系统,例如数据平台、消息中心、CRM、工单系统或 BI 流水。如果只在监听器里直接调用 Kafka、RabbitMQ 或外部 HTTP,一旦进程崩溃、网络抖动或 broker 短暂不可用,就会出现业务状态已提交但外部消息丢失的问题。
Spring Modulith 的事件外部化机制正是为这个问题准备的。官方文档说明,事件外部化会选择需要发布的事件、准备消息、确定路由目标,然后交给对应 broker 基础设施。事件发布注册表会保护外部化过程,失败后可以通过 API 重提。
基础配置示例:
spring:
modulith:
events:
externalization:
enabled: true
serialize-externalization: true
completion-mode: archive
staleness:
processing: 2m
published: 2m
resubmitted: 5m
check-interval: 30s
如果引入 2.1 文档里的 Namastack 或 JobRunr Outbox 支持,可以把外部化模式切到 outbox:
spring:
modulith:
events:
externalization:
mode: outbox
建议这样分层选择:
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 模块内异步协作 | @ApplicationModuleListener |
简单、贴近 Spring 事件模型 |
| 普通 broker 外部化 | Spring Modulith events Kafka/AMQP/JMS | 够用、接入成本低 |
| 高可靠外部消息 | Outbox 模式 | 更适合强恢复、批量重试和运维治理 |
| 高风险业务动作 | 审批流 + Outbox + 人工确认 | 模型不能直接完成最终副作用 |
不要把 Outbox 理解成“消息发送更稳一点”。它真正解决的是事务边界:业务状态和待发送消息必须先在同一侧形成可恢复记录,然后再异步投递。
11. 事件发布生命周期:Agent 场景要重点看失败和重提
Spring Modulith 2.0 之后,事件发布有更明确的生命周期状态,例如待处理、处理中、完成、失败、重提。2.1 继续沿用这套机制。对 Agent 场景来说,最重要的不是成功路径,而是以下几类异常路径:
- 模型重复触发同一个工具。
- Tool 成功写入业务状态,但监听器执行失败。
- 外部 broker 不可用。
- 应用在监听器处理中崩溃。
- 某个事件卡在处理中状态。
- 下游系统恢复后需要批量重投。
建议至少配置三类策略:
- 幂等策略:业务命令有
requestId,核心表有唯一约束。 - 失效检测:设置 staleness,让卡住的发布记录可以被标记为失败。
- 重提策略:使用
FailedEventPublications或IncompleteEventPublications做受控重提,不要无限重试。
示例方向:
import java.time.Duration;
import org.springframework.modulith.events.core.FailedEventPublications;
import org.springframework.modulith.events.core.ResubmissionOptions;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
class FailedPublicationResubmitter {
private final FailedEventPublications failed;
FailedPublicationResubmitter(FailedEventPublications failed) {
this.failed = failed;
}
@Scheduled(fixedDelayString = "PT1M")
void resubmitOldFailures() {
var options = ResubmissionOptions.defaults()
.withBatchSize(50)
.withMinAge(Duration.ofMinutes(2))
.withFilter(publication -> publication.getCompletionAttempts() < 5);
failed.resubmit(options);
}
}
这类任务要加运维开关。线上出现下游系统大面积故障时,盲目自动重提可能把故障放大。
12. 可观测性:看见模块、事件和 Agent 工具链路
spring-modulith-starter-insight 会带来两个很实用的能力:
- 通过 Actuator 暴露应用模块结构。
- 通过 Micrometer 对模块调用和事件发布建立指标与 trace。
对于 Agent 应用,建议把三类观测串起来:
AI 调用观测:model、promptHash、toolName、tokenUsage、duration
业务事件观测:eventType、module、requestId、tenantId、status、attempts
外部化观测:broker、topic、routingKey、publicationStatus、resubmissionCount
日志字段示例:
{
"requestId": "63ef5a7d-0bb9-40cf-bf0c-6bb2b3f5c938",
"tenantId": "t001",
"agentId": "support-agent",
"toolName": "requestRefundApproval",
"module": "refund",
"eventType": "RefundApprovalRequested",
"publicationStatus": "PUBLISHED",
"completionAttempts": 1,
"externalizedTarget": "refund-approval-requested",
"routingKey": "t001"
}
注意不要在日志里写完整 prompt、完整用户输入、身份证号、手机号、银行卡号、合同正文或工具原始参数。建议写 hash、业务 ID、状态和低基数字段。
13. 模块边界验证:把架构约束放进测试
Agent 应用容易在快速迭代中变成“大一统工具箱”。今天一个 Tool 调订单,明天另一个 Tool 调库存,后天为了省事直接注入审批模块内部 Bean。几轮之后,模块边界会被模型工具层悄悄打穿。
Spring Modulith 的结构验证应该放进常规测试:
import org.junit.jupiter.api.Test;
import org.springframework.modulith.core.ApplicationModules;
class ModulithStructureTests {
@Test
void verifiesModuleBoundaries() {
ApplicationModules.of(Application.class).verify();
}
}
它会检查典型问题:
- 模块之间不能形成循环依赖。
- 其他模块不能访问当前模块的内部包。
- 如果声明了允许依赖,未允许的依赖会被拒绝。
对 Agent 项目,建议新增一条团队规则:Tool 所在模块只能依赖领域模块公开 API,不能依赖内部实现类。
14. 模块测试:用 Scenario 测异步事件链路
Agent 动作的测试不能只 mock 模型返回。真正要测的是:工具调用之后,业务命令是否落库,领域事件是否发布,监听器是否完成,下游状态是否符合预期。
Spring Modulith 的 @ApplicationModuleTest 可以只启动当前模块或依赖模块,避免每个测试都全量跑一个大应用:
import org.junit.jupiter.api.Test;
import org.springframework.modulith.test.ApplicationModuleTest;
import org.springframework.modulith.test.Scenario;
@ApplicationModuleTest
class RefundModuleTests {
@Test
void createsWorkflowTaskAfterRefundApprovalRequested(Scenario scenario) {
var event = new RefundApprovalRequested(
"t001",
"ORD-1001",
new BigDecimal("99.00"),
"duplicate payment",
"u001",
UUID.randomUUID(),
Instant.now());
scenario.publish(event)
.andWaitForStateChange(() -> workflowTasks.findByRequestId(event.requestId()))
.andVerify(task -> assertThat(task.status()).isEqualTo("WAITING_APPROVAL"));
}
}
这类测试对 Agent 特别有价值,因为它不依赖真实 LLM。你可以把模型输出固定成工具调用输入,然后验证业务事件链路是否正确。
建议覆盖这些用例:
- 同一个
requestId重复调用只创建一条业务记录。 - 监听器失败后事件发布记录保留为未完成或失败。
- 下游恢复后可以重提。
- 高风险动作只创建审批,不直接执行最终副作用。
- 非授权用户即使让模型调用工具,也不能越权创建业务命令。
15. 和 Spring Cloud 的关系:本地事件不等于架构退回单体
Spring Modulith 很适合模块化单体,但它不和 Spring Cloud 冲突。更准确地说,它可以帮助团队先把服务内部边界理清,再决定哪些能力真正需要拆成微服务。
在 Agent 平台里,可以这样分工:
- Spring Cloud Gateway:统一入口、鉴权、租户、限流和审计。
- Agent Orchestrator:承载 Spring AI
ChatClient、工具注册和工具选择。 - 业务 Modulith 应用:承载领域命令、领域事件、Outbox 和模块测试。
- Spring Cloud Stream / Kafka:接收外部化后的业务事件。
- Config / Vault:管理模型开关、工具开关、外部化目标、密钥和降级策略。
- CircuitBreaker:保护外部 SaaS、MCP Server、broker 和工作流系统。
不要因为用了 Agent 就绕开微服务体系,也不要因为用了 Spring Cloud 就把所有内部协作都拆成远程调用。一个更稳的原则是:
进程内强一致:领域命令 + 本地事务 + 应用事件
跨系统最终一致:事件发布注册表 + Outbox + 消息中间件
平台级治理:Gateway + Config + Vault + Observability
16. 迁移建议:从高价值低风险动作开始
如果团队已经有 Spring AI Tool Calling,可以按这个顺序改造:
- 盘点工具:把工具分成只读、低风险写入、高风险写入、外部网络访问。
- 给写入工具补命令对象:不要让 Tool 直接改状态。
- 给命令补幂等键:至少使用
requestId或业务自然键。 - 发布领域事件:把后续动作从主事务里拆出来。
- 启用事件发布注册表:使用 JDBC/JPA/MongoDB 等持久化方案。
- 外部消息走事件外部化:Kafka、AMQP、JMS 或 Outbox。
- 补模块边界测试:防止工具层打穿模块内部包。
- 补模块事件测试:用
Scenario覆盖异步链路。 - 补观测字段:把 toolName、eventType、requestId、publicationStatus 串起来。
- 配置降级开关:能按工具、事件类型、外部化目标关闭。
不建议第一步就改所有 Tool。优先选择“高价值、低风险、需要审计”的动作,例如:
- 创建审批草稿。
- 创建工单。
- 发起库存校验任务。
- 创建客户跟进计划。
- 生成待确认报价。
- 创建知识库更新申请。
这些动作即使模型误判,也不会直接造成不可逆副作用,但能验证 Agent 到业务事件的整条链路。
17. 常见误区
误区一:Tool 调用成功就等于业务成功。
Tool 成功只说明模型到应用的入口成功。业务是否成功,要看命令是否提交、事件是否发布、监听器是否完成、外部化是否成功。
误区二:事件发布就是消息发送。
领域事件首先是进程内业务事实。需要跨系统传播时,再通过事件外部化或 Outbox 发送到 broker。
误区三:Outbox 可以替代幂等。
Outbox 提高投递可靠性,但下游仍然可能收到重复消息。业务 ID、事件 ID 和消费幂等仍然必须做。
误区四:模块化单体不需要观测。
Agent 动作的复杂度不一定来自服务数量,而是来自模型、工具、事件、监听器和外部系统的组合。进程内也需要 trace 和指标。
误区五:让模型决定事件类型。
模型可以选择工具,但事件类型应该由应用代码决定。不要把模型生成的字符串直接当业务事件或消息路由。
18. 今天可以直接落地的实践建议
如果你正在做第一个 Java Agent:
- 先只开放只读工具和“创建草稿/申请”的低风险工具。
- 每个写入 Tool 都必须调用应用服务,不直接操作 Repository。
- 每个写入动作都生成
requestId。 - 每个业务动作都发布领域事件。
如果你已经有 Spring AI 应用:
- 找出所有有副作用的 Tool,检查是否绕过了业务服务。
- 把高风险 Tool 改成“生成待确认任务”。
- 给工具调用链路补
toolName + requestId + tenantId + eventType。 - 把外部消息发送从 Tool 或普通 listener 里移到事件外部化。
如果你在做企业级 Agent 平台:
- 把工具注册、权限、审计、事件类型和 Outbox 目标都纳入平台配置。
- 用 Spring Modulith 测试业务模块边界,用 Spring Cloud 管跨服务治理。
- 建立失败事件看板,按事件类型、租户、监听器和失败原因聚合。
- 给重提任务增加批量大小、最小失败时间、最大尝试次数和人工暂停开关。
19. 参考资料
- Spring Modulith Reference:
https://docs.spring.io/spring-modulith/reference/ - Spring Modulith - Working with Application Events:
https://docs.spring.io/spring-modulith/reference/events.html - Spring Modulith - Production-ready Features:
https://docs.spring.io/spring-modulith/reference/production-ready.html - Spring Modulith - Verifying Application Module Structure:
https://docs.spring.io/spring-modulith/reference/verification.html - Spring Modulith - Integration Testing Application Modules:
https://docs.spring.io/spring-modulith/reference/testing.html - Spring Boot Reference:
https://docs.spring.io/spring-boot/index.html - Spring AI Reference:
https://docs.spring.io/spring-ai/reference/
更多推荐




所有评论(0)