Files
baiyue-firstleg/firstleg/docs/firstleg-internal-modularization-baseline.md
T
2026-08-25 11:52:25 +08:00

12 KiB
Raw Blame History

Firstleg 内部模块化基线与首切片方案

1. 范围与生成方式

本基线只分析 firstleg 同一部署单元,不拆微服务,不改变业务流程、数据库结构、OpenAPI 或跨服务契约。分析输入为:

  • src/main/java:429 个 Java 文件;948 条可解析的模块内类依赖;66 个物理包。
  • src/test/java:169 个 Java 文件。与主源码合并分析时共 598 个 Java 文件、1,635 条模块内类依赖、69 个物理包。

可重复命令:

node firstleg/scripts/analyze-firstleg-dependencies.mjs --source-set main --format markdown
node firstleg/scripts/analyze-firstleg-dependencies.mjs --source-set all --format json
node firstleg/scripts/analyze-firstleg-dependencies.mjs --source-set main --format dot

分析器解析显式内部 import、静态 import 及同包类型引用,输出物理包图、逻辑切片图、SCC、跨切片类依赖、Entity/DTO 泄漏和 Mapper 泄漏。Spring 字符串扫描路径、反射、MyBatis SQL 中的表引用不属于 Java 类型图,因此在本报告单独标注。

2. 当前 SCC

2.1 主源码物理包 SCC

当前只有一个多节点物理包 SCC:

com.baiyue.firstleg.mapper
  <-> com.baiyue.firstleg.repository

直接证据是全局 Repository 大量依赖全局 Mapper,同时 InboundAllocationQueryMapper 反向引用 repository.PriceBookQueryRows。这说明全局数据访问层尚不能按传统横向层直接单向化。

合并测试源码后,测试对生产类型的依赖把 SCC 扩大为下列十包;这不是生产运行时环,但表示测试也直接绑定了横向实现包:

controller -> loading.repository -> loading.service -> mapper
-> oceanbooking.controller -> oceanbooking.repository -> oceanbooking.service
-> pricing -> repository -> service -> controller

2.2 逻辑切片 SCC

逻辑切片图存在一个八节点 SCC:

booking-customs-trucking
finance-integration
inbound
measurement-pricing
outbound
shared-technical
shipping-nodes
split-loading

customer-statistics 不在 SCC 内。当前 36 条切片间边均冻结在 src/test/resources/architecture/firstleg-slice-dependencies.json;合同允许删除现有边,不允许增加新边。

主要双向依赖包括:

双向关系 直接证据
入库 ↔ 测量计价 入库编排直接使用测量/费用 Entity、Mapper;计价与客户费用查询直接使用入库单、状态和 Outbox
入库 ↔ 分柜装柜 统一入库读模型读取装柜/PDA Mapper 与 Entity;装柜结算和事件发布读取入库状态、托盘与 Outbox
分柜装柜 ↔ 出仓 货柜聚合读出仓历史;海外卸柜回写反向更新货柜/装柜事实
订舱报关拖车 ↔ 出仓 出仓门禁聚合报关/船期贡献者;报关和船期贡献者反向依赖出仓 DTO
订舱报关拖车 ↔ 分柜装柜 订舱货柜分配使用装柜实体;报关直接读取装柜计划与明细 Mapper
订舱报关拖车 ↔ 测量计价 价格簿校验影响订舱;供应商/渠道校验又由全局计价服务消费

3. 八个业务切片现状映射

类数量和跨切片依赖数量以主源码图为准;“出/入”均为类级直接依赖条数。

