框架参考

Ineed-Core 集成事件接入指南

查看内部事件、蒸馏事件、开关和监听器的具体接入契约。

浏览全部手册
本页目录
文档来源与 Markdown 原文

权威正文:ineed-core/docs/core/integration-event-guide.md。网站按工作区快照同步,原文中的历史日期和验证范围保留。

开始编码前核对同版本源码;跨仓文件引用可在源码定位目录查找。

下载 Markdown 原文 ↓

文档定位

本文档说明 ineed-integration 当前已经落地的接入方式、配置规则、事件模型和业务模块的推荐用法。

当前范围覆盖标准 Manager 主链路中的 CRUD 和数据状态事件:

  • create
  • update
  • logicDelete
  • physicsDelete
  • dataStatusChange

已接入的执行线包括:

  • 阻塞式 ineed-core-data-core
  • 响应式 ineed-core-data-core-reactive

暂不覆盖:

  • recovery
  • WebHook adapter
  • Spring Cloud adapter
  • outbox / 可靠投递

1. 当前模块关系

  • ineed-integration 负责事件模型、开关、默认 key 解析、Spring 事件发布与蒸馏监听。
  • ineed-core-data-core 已在标准同步 Manager 默认实现里接入事件发送。
  • ineed-core-data-core-reactive 已在标准响应式 Manager 默认实现里接入同一套事件发送。
  • 业务模块 当前不需要改 Service 主流程,只需要按需配置开关、监听事件,或者覆盖默认 key 解析。

2. 当前接入前提

2.1 标准启动方式

如果应用主类使用:

@SpringBootApplication(scanBasePackages = "com.ineed")

当前可以直接扫描到:

  • IntegrationConfiguration
  • BusinessEventPublisher
  • DistilledBusinessEventListener

这是仓库里当前 demo / test project 的默认接法。

2.2 非标准扫描方式

如果应用没有扫描 com.ineed 根包,需要显式接入:

import org.springframework.context.annotation.Import;

import com.ineed.integration.configuration.IntegrationConfiguration;

@Import(IntegrationConfiguration.class)
public class YourApplication {
}

或者把 com.ineed.integration 加入你的扫描路径。

3. 当前配置方式

3.1 YAML 示例

下面示例是“开启发送”的示例。

真实默认值仍然是:

  • ineed.integration.enabled = false
  • ineed.integration.internal.enabled = false
  • ineed.integration.distilled.enabled = false
ineed:
  integration:
    enabled: true
    internal:
      enabled: true
      keys:
        security.member-identity: true
    distilled:
      enabled: true
      keys:
        security.member-identity: true

3.2 Properties 示例

ineed.integration.enabled=true
ineed.integration.internal.enabled=true
ineed.integration.internal.keys[security.member-identity]=true
ineed.integration.distilled.enabled=true
ineed.integration.distilled.keys[security.member-identity]=true

3.3 判断规则

事件是否会进入发布链,当前按下面顺序判断:

  1. ineed.integration.enabled 必须为 true
  2. 对应通道 enabled 必须为 true
  3. 对应 keys[eventKey] 必须为 true

未配置的 key 默认视为关闭。

这里的 eventKey 是兼容命名。Manifest 配置了 resourceKey 时,发布链使用 resourceKey 作为 key;旧 Manifest 只配置 eventKey 时仍按旧 key 兼容。

3.4 一个容易忽略的点

当前 shouldPublishInternal(eventKey) 的规则是:

  • 只要内部通道开启了该 key,就发布内部事件
  • 只要蒸馏通道开启了该 key,也会先发布内部事件

这是当前的有意设计。

原因是蒸馏事件本身依赖 InternalBusinessEvent,并且蒸馏监听器要在事务提交后再处理。

4. 默认资源键规则

当前默认解析器是 DefaultBusinessEventDefinitionResolver

规则如下:

  • sourceModule 取实体包名中 com.ineed 后面的第一个段
  • aggregateType 取实体简单类名并转换为 snake_case
  • resourceKey 注册后使用 Manifest 中的业务资源键;未注册时回退为 类名.操作后缀
  • operationKeyBusinessOperationType 的资源操作键,如 createupdate
  • eventKey 兼容字段;有 resourceKey 时等同 resourceKey,旧 Manifest 仅配置 eventKey 时继续使用旧值

