# ADR-0029: crm-opportunity 编译期依赖 crm-lead —— 下游反向依赖上游的技术债 ## Status Accepted(技术债,带触发重设计条件) ## Context 线索「转商机」由 crm-lead 定义 outbound port `OpportunityCreationPort`(`crm-lead/port/outbound`),商机模块提供实现(ADR-0020:首个实现须与 crm-lead 同库同事务)。 票 11/12/13 落地商机侧实现 `OpportunityCreationPortImpl` 时发现:要实现该 port,crm-opportunity 必须能在**编译期** import `com.crm.lead.port.outbound.*`(接口 `OpportunityCreationPort` + 命令 `CreateOpportunityCmd` + 异常 `OpportunityCreationException`)。而此前 `crm-opportunity/pom.xml` **并不依赖 crm-lead**——这正是该 port 长期「定义了但无人实现」的真实原因(不是遗漏,是依赖方向未解决)。 crm-lead 侧用 `ObjectProvider#getIfAvailable()` 注入,只解耦了「运行期 bean 是否存在」,**没有**解耦「编译期接口是否可见」——实现方仍需编译依赖接口所在模块。 领域上商机是线索的**下游**(线索转商机)。让 crm-opportunity 依赖 crm-lead = **下游反向依赖上游**,方向不干净。 ## Considered Options - **crm-opportunity 直接依赖 crm-lead(采纳)**:pom 加一行即可,ADR-0020 的同库同事务立即成立。代价是方向倒置 + 埋循环依赖雷(见 Consequences)。 - **抽共享 port 契约模块**(把 3 个 port 类移到 crm-base 或新建 crm-contract,两边都依赖它,谁都不直接依赖对方):干净、符合 hexagonal、无循环。否决于**当前阶段**——要动 crm-lead 现有 3 个类的包路径 + 全部 import + 可能新建模块 + 改两个 pom,工作量与影响面明显超出票 11/12/13 范围,属在「商机模块刚落地、crm-lead 尚不读商机」时提前付重构成本。留作终态方向。 - **事件/最终一致**:ADR-0020 已否决为过度设计(近期大概率同库单体)。 ## Decision `crm-opportunity/pom.xml` 显式依赖 `crm-lead`,以在编译期实现其 outbound port。pom 内以注释标注本 ADR 为技术债来源。 ## Consequences - **技术债(方向倒置)**:下游 crm-opportunity 依赖上游 crm-lead,商机模块被拖进线索模块的依赖闭包。 - **循环依赖雷**:一旦 crm-lead 将来需要**读商机**(如线索详情展示「已转的商机」),会形成 crm-lead ⇄ crm-opportunity 循环,Maven 直接编译失败。届时**必须**改走「抽共享 port 契约模块」方案(上方否决项),并 supersede 本 ADR。 - **兜底**:本债与 ADR-0020 的「独立部署触发重设计」同源——当前同库单体阶段可接受;触发条件(crm-lead 读商机 / 商机独立部署)出现时一并重设计。 ## 决策来源 票 11/12/13 实现(走法甲,customerId 可空占位)+ 用户拍板「甲,但需要记录这一技术债」(20260825 grill)。关联 ADR-0020。