业务切片 当前物理位置与识别范围 类数 出/入 主要边界问题
入库 全局 controller/service/repository/mapper/entity/dto 中 Inbound*、Fulfillment*、Assignment*、UnifiedInbound* 67 26/60 被计价和装柜大量反向读取;InboundWorkflowRepository 直接持有测量/费用 Mapper
测量计价 全局层中的 Measurement*、Mojia*、Pricing*、PriceBook*、Fba*、Dimension*、FeeItem*、InboundFee*,以及 pricing/ 113 72/25 最大切片;对入库有 35 条直接依赖,其中 20 条为跨切片数据模型泄漏、6 条为 Mapper 泄漏
分柜装柜 split/、loading/ 加全局 Loading*、SmartLoading*、货柜与 PDA 类 82 50/32 同时依赖入库、订舱、出仓和共享层;全局 Repository 直接读取 loading/split Mapper/Entity
订舱报关拖车 oceanbooking/、customs/,全局 TruckAppointment*、WarehouseSupplier*、出仓门禁贡献者 68 25/13 CustomsRepository 直接读取入库与装柜 Mapper;订舱 Repository 直接读取全局货柜 Entity
出仓 outbound/dispatch/ 与全局 OutboundDispatch*、运输历史、海外卸柜回写 17 8/10 与装柜、订舱门禁双向;海外回写直接使用全局货柜 Mapper/Entity
船节点 portschedule/ 中 CarrierScheduleQuery/Node/Adapter/Attempt/Exception* 22 8/1 查询服务直接读取订舱、船期、货柜 Entity;节点 Outbox 直接使用入库 Outbox Mapper/Entity
客户统计 customerstatistics/ 4 1/0 Java 依赖最低;唯一跨切片依赖为 Controller 调用 FirstlegScopeGuard。SQL 只读订舱、货柜和装柜表
财务集成 全局 InternalFinance*、FinanceFeeSource*、FinanceCostSnapshot*、付款凭证/快照类 20 17/6 直接读取入库费、柜次费、装柜及共享 Outbox 的 Entity/Mapper;继续受财务总控冻结的业务口径约束

共享技术层当前有 36 类,包含应用启动/配置、Scope Guard、单据号、部分跨服务 Client 及无法安全归属的全局模型。跨服务适配器按 client 包独立标识,不能因逻辑切片归属而被迁入应用核心。

4. 泄漏与逆向依赖基线

主源码共有 88 条跨切片 Entity/DTO 泄漏、31 条跨切片 Mapper 泄漏。高频项如下:

来源 → 目标 Entity/DTO Mapper 代表性证据
测量计价 → 入库 20 6 FbaPricingRepository -> InboundOutboxEntity/Mapper;客户费用查询直接使用 InboundOrderDetail
分柜装柜 → 入库 7 6 LoadingDeliveryService -> InboundStatusHistoryEntity/Mapper;装柜 Outbox 复用入库 Outbox
入库 → 测量计价 4 4 InboundWorkflowRepository -> InboundFee/Measurement/Review Mapper
入库 → 分柜装柜 4 3 UnifiedInboundReadRepository 直接读取装柜证据、PDA 任务、预装计划 Mapper/Entity
财务集成 → 入库 4 2 财务来源 Outbox 直接复用 InboundOutboxEntity/Mapper
订舱报关拖车 → 分柜装柜 3 3 CustomsRepository -> LoadingPlan/LoadingPlanItem Mapper
分柜装柜 → 订舱报关拖车 3 0 ContainerService -> OceanBookingContainer*Entity
船节点 → 入库 1 1 CarrierScheduleNodeOutboxService -> InboundOutboxEntity/Mapper

逆向依赖的判定不是简单的 controller -> service -> repository 层次关系,而是“业务核心直接依赖另一切片的持久化实现或 Entity”。首轮治理应优先用查询/命令 Port 替代这些直接引用,不共享领域 Entity。

5. 首切片选择:客户统计

选择 customer-statistics,不是因为预设,而是因为以下实际证据:

  1. 仅 4 个生产类,已集中在单一顶层包;没有业务切片对其产生 Java 类型入向依赖。
  2. 唯一跨切片 Java 依赖为 Web Controller 对共享 FirstlegScopeGuard 的调用,不涉及 Entity 或 Mapper 泄漏。
  3. 不属于当前八节点 SCC,也没有跨服务 Feign/HTTP Client。
  4. 现有调用面稳定:一个平台 GET 接口、一个 Admin API 消费者、两个后端测试。
  5. 它仍包含一个明确的内部逆向依赖:应用 Service 直接依赖 MyBatis Mapper,适合用最小 Out Port 证明目标依赖方向。

限制:CustomerTransitStatisticsMapper 的 SQL 只读 fl_ocean_booking、fl_container_booking、fl_loading_plan、fl_loading_plan_item。首切片只隔离 Java 依赖,不宣称拥有这些表,也不改变 SQL、索引或统计口径。

6. 首切片目标目录与允许依赖

customerstatistics/
  adapter/in/web/
    PlatformCustomerTransitStatisticsController.java
  application/
    CustomerTransitStatisticsQueryService.java
    model/CustomerTransitStatisticsModels.java
    port/out/CustomerTransitStatisticsReader.java
  adapter/out/persistence/
    CustomerTransitStatisticsMapper.java