例如:

  • 实体类:com.ineed.security.authorization.memberidentity.jdbc.entity.MemberIdentity
  • 操作:UPDATE
  • 默认 resourceKey/eventKey:com.ineed.security.authorization.memberidentity.jdbc.entity.MemberIdentity.updated
  • 默认 operationKey:update

如果实体类名本身带后缀,当前也会保留下来。

例如:

  • 实体类:MemberIdentityEntity
  • 默认聚合名:member_identity_entity

如果这个默认规则不符合业务语义,可以覆盖 BusinessEventDefinitionResolver

5. 当前事件模型

5.1 顶层结构

内部事件和蒸馏事件共用同一份 BusinessEventMessage<T>,核心字段包括:

  • eventId
  • eventKey(兼容字段,推荐与 resourceKey 保持一致)
  • resourceKey
  • operationKey
  • aggregateType
  • operationType
  • entityId
  • tenantKey
  • sourceModule
  • sourceType
  • occurredAt
  • payload

5.2 当前 CRUD payload

当前 payload 类型是 CrudBusinessEventPayload<T>,包含:

  • entityId
  • before
  • after
  • deleteMode

5.3 当前各操作的实际负载

CREATE

  • before = null
  • after = 创建后的 VO
  • deleteMode = null

UPDATE

  • before = 修改前 VO
  • after = 修改后 VO
  • deleteMode = null

LOGIC_DELETE

  • before = null
  • after = null
  • deleteMode = LOGIC

PHYSICS_DELETE

  • before = null
  • after = null
  • deleteMode = PHYSICS

删除事件现在只保证:

  • entityId
  • tenantKey
  • operationType
  • deleteMode

如果后面要补删除前快照,再单独增强。

5.4 DataStatus payload

数据状态变化使用 DataStatusBusinessEventPayload,字段固定为:

  • id
  • action:追加状态位为 append,移除状态位为 subtract
  • statusFeatureDataStatusFeature 枚举名,例如 ENABLED
  • beforeStatus:变化前的完整 byte 状态;实体原值为 null 时按 0 处理
  • afterStatus:变化后的完整 byte 状态

Manager 先读取并校验全部目标,再比较完整状态。只有状态位实际变化的目标才会进入唯一状态处理、持久化和事件发布;空编号集合、全部幂等的命令以及未注册或关闭的事件 key 都不会发送事件。批量操作对每个实际变化的目标发送一条事件。

6. 事件时序

阻塞式主链路当前时序如下:

  1. Manager 主流程完成数据变更
  2. 立即发布 InternalBusinessEvent
  3. DistilledBusinessEventListener@TransactionalEventListener(phase = AFTER_COMMIT) 监听
  4. 主事务提交成功后,才发布 DistilledBusinessEvent

这意味着:

  • 内部事件是事务内的 Spring 事件
  • 蒸馏事件是提交后的 JVM 内事件
  • 当前不保证跨进程可靠送达

响应式主链路使用同一套 IntegrationEventSupport 发布事件:

  • 事件在 repository 保存 / 删除链路完成、afterXxx 钩子完成之后发布
  • 数据状态事件在对应 append / subtract 持久化完成之后发布
  • 发布动作发生在当前 reactive subscription 内,不要求业务 Service 手工补发
  • 未订阅时不会查询目标、修改状态或发布事件
  • 如果业务链路接入 reactive transaction,DistilledBusinessEvent 只在事务提交后发送,回滚时不发送
  • 当前不承诺跨进程可靠投递,也不承诺并发写入下的可串行化行为

7. 业务模块怎么监听

7.1 模块内协作监听内部事件

如果你要做同 JVM 的模块内协作,并且需要拿到内部标准消息,可以监听 InternalBusinessEvent<?>

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import com.ineed.integration.event.BusinessEventMessage;
import com.ineed.integration.event.InternalBusinessEvent;

@Component
public class MemberInternalEventListener {

  @EventListener
  public void onInternalEvent(final InternalBusinessEvent<?> event) {
    final BusinessEventMessage<?> message = event.getMessage();
    if (!"security.member-identity".equals(message.getResourceKey()) || !"create".equals(message.getOperationKey())) {
      return;
    }
    // 模块内协作逻辑
  }
}

7.2 跨模块或外发前置监听蒸馏事件

如果你要做 WebHook、对外同步、跨模块桥接,优先监听 DistilledBusinessEvent<?>

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import com.ineed.integration.event.BusinessEventMessage;
import com.ineed.integration.event.DistilledBusinessEvent;

@Component
public class MemberPrivilegeBridgeListener {

  @EventListener
  public void onDistilledEvent(final DistilledBusinessEvent<?> event) {
    final BusinessEventMessage<?> message = event.getMessage();
    if (!"security.member-identity".equals(message.getResourceKey()) || !"create".equals(message.getOperationKey())) {
      return;
    }
    // 这里再去调用权益中心 adapter / webhook / stream bridge
  }
}

推荐原则:

  • 模块内同步协作:优先内部事件
  • 跨模块、外发、集成边界:优先蒸馏事件

8. 业务模块怎么覆盖默认 key

如果默认 key 不够稳定,或者你不想把实体命名暴露给事件 key,可以自己提供 BusinessEventDefinitionResolver Bean:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.ineed.integration.event.BusinessEventDefinitionResolver;
import com.ineed.integration.event.BusinessOperationType;
import com.ineed.integration.event.DefaultBusinessEventDefinitionResolver;
import com.ineed.integration.event.ResolvedBusinessEventDefinition;
import com.ineed.security.authorization.memberidentity.jdbc.entity.MemberIdentity;

@Configuration
public class MemberEventDefinitionConfiguration {

  @Bean
  public BusinessEventDefinitionResolver memberBusinessEventDefinitionResolver() {
    final BusinessEventDefinitionResolver delegate = new DefaultBusinessEventDefinitionResolver();
    return (entityType, operationType) -> {
      if (MemberIdentity.class.equals(entityType)) {
        return memberIdentityDefinition(operationType);
      }
      return delegate.resolve(entityType, operationType);
    };
  }

  private static ResolvedBusinessEventDefinition memberIdentityDefinition(final BusinessOperationType operationType) {
    final ResolvedBusinessEventDefinition definition = new ResolvedBusinessEventDefinition();
    definition.setSourceModule("security");
    definition.setAggregateType("member_identity");
    definition.setOperationType(operationType);
    definition.setResourceKey("security.member-identity");
    definition.setOperationKey(operationType.getOperationKey());
    definition.setEventKey(definition.getResourceKey());
    return definition;
  }

}

更推荐的实际做法是:

  • 业务模块内部自己封装一层 resolver
  • 未命中的实体继续委托给默认 resolver

这样不会把所有规则都重写掉。

9. 当前已经接入的发送点

当前标准链路的发送点在 Manager 主流程中,不在 Service 中。

阻塞式发送点:

  • CreateManager.save(...)
  • UpdateManager.update(...)
  • LogicDeleteManager.logicDelete(...)
  • PhysicsDeleteManager.physicsDelete(...)

响应式发送点:

  • com.ineed.core.data.reactive.manager.crud.CreateManager.save(...)
  • com.ineed.core.data.reactive.manager.crud.UpdateManager.update(...)
  • com.ineed.core.data.reactive.manager.crud.LogicDeleteManager.logicDelete(...)
  • com.ineed.core.data.reactive.manager.crud.PhysicsDeleteManager.physicsDelete(...)

并且发布动作都放在:

  • 业务 afterXxx 钩子执行完成之后
  • 返回结果之前

这样可以避免业务覆写 afterXxx 时绕过底座事件。

10. 当前不建议的用法

  • 不要在业务 Service 里手工重复发同一类标准 CRUD 事件
  • 不要把开关分散到业务模块各自配置里
  • 不要直接依赖删除事件里存在完整快照,当前没有这个保证
  • 不要把 DistilledBusinessEvent 当成可靠消息中间件

11. 下一阶段候选

后续如果继续推进,下一批更自然的工作是:

  • DataStatus 事件
  • recovery 事件
  • 删除前快照增强
  • WebHook adapter
  • Spring Cloud adapter
  • outbox / 可靠投递

最后更新:2026-05-15

仍有疑问?按反馈清单整理复现信息 →