升级指南
每一节对应一次小版本升级,只列需要使用者动手或者需要知道的变化;完整的变更列表见仓库的 CHANGELOG。0.x 阶段小版本可以有破坏性变更,每一条都写在 CHANGELOG 的 Breaking 下。
0.9 → 0.10
0.10.0 于 2026-10-22 发布,发布说明。这一版主要是为 1.0 冻结做的整理:Maven 坐标换成 DDK 自己的命名空间,自动配置统一命名,依赖收紧。没有表结构和配置项的变化。
换版本和坐标
先把 0.10 装进本地 Maven 仓库:
DDK_REF=v0.10.0 ./scripts/ddk.sh install # 默认就是最新的正式版groupId 从 com.ddk 改成了 com.domaindrivenkit,artifactId 和 Java 包名(com.ddk.*)都不变,所以只改构建文件,不改代码。用 archetype 生成的项目和自己引入 BOM 的项目都一样:
# 在项目根目录,把所有 pom.xml 里 DDK 的 groupId 换掉,再改版本号
grep -rl --include=pom.xml '<groupId>com.ddk</groupId>' . | xargs sed -i.bak 's|<groupId>com.ddk</groupId>|<groupId>com.domaindrivenkit</groupId>|'
find . -name 'pom.xml.bak' -delete然后把 <ddk.version>(或 BOM 的版本)改成 0.10.0,执行 mvn clean verify。用 archetype 生成新项目时,参数改成 -DarchetypeGroupId=com.domaindrivenkit。ddk.sh 会按所装版本自动选用 groupId,DDK_REF=v0.9.0 这样的旧版本照常可用。
先看这张表
| 你用了什么 | 要做什么 | 不做会怎样 |
|---|---|---|
| 任何 DDK 依赖 | groupId 改为 com.domaindrivenkit | 依赖解析失败 |
spring.autoconfigure.exclude 或测试里点名了 MybatisPlusAutoConfiguration、WebAutoConfiguration、WebOpenApiAutoConfiguration | 改为 DdkMybatisAutoConfiguration、DdkWebAutoConfiguration、DdkWebOpenApiAutoConfiguration,包名不变 | 编译失败;写在配置里的排除不再生效 |
直接调用了 cache、event、MyBatis、Redis、Web 自动配置里的 @Bean 方法 | 改为注入对应的 Bean,或自己创建对象 | 编译失败:这些方法改为包级可见 |
只靠 ddk-core 传递得到 Hutool,自己的代码又直接用了它 | 自己声明 cn.hutool:hutool-core | 编译失败。用了 ddk-mybatis 或 ddk-mybatis-starter 的不受影响 |
其他变化
- 消费方因契约版本不符而跳过事件时,同一个消费方、同一个版本只在第一次打 WARN,之后记 DEBUG。并行发布 v1、v2 期间不再每条消息一条警告。
- Release 上的每个 jar 和 BOM 都附有构建来源证明,可以用
gh attestation verify <文件> --repo poppycoderr/domain-driven-kit校验。
检查清单
- 装好 0.10,替换 groupId、改版本号,然后
mvn clean verify。 - 搜一下配置和测试里有没有旧的三个自动配置类名。
- 编译不过的地方,按上表处理
@Bean方法和 Hutool。
0.8 → 0.9
0.9.0 于 2026-10-21 发布,发布说明。
换版本
DDK 还没有发布到 Maven 中央仓库,先把 0.9 装进本地 Maven 仓库,再改项目里的版本:
DDK_REF=v0.9.0 ./scripts/ddk.sh install # 或者用安装时下载的 ddk.sh;默认就是最新的正式版用 archetype 生成的项目改 pom.xml 里的 <ddk.version>;自己引入 BOM 的改 ddk-dependencies 的版本。之后用 mvn clean verify:不带 clean 时,target/classes 里可能残留已经删掉或改名的 Flyway 脚本。
先看这张表
| 你用了什么 | 要做什么 | 不做会怎样 |
|---|---|---|
ddk-projection-starter | 停掉 0.8 实例后升级;自己管表结构的,执行下面的迁移语句 | initialize-schema=false 时启动失败并打印语句 |
@Idempotent / @RateLimit 没写 scope | 接受一个 TTL 的过渡窗口,或者显式写 scope 保持 key 不变 | 发布期间旧版本登记的请求不被识别为重复,限流窗口重新计数 |
去重的集成事件消费方(idempotent() 为 true) | 检查同一个消费组里有没有同名的类;被 Spring 代理的消费方考虑固定 consumerId() | 同名时启动失败;代理类的旧记录对不上,重投的旧事件会再处理一次 |
四层项目的 ArchitectureTest | 应用服务不再直接引用基础设施类,改依赖领域端口 | 架构测试失败 |
领域层用了 Jackson 3(tools.jackson..) | 移出领域层 | 架构测试失败 |
scripts/ddk.sh | 想要开发中的快照时设置 DDK_REF=main | 默认装最新的正式版 |
其余变化(事件契约、租户、运维指标、定时维护)都是新增的可选能力,不升级配置也不受影响。
投影:待刷新表重建
0.9 修复了两个实例同时刷新同一条数据时读模型可能停在旧状态的问题,并让标记带上租户。待刷新表因此变了:新增 tenant_id、generation、claim_token、claimed_until 四列,主键变为 (projection, tenant_id, entity_id),另加一个到期索引。
initialize-schema=true(默认):启动时自动复制重建,已有的标记保留,算作没有租户。不需要做什么,但要先停掉 0.8 的实例:复制期间旧实例写入的标记会丢,旧实例按旧协议工作也可能留下旧数据。做不到停机的,升级完成后对每个读模型调用一次 Projections.rebuild。
initialize-schema=false(Flyway、Liquibase 管表结构):0.9 启动时发现旧表会失败,异常里给出语句。把它们放进迁移脚本(表名换成你配置的 ddk.projection.table):
CREATE TABLE ddk_projection_pending_upgrade AS SELECT * FROM ddk_projection_pending;
DROP TABLE ddk_projection_pending;
CREATE TABLE ddk_projection_pending (
projection VARCHAR(100) NOT NULL,
tenant_id VARCHAR(64) DEFAULT '' NOT NULL,
entity_id VARCHAR(200) NOT NULL,
version BIGINT NOT NULL,
generation VARCHAR(36),
claim_token VARCHAR(36),
claimed_until TIMESTAMP,
attempts INT NOT NULL,
available_at TIMESTAMP NOT NULL,
last_error VARCHAR(500),
PRIMARY KEY (projection, tenant_id, entity_id)
);
CREATE INDEX ddk_projection_pending_due ON ddk_projection_pending (available_at);
INSERT INTO ddk_projection_pending (projection, tenant_id, entity_id, version, generation, attempts, available_at, last_error)
SELECT projection, '', entity_id, version, CONCAT('upgraded-', version), attempts, available_at, last_error
FROM ddk_projection_pending_upgrade;
DROP TABLE ddk_projection_pending_upgrade;复制重建中途停下(MySQL 的 DDL 不能回滚)会留下 ddk_projection_pending_upgrade。0.9 下次启动时会接着做完;用 Flyway 管表的,启动失败并打印收尾的语句,放进下一个迁移即可,不要手工删掉这张表,里面是还没处理的标记。
另外两处行为变化:
- 配置不合法时启动失败:
batch-size小于 1、任一时长不为正、retry-delay大于max-retry-delay。 - 新配置项
ddk.projection.lease-time(默认2m),要长于一次刷新的正常耗时,见 Projection Starter 的「多个实例」。
Projections 从类变成了接口。注入它的代码不用改;它原来的公开构造器要传入 internal 类型,没有应用能合法调用,所以也不会有代码因此编译失败。
并发:防重复提交和限流的默认 scope
没写 scope 时,默认值从 OrderService.place 变成 com.acme.OrderService#place(PlaceOrderCommand):不同包里的同名类、重载方法不再共用登记和配额。Redis key 随之改变,所以发布期间有一个 TTL 的过渡窗口:旧版本登记的请求不会被新版本认出是重复的,限流窗口也从零开始。
- 能接受这个窗口的,不用做什么。
- 不能接受的,升级前在注解上写出原来的 scope,例如
@Idempotent(key = "#command.requestId()", scope = "OrderService.place"),等稳定后再去掉。
另外,失败调用现在只撤销自己的登记,撤销失败也不会掩盖方法本身的异常,这两点不需要改代码。
事件:去重消费方的身份
去重表按「消费组:消费方标识」记录处理过的事件。0.9 新增 IntegrationEventConsumer.consumerId(),默认取实现类的简单类名(用户类,不是 Spring 代理类)。
- 同一个消费组里两个去重消费方身份相同时,启动失败:同名的两个类、同一个类的两个 Bean、匿名类。给其中一个覆盖
consumerId()。0.8 里这种情况下第二个消费方会悄悄跳过第一个处理过的所有事件。 - 被 Spring 代理的去重消费方(例如
handle上有@Transactional),0.8 记录的是代理类名(Name$$SpringCGLIB$$0),0.9 记录的是Name。升级后重投的旧事件会再处理一次。处理逻辑可以重复执行的,不用管;不能的,升级期间把consumerId()固定为旧名字。 - 没有代理、没有重名的消费方,身份不变,旧记录照样有效。
自己写监听器调用 IntegrationEventDispatcher.dispatch 的:返回值现在不包括因契约版本不符而跳过的消费方。
架构守卫:分层规则收紧
LAYERED_ARCHITECTURE_RULE(四层)不再允许应用服务直接引用基础设施类,和三层规则、四层文档的约定一致。应用服务改为依赖领域里的端口;必须点名实现类的装配,放到层外(例如config包)的@Configuration类里,两条分层规则都豁免它。DOMAIN_MUST_NOT_DEPEND_ON_FRAMEWORKS覆盖了 Jackson 3(tools.jackson..),也就是 Spring Boot 4 默认用的 Jackson。领域类里用到的 Jackson 类型(例如字段是JsonNode、ObjectNode)要换成领域自己的值对象或Map这类 JDK 类型,JSON 的读写放到适配层的 DTO 或基础设施的转换器里。(Jackson 的注解包com.fasterxml.jackson.annotation0.8 起就在禁止列表里。)- 新增
CommonArchRules.domainMustNotDependOn(String...),可以把自己的包(例如内部 SDK)加进领域纯度检查。
用 0.8 模板生成的项目,四层模板里 ddk-add-domain-event 技能原来对所有订阅都建议 AFTER_COMMIT;0.9 的模板改为按用途选择。已生成的项目可以对照 四层架构 检查自己的事件订阅。
安装脚本
scripts/ddk.sh 默认安装最新的正式版(vX.Y.Z tag),不再是 main。想要开发中的快照,设置 DDK_REF=main。只有 ref、远端提交、本地 Maven 产物都没变时才复用已有的安装,换版本后会重新构建。
升级之后可以考虑开启的
| 能力 | 怎么开 | 文档 |
|---|---|---|
| 消费方声明认识的事件类型和版本 | 覆盖 contract() | Event:消费方契约 |
| 事件携带租户、消费方在事件租户里处理 | @IntegrationEvent(tenant = ...) + runInEventTenant() | Event:多租户 |
| 发布记录重投、清理,去重表清理 | ddk.event.maintenance.enabled=true | Event:运维 |
| 投影的失败定位、立即重试、积压指标 | Projections.failures / retryFailed,引入 Micrometer | Projection:运维 |
| 定时任务、MCP 工具的租户写法 | 按文档调整 | Job:多租户、MCP:操作者与租户 |
检查清单
- 装好 0.9、改好版本后先跑一遍
mvn clean verify:分层规则和 Jackson 3 的问题会在架构测试里出现,消费方身份冲突会在启动时出现。 - 投影:停掉旧实例再发布;
initialize-schema=false的,先执行迁移脚本。 - 并发:确认能不能接受一个 TTL 的过渡窗口,不能就先显式写
scope。 - 事件:列出被代理的去重消费方,决定要不要固定
consumerId()。 - 发布后看一眼:
ddk_projection_pending里没有长期不动的行,日志里没有Skipped event警告(声明了契约时)。