允许方向:

adapter.in.web -> application
adapter.out.persistence -> application.port.out
application -> application.model + common-core

额外允许项仅限 Web 适配器使用 common-security、Spring Web 和现有 FirstlegScopeGuard。禁止 application 依赖 MyBatis、Spring MVC、全局 entity/mapper/repository/service 或其他业务切片。

7. 精确迁移清单、调用方与测试

首切片实施时只迁移/新增以下文件:

当前文件 目标/动作
customerstatistics/controller/PlatformCustomerTransitStatisticsController.java 移到 adapter/in/web/,URI、权限和响应保持不变
customerstatistics/dto/CustomerTransitStatisticsDtos.java 移到 application/model/,保持 JSON 字段与数值语义不变
customerstatistics/service/CustomerTransitStatisticsService.java 移到 application/ 并改名为 Query Service,只依赖 Out Port
customerstatistics/mapper/CustomerTransitStatisticsMapper.java 移到 adapter/out/persistence/,实现/扩展只读 Out Port;SQL 原样保留
新增 customerstatistics/application/port/out/CustomerTransitStatisticsReader.java 定义 summarize(customerId) 与只读投影,不暴露 MyBatis 类型
src/test/java/.../customerstatistics/CustomerTransitStatisticsServiceTest.java Mock Out Port,不再 Mock Mapper
src/test/java/.../customerstatistics/CustomerTransitStatisticsContractTest.java 更新包引用,继续锁定 URI、权限、SQL、OpenAPI 与索引语义
BaiyueFirstlegApplication.java、BaiyueFirstlegApplicationContractTest.java 只有 Mapper 扫描路径因移动确需调整时修改;不改变其他扫描包

外部调用方只读核对,不在首切片修改:

  • firstleg/src/main/resources/openapi/customer-statistics-v1.yaml
  • apps/admin-web/src/modules/container-loading/api/platformContainerLoading.ts
  • apps/admin-web/src/modules/container-loading/api/platformContainerLoading.test.ts

8. 回滚边界

  • 不修改表、SQL 内容、接口路径、权限码、响应字段、统计定义、Nacos、部署或跨服务契约。
  • 回滚只涉及上述客户统计 Java 包移动、Out Port 和对应测试;可整体恢复到现有四类结构。
  • 若 Mapper 继承 Out Port 导致 MyBatis 代理行为或扫描合同失败,停止实施并恢复包内变更,不以增加全局扫描、共享 POM 依赖或兼容 Bean 绕过。
  • 首切片完成前不开始第二切片。

9. 当前只读合同与后续协作

本轮已增加模块内、无第三方依赖的只读合同:

  • scripts/analyze-firstleg-dependencies.mjs:生成 JSON/Markdown/DOT 和 Tarjan SCC。
  • src/test/resources/architecture/firstleg-slice-dependencies.json:冻结当前切片边;历史边可减少,不可增加。
  • scripts/analyze-firstleg-dependencies.test.mjs:验证基线无新增边,并锁定客户统计 4 类、唯一出向边、零入向边和不进入 SCC。

与其他架构专项的依赖:

  • 专项一(拓扑与质量门禁):决定是否引入 ArchUnit、如何把本合同接入 Maven/CI、以及共享 POM 和全局质量门禁。当前未修改 POM;建议由专项一把本脚本作为 Firstleg 局部门禁接入,再以 ArchUnit 替换/增强源码级解析。
  • 专项二(同步依赖与失败传播):在为其他切片定义应用 Port 前提供 Feign/HTTP 调用图和失败语义。客户统计当前没有跨服务 Client,因此首切片无需等待专项二;后续入库、财务集成、订舱和装柜切片必须协调。

10. 最小验证命令

node --test firstleg/scripts/analyze-firstleg-dependencies.test.mjs
node firstleg/scripts/analyze-firstleg-dependencies.mjs --source-set main --format markdown
mvn -pl firstleg -am -Dtest=CustomerTransitStatisticsServiceTest,CustomerTransitStatisticsContractTest -Dsurefire.failIfNoSpecifiedTests=false test

当前只新增只读脚本、合同和报告,没有 Java 签名变化;前两条命令是本轮直接门禁。第三条用于首切片实际迁移后的 Maven 行为验证。