market 5 类(MarketAccountProjectionProvider/AdminMarketReviewServiceImpl/MarketInstallServiceImpl/ MarketLicenseServiceImpl/MarketPublishServiceImpl)此前直接构建 member.dal 的 AccountRecordProjectionDO 并经 AccountRecordProjectionMapper upsert 进 member 表——跨 BC 写路径违例(原 BcBoundaryArchTest 单点豁免登记)。 本次整改(ultracode 工作流:设计→实现→对抗验证;JDK21 scoped 实跑验证): - member-api 新增对外写端口 MuseAccountRecordProjectionApi + MuseAccountRecordProjectionSaveReqDTO; member-server MuseAccountRecordProjectionApiImpl 实现,读写 member 自有 DAL,整体平移原 upsert 语义 (insert/update 分支、update rows!=1 抛 IllegalStateException 防伪成功)。 - 安全边界:DTO 刻意不含 tenantId,由实现侧从 TenantContextHolder 注入,杜绝调用方(他域)伪造租户。 - 事务红线:写端口为进程内 Bean、不加 @Transactional,沿用调用方(market 的 REQUIRES_NEW)事务上下文, market 业务回滚则投影一并回滚,原子性与原实现等价。 - market 5 类改消费写端口 + DTO 替代 DO,移除 member.dal 代码依赖; BcBoundaryArchTest.KNOWN_VIOLATION_EXEMPTIONS 清空 → 通用 BC 门全绿(0 Architecture Violation)。 - 测试:insert/update/租户隔离语义随实现迁移至 MuseAccountRecordProjectionApiImplTest(4/0F); provider 测试改 mock 写端口、保留"失败→写 blocked outbox→上抛 UNAVAILABLE"语义(5/0F); market 其余 4 类测试同步(AdminReview 16 / Publish 14 / License 9 / Install 5)。 附带修复 round-2 一处假绿:round-2 把 ContentKnowledgeWorkOwnerFacade 重构为消费 MuseContentWorkOwnerApi 后, 旧测试 KnowledgeWorkOwnerFacadeTest 仍断言旧 WorkMapper 行为(当时验证构建在平台 QiniuSmsClientTest 时区用例处中止、未真正跑到 knowledge 模块,故漏网=假绿)。删除该旧测试,其装配守卫 (@ConditionalOnBean 值应为 MuseContentWorkOwnerApi)与 Unavailable 兜底失败关闭两用例并入 ContentKnowledgeWorkOwnerFacadeTest(3→5/0F),覆盖不丢。 验证(JDK21;-Dtest scoped 避开预存红 + muse-server -am):BUILD SUCCESS,日志无任何 <<< FAILURE/ERROR; BcBoundaryArchTest 1/0F 且 0 Architecture Violation、AgentsInfraIntegrity 3/0F、ContractFirst 2/0F、 P1rApiCoverage 7/0F、member 4/0F、knowledge 5/0F、content 端口 7/0F、market 5 类全绿,全 reactor 模块 SUCCESS。 注:本仓存在预存红测试(非本轮引入,启用真实测试 + CI 接电后将暴露,已登记 总账/AGENTS 后续): MuseAiTaskServiceTest 桩 eventPublishOutboxService 缺失致 11 例 NPE、MuseAiEventPublishOutboxMapperTest 需真实 PostgreSQL、平台 QiniuSmsClientTest 硬编码北京时区在非 +8 机器失败。故全量 reactor / CI-on-main 当前仍会因这些预存红呈 RED——本轮只声明 market 整改切片与 BC/契约/loop/覆盖门全绿,不声称全仓全绿。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.5 KiB
规则:BC 边界 —— 禁跨域 DAL 直连(ArchUnit 机械门禁)
类型:硬约束(rules) · 状态:生效 2026-06-14 · 门禁:ArchUnit(机械阻断,非自觉) 上位:
verification-and-anti-false-green.md§二.6 由来:对抗复盘 A4——软约束下 AI 模块ContentMuseWorkOwnerFacade直连 content 的WorkMapper/ChapterMapper/BlockMapper+WorkDO/ChapterDO/BlockDO,实质破了"模块化单体 + 清晰 BC 边界"(ADR)的根基,且会随时间蔓延、拆服务时代价爆炸。
一、规则
- 一个业务域(BC)模块不得 import / 依赖他域的 DAL(
.dal..,含 dataobject 与 mysql Mapper)。 - 跨 BC 读写只能走对外 API 或 facade-api 契约(本域定义端口,他域提供适配器;或经对外 API)。
- 本域读自己的 DAL 不受限。
正面范例:P1 增量 2 的
AiSuggestionMergeProjectionFacade—— AI 实现 content 的ContentAiSuggestionFacade端口、只读 AI 自有 suggestion 库,不碰 content DAL,即合规的跨 BC 接缝。
二、机械门禁(不是自觉)
- 测试:
muse-server/src/test/java/cn/iocoder/muse/server/framework/arch/BcBoundaryArchTest.java(ArchUnit 1.3.0)。 - CI:随
muse-cloud/.github/workflows/maven.yml(P0 已打开测试门、JDK21)在 PR/push 阻断。 - 任何新增跨域 DAL 依赖 → 测试红 → 阻断合入。
三、违例登记与整改
规则现为通用条件(BcBoundaryArchTest.no_business_bc_may_depend_on_another_bc_dal):覆盖全部业务 BC 间方向(ai/content/knowledge/market/meta/member/events),任一域依赖他域 .dal 即红;已知违例在测试的 KNOWN_VIOLATION_EXEMPTIONS 单点显式登记(2026-06-14 评审整改:此前只守 AI 单向,放过了 knowledge/market 同构活违例)。
已整改(历史)
| 违例类 | 曾经内容 | 整改 | 验收 |
|---|---|---|---|
module.ai.application.muse.facade.ContentMuseWorkOwnerFacade |
直连 content Work/Chapter/Block Mapper+DO(字节码 31 处) |
content 暴露只读端口 MuseContentWorkOwnerApi(content-api 定义、content-server 实现读自有 DAL);AI 适配器改消费该端口 |
反向删豁免+旧码→红报 31 例;正向整改→绿,适配器 13/0F、端口实现 7/0F |
module.knowledge.application.muse.facade.ContentKnowledgeWorkOwnerFacade |
直连 content WorkMapper/WorkDO(与 AI 同构) |
复用同一 MuseContentWorkOwnerApi.getActiveOwnedWorkRevision 端口,移除 content.dal 依赖 |
通用门禁绿(0 Architecture Violation)、新增适配器单测 3/0F |
module.market.application.muse.{MarketAccountProjectionProvider, AdminMarketReviewServiceImpl, MarketInstallServiceImpl, MarketLicenseServiceImpl, MarketPublishServiceImpl}(5 类) |
写路径:构建 member.dal.AccountRecordProjectionDO、经 AccountRecordProjectionMapper upsert 进 member 表 |
member 暴露写端口 MuseAccountRecordProjectionApi.upsertRecordProjection(DTO)(member-api 定义、member-server 实现读写自有 DAL,整体平移原 upsert 语义且事务上下文沿用调用方);market 5 类改消费端口 + MuseAccountRecordProjectionSaveReqDTO 替代 DO,移除 member.dal 依赖 |
反向:删豁免 5 类即门禁收紧;正向整改→通用门禁绿(0 Architecture Violation),新增 member 端口实现单测 4/0F、market provider 测试迁移为 mock 写端口 |
活跃登记(待整改,单点豁免)
| 违例类 | 内容 | 处置 | 整改方向 |
|---|---|---|---|
| (空) | —— | —— | 当前无活跃跨域 DAL 违例;BcBoundaryArchTest.KNOWN_VIOLATION_EXEMPTIONS 已清空 |
豁免一律临时、显式、可见(测试源码点名)。违例消除后必须从
KNOWN_VIOLATION_EXEMPTIONS删除该类——收紧即整改验收。新违例不在豁免内,一律红。
遗留(更深一层 BC 收口,不在本次范围):market-server 的 pom 仍依赖
muse-module-member-server(可见 member 全部 internal);本次只消除 member.dal 的代码 import(ArchUnit 校验 import、不校验 pom 坐标),pom 坐标收口可登记后续。
四、扩展方式
- 规则已是通用条件,自动覆盖所有业务 BC 间的
.dal跨域依赖,无需为新方向加@Test。 - 新增业务 BC:加入
BcBoundaryArchTest.BUSINESS_BCS即纳入约束。 - 收紧已知违例:整改后从
KNOWN_VIOLATION_EXEMPTIONS删除该类,门禁随即收紧。 - 更强边界(如禁跨域
.application非 facade-api 包耦合):在条件里追加判断,但须为合规 facade 端口留通道(见评审 P1-3,属后续)。