Repository navigation
Guide Add Feature Module
本页回答:要在
coco-features/下新增一个可独立启停的 Feature(例如假想的example),需要创建哪些文件、登记到哪些位置、补哪些测试、怎么验证。 本清单由已有模块coco-captcha、coco-lock在仓库中出现的位置反推而来,并对照了校验脚本和测试的实际行为。清单未必覆盖所有登记点;动手前请先用rg或 CodeGraph 搜索一个现有同类模块(如coco-captcha)的全部出现位置,逐项对照。 文中example/coco-example/Example是占位符,请替换为真实名称。
- 先确认这项能力属于框架。AGENTS.md 规定:框架是独立的 Web 服务器基础,业务领域、认证选择、组织权限模型属于下游;CRUD 与业务脚手架通过代码生成产出,不做运行时魔法。
- 新增 Feature 属于 “feature contract” 变更:GOVERNANCE.md 的决策流程要求这类变更在实现前先有 Issue 或设计讨论(CONTRIBUTING.md 同样要求破坏性 API、Maven 坐标、feature 契约变更先开 Issue)。
- 决定命名:Feature id 用 kebab-case(如
example);模块 artifactId 取coco-<id>(现有 16 个 Feature 全部如此,verify_boot_archive_feature_coordinates.py也按coco-<id>-前缀匹配归档中的 jar);配置前缀coco.<id>;Java 包见下一条。 - 包名在
io.github.coco.feature.<名称>与io.github.coco.<名称>之间二选一,两种风格并存:现有 16 个 Feature 模块中,10 个使用前者(audit、data-permission、idempotency、lock、mybatis-plus、openapi、rate-limit、security、tenant、web),6 个使用后者(cache、captcha、messaging、notification、scheduling、storage)。设计意图未见文档说明,行为以各模块src/main/java下的实际包路径为准(另见 约定与规范)。实操建议:与最近的同类模块保持一致,并在 PR 描述里说明所选风格。
| # | 位置 | 动作 | 必须? | 依据 |
|---|---|---|---|---|
| 1 | coco-features/coco-example/ |
新建模块(pom、代码、资源、测试) | 是 | 参照 coco-features/coco-captcha/pom.xml
|
| 2 | coco-features/pom.xml |
在 <modules> 追加 <module>coco-example</module>
|
是 | 现有 16 个模块均列于此 |
| 3 | coco-build/coco-dependencies/pom.xml |
在 dependencyManagement 登记,版本 ${project.version}
|
是 | 业务侧使用的 BOM 已登记全部 Feature 模块 |
| 4 | 根 pom.xml 的 dependencyManagement
|
是否也要登记 | 做法不统一 | 根 pom 目前缺少 rate-limit、scheduling、lock、storage、messaging、cache、notification、captcha 共 8 个模块,它们只登记在 BOM;设计意图未见文档说明,行为以 coco-build/coco-dependencies/pom.xml 为准,BOM 必须登记 |
| 5 | coco-foundation/coco-api/.../feature/CocoFeature.java |
增加枚举常量 EXAMPLE("example")
|
是 | Feature id 的稳定来源 |
| 6 | coco-foundation/coco-feature-model/.../StandardCocoFeatures.java |
在 FEATURES 追加 feature(...)
|
是 | Feature 计划、manifest、裁剪的来源 |
| 7 | coco-foundation/coco-feature-model/src/test/.../StandardCocoFeaturesTest.java |
补断言 | 是 |
registersAllStandardFeatures 要求枚举与登记一一对应,否则测试失败 |
| 8 | coco-spring/coco-spring-boot-starter/pom.xml |
是否让 starter 直接依赖 | 视情况 | 见 §4 |
| 9 | starter 契约测试 | 若不加入 starter,补一个契约测试 | 视情况 |
CocoRateLimitStarterDependencyContractTest 的做法 |
| 10 | .github/release/release-artifact-expectations.json |
在 modules[] 追加条目 |
是(见 §7 的出入说明) |
release_file_count_preflight.py 会比对反应堆与该清单 |
| 11 |
website/docs/features/example.md、website/sidebars.ts、英文版、feature-toggles.md
|
用户文档 | 建议 | 官网侧边栏是显式列表,不会自动收录 |
| 12 | coco-support/coco-tools/coco-agent-skills |
重建文档索引 | 视情况 | 索引由 website/docs 生成 |
coco-features/coco-example/
pom.xml
src/main/java/io/github/coco/example/ # 或 io/github/coco/feature/example/,见 §0
src/main/resources/
coco-example-messages.properties
coco-example-messages_zh_CN.properties
coco-example-messages_en_US.properties
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
src/test/java/io/github/coco/example/
pom 骨架(依据 coco-features/coco-captcha/pom.xml;<description> 在现有 pom 中是英文一句话):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.github.patton174</groupId>
<artifactId>coco-framework</artifactId>
<version>${revision}</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>coco-example</artifactId>
<name>Coco Example</name>
<description>One-sentence description of the capability.</description>
<dependencies>
<dependency><groupId>${project.groupId}</groupId><artifactId>coco-exception</artifactId></dependency>
<dependency><groupId>${project.groupId}</groupId><artifactId>coco-i18n</artifactId></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency>
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></dependency>
<!-- 重依赖设为 optional,运行期用 @ConditionalOnClass 保护 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><optional>true</optional></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><scope>test</scope></dependency>
</dependencies>
</project>依赖取舍:
- 不需要 Redis 就去掉 Redis 依赖;
coco-captcha、coco-lock的 Redis 为optional。 - 若要使用
@ConditionalOnCocoFeature,模块必须能看到注解所在的coco-spring-boot-autoconfigure,需要增加该依赖(coco-web、coco-rate-limit等就是这样做的,见 架构总览)。没有使用该注解的 7 个模块(scheduling、lock、storage、messaging、cache、notification、captcha)的 pom 也都没有依赖coco-spring-boot-autoconfigure,两者吻合。这些模块是否有意不使用该注解,设计意图未见文档说明,行为以各模块的自动配置类与pom.xml为准。是否应该使用@ConditionalOnCocoFeature的取舍见 Feature 机制。 - 依赖版本不要在子模块里重复声明:
banDuplicatePomDependencyVersions与dependencyConvergence会在validate阶段把关。
最小集合,细节见 指南:新增或调整自动配置:
-
Coco<Example>Properties:@ConfigurationProperties("coco.example"),至少一个enabled; -
Coco<Example>AutoConfiguration:@AutoConfiguration,注册消息包 Bean、默认实现 Bean(@ConditionalOnMissingBean); - 可替换点(SPI):接口 +
Default*实现,只有存在真实替换点才加; - 错误码(如有):
Coco<Example>ErrorCode,参照CocoLockErrorCode; - JavaDoc 使用项目模板(作者、仓库、
模块:{@code coco-example}、@since),中文描述。
- 消息包
coco-example-messages.properties(及_zh_CN、_en_US);Bean 里registry.add("coco-example-messages")要与文件基名一致。 -
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:每行一个自动配置 FQCN(Redis 三件套时按Redis、RedisMissingDependency、主配置列出,参照coco-captcha的同名文件)。 - 需要在 IDE 里提示属性时,可参照 audit / tenant / web 等模块提供
META-INF/additional-spring-configuration-metadata.json;其余模块靠spring-boot-configuration-processor生成。
在 CocoFeature 枚举末尾追加常量,id 一旦发布就不能改:
EXAMPLE("example");在 FEATURES 列表追加一项:
feature(CocoFeature.EXAMPLE, "coco-example",
"io.github.coco.example.CocoExampleAutoConfiguration"),- 有 Feature 依赖时作为可变参数追加,例如
CocoFeature.WEB(rate-limit、idempotency就是feature(..., CocoFeature.WEB)); - 需要额外裁剪的 artifact(例如同时裁剪第三方库或旧坐标)时,使用带
Set<String> pruneArtifactIds的重载;否则裁剪集合默认就是Set.of(artifactId); - 新 Feature 的
defaultEnabled由辅助方法写死为true;这意味着继承coco-parent的应用在构建期会通过coco:features得到该模块(除非被禁用),详见 §4。
按已有写法补断言(id、artifactId、自动配置 FQCN、defaultEnabled、依赖),例如:
assertEquals("example", CocoFeature.EXAMPLE.id());
assertEquals("coco-example", definitions.get(CocoFeature.EXAMPLE).artifactId());
assertEquals("io.github.coco.example.CocoExampleAutoConfiguration",
definitions.get(CocoFeature.EXAMPLE).autoConfigurationClassName());
assertTrue(definitions.get(CocoFeature.EXAMPLE).defaultEnabled());
assertEquals(Set.of(), definitions.get(CocoFeature.EXAMPLE).dependencies());只要枚举与 FEATURES 不一致,registersAllStandardFeatures 就会失败,可以用它兜底。
AGENTS.md 要求 starter 只组合能力、不承载行为,但没有写“哪些模块应该进 starter”。现状可以作为参照:
| 做法 | 现有例子 | 说明 |
|---|---|---|
加入 coco-spring-boot-starter
|
14 个 Feature(web、mybatis-plus、audit、security、tenant、data-permission、openapi、scheduling、lock、storage、messaging、cache、notification、captcha) | 在 starter pom 追加依赖;若根 pom 的 dependencyManagement 没有登记该模块,需要显式写 <version>${project.version}</version>(scheduling … captcha 就是这样) |
不加入 starter,由 coco:features 在构建期装配 |
coco-rate-limit、coco-idempotency(二者的 Feature 依赖 web) |
CocoRateLimitStarterDependencyContractTest 断言 starter 直接依赖中不能出现它们 |
CocoFeaturesMojo.applyFeatureDependencies 会为 Feature 计划中已启用、且项目尚未声明的每个 Feature 追加一个 runtime 作用域依赖。因为所有 Feature 都是 defaultEnabled=true,所以继承 coco-parent 的应用即使 starter 不含该模块,也会在构建期得到它;差别主要出现在“只依赖 starter、不使用 coco-parent”的场景。哪些模块应进入 starter,设计意图未见文档说明,行为以 coco-spring/coco-spring-boot-starter/pom.xml 与 coco-build/coco-maven-plugin 中的 CocoFeaturesMojo 为准;建议在 Issue 或 PR 里与维护者沟通你的选择。
如果选择“不加入”,请仿照 CocoRateLimitStarterDependencyContractTest 加一个契约测试(读取 starter 的 pom.xml,断言直接依赖集合不含新模块),防止日后被无意加入。
模块内至少包含(参照 coco-features/coco-lock/src/test/java/io/github/coco/feature/lock/ 与 coco-captcha):
-
Coco<Example>AutoConfigurationTest:用ApplicationContextRunner(Servlet 相关用WebApplicationContextRunner)+AutoConfigurations.of(...),验证默认 Bean、enabled开关、@ConditionalOnMissingBean覆盖;需要模拟“classpath 缺类”时用FilteredClassLoader; -
Default<X>Test、InMemory<X>StoreTest、Redis<X>StoreTest等功能单测; - 若有 Redis 三件套,补 “store-type=redis 但缺少 Redis 类时启动失败” 的断言(
CocoLockAutoConfigurationTest中有coco.lock.store-type=redis requires的断言)。
- 用户文档:
website/docs/features/example.md,英文版website/i18n/en/docusaurus-plugin-content-docs/current/features/example.md。 - 官网侧边栏是显式列表(
website/sidebars.ts的docsSidebar),新增页面必须手工加入,如'features/example'。 - 官网 特性开关 中的 Feature 一览表需要增加一行(英文版同步)。
- agent-skills 的文档索引由
website/docs与英文目录生成,更新用户文档后如需刷新,在coco-support/coco-tools/coco-agent-skills运行npm run build-index(脚本定义见该包package.json)。 - README:
README.md/README_CN.md是生成物,不要直接编辑;能力清单若要体现新 Feature,修改.github/readme/fragments/下en与zh-CN成对的40-capabilities-and-boundaries.md(能力分组)和50-samples-and-architecture.md(架构图),然后运行node .github/readme/scripts/render.mjs --write与--check。 - 架构类说明放
coco-support/coco-document/;本 Wiki 的相关页面(Module-*模块参考页、Feature 机制、速查表)需要同步更新,做法见 指南:维护与发布本 Wiki。 - 用户可见的新能力还应在
CHANGELOG.md的[Unreleased]下记一条。
.github/release/release-artifact-expectations.json 的 modules[] 需要追加一项(字段含义见该文件顶部的 schema_version、group_id、checksum_suffixes):
{
"path": "coco-features/coco-example",
"artifact_id": "coco-example",
"packaging": "jar",
"artifacts": ["pom", "main", "sources", "javadoc"]
}原因:release_file_count_preflight.py 的 validate_expected_reactor 会比较反应堆里的模块路径与该清单的路径集合,不一致会抛出 Reactor differs from release expectations; missing=..., extra=...。该检查位于发布工作流的 central-capacity 作业。
文档与实现存在出入:该清单记录 34 个模块,当前反应堆共有 40 个项目,二者并不一致,以 release_file_count_preflight.py 的比对输出为准。差异集中在两类:清单缺少 coco-features 下 6 个新布局模块(coco-web、coco-audit、coco-security、coco-tenant、coco-data-permission、coco-openapi)以及 coco-build/coco-compatibility/ 下 8 个兼容门面;清单里仍保留旧布局的 coco-features/coco-feature-*(7 项)和 coco-spring/coco-config(1 项)。这与你新增的模块无关,本页只做陈述,未改动清单;如果你的改动会触及发布清单,请在 PR 描述里说明,并向维护者确认是否已有进行中的同步。
命令来自 AGENTS.md、CONTRIBUTING.md 与 CI 配置,请以你的环境为准。先按 AGENTS.md 设置 JDK 21(见 快速开始):
# 单模块 + 上游依赖
mvn -pl :coco-example -am test
# StandardCocoFeatures 与 starter 相关测试一并验证
mvn -pl :coco-spring-boot-starter -am test
# CI 同款完整验证
mvn -B -ntp install
# 静态分析(Checkstyle 阻塞 CI;SpotBugs 目前软失败)
mvn -B -ntp checkstyle:check涉及发布行为(pom、插件、BOM)时再跑 release profile 冒烟:
mvn -B -Prelease '-Drevision=1.0.0' '-Dgpg.skip=true' '-DskipTests' verify完成后:修改了源码就执行 codegraph sync .;提交前 git status -sb。
如果不是新增而是把已发布的 coco-feature-x 改名为 coco-x,必须遵守 module-layout.md 的兼容基线:
- 在
coco-build/coco-compatibility/<旧 artifactId>/保留一个无源码门面,参照coco-build/coco-compatibility/coco-feature-web/pom.xml:<parent>为coco-framework(relativePath为../../../pom.xml),packaging为jar,唯一依赖是规范制品coco-x(${project.version}); - 门面不得包含实现类、自动配置导入、
spring.factories、消息资源; - 在根
pom.xml的<modules>加入门面;BOM 同时管理新旧两个坐标;starter 只组合新坐标(CocoMybatisPlusStarterDependencyContractTest断言了这一点); - 增加编译探针测试
<门面名>CompatibilityCompileProbe(位于src/test/java/io/github/coco/compatibility/<名称>/),把旧坐标曾对外暴露的公开类型以Class<?>[]的形式引用起来; - Java 包名、公开 FQCN、配置前缀、feature id、插件 goal 保持不变;
-
StandardCocoFeatures里该 Feature 的裁剪集合要同时包含新旧 artifactId(如Set.of("coco-web", "coco-feature-web"))。
仓库 · CHANGELOG · CONTRIBUTING · GOVERNANCE · SECURITY · 许可证 Apache-2.0
本 Wiki 镜像自仓库的 docs/wiki 目录,请通过 PR 修改,不要在网页上直接编辑。
开始
架构
模块参考
约定与指南
速查与排障
仓库