鸿蒙大型项目高级模块化拆分:高内聚低耦合架构设计/HSP动态交付/接口契约/依赖倒置落地规范
·




掌握 HAR/HSP 模块化体系、建立接口契约与依赖倒置规范、落地路由解耦与 DI 注入、让多团队协作的大型工程可持续演进
一、前置思考:模块化是"大型工程的生存方式"
1.1 单模块巨兽的困境
| 问题 | 表现 |
|---|---|
| 编译慢 | 改一行全量编译(第 38 篇联动) |
| 冲突多 | 多人改同一文件频繁冲突 |
| 测试难 | 无法独立验证模块 |
| 复用差 | 代码无法跨业务复用 |
| 失控 | 依赖纠缠成"意大利面条" |
核心认知:工程规模到一定程度,模块化不是选择,是生存方式。
1.2 模块化的收益
| 维度 | 单模块 | 模块化 |
|---|---|---|
| 编译 | 全量 32 分钟 | 增量 40 秒 |
| 协作 | 全队抢同一文件 | 模块自治 |
| 测试 | 全量回归 | 模块独立测试 |
| 复用 | 复制粘贴 | HAR 共享 |
| 交付 | 整体发布 | HSP 按需 |
1.3 模块化的原则
高内聚: 一个模块只做一件事, 内部紧密
低耦合: 模块间只通过接口通信, 不互相依赖实现
依赖倒置: 上层依赖抽象(接口), 不依赖具体实现
二、核心原理:HAR / HSP / HAP
2.1 三类模块
| 类型 | 全称 | 特点 | 用途 |
|---|---|---|---|
| HAP | Harmony Ability Package | 可独立运行 | 应用入口/页面 |
| HAR | Harmony Archive | 静态库, 编译时打包 | 代码/资源共享 |
| HSP | Harmony Shared Package | 动态库, 运行时加载 | 动态交付/共享 |
2.2 模块依赖关系
HAP (entry) ──► HAR (common/ui) ──► 编译期合并
HAP (entry) ──► HSP (feature_x) ──► 运行期按需加载
HAR ──► HAR (可嵌套, 注意循环)
HSP ──► HAR (共享包可依赖静态库)
关键约束:
- HAR 之间禁止循环依赖;
- HSP 不依赖 HAP(反向依赖会破坏动态性);
- 依赖方向单向:上层 → 下层。
2.3 高内聚低耦合的设计
┌────────────┐
│ entry HAP │ (编排层: 只做组装)
└─────┬──────┘
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ feature_1 │ │ feature_2 │ │ feature_3 │ (业务模块: 各自内聚)
└─────┬────┘ └─────┬────┘ └─────┬────┘
└──────────┐ │ ┌──────────┘
▼ ▼ ▼
┌─────────────────────┐
│ core 基础层 │ (common/network/data/ui)
│ (稳定, 少变更) │
└─────────────────────┘
三、源码/API 深度解析:落地四大规范
3.1 接口契约设计
原则:模块间只通过显式接口通信,不直接访问对方内部。
// 契约模块 (contract): 定义模块对外接口
// module: feature_shop/contract
export interface ShopService {
getProducts(category: string): Promise<Product[]>;
getProductDetail(id: string): Promise<ProductDetail>;
}
// 实现模块 (impl): 实现契约
// module: feature_shop/impl
export class ShopServiceImpl implements ShopService {
getProducts(category: string): Promise<Product[]> {
return network.fetch('/shop/products?cat=' + category);
}
getProductDetail(id: string): Promise<ProductDetail> {
return network.fetch('/shop/detail/' + id);
}
}
契约规范:
- 契约接口放独立模块(contract 包),实现与使用方都依赖契约;
- 接口方法必须有完整类型定义(禁止 any);
- 接口变更 = 契约变更 = 需要评审;
- 版本管理:契约升级向后兼容。
3.2 依赖倒置(DIP)
// ❌ 上层直接依赖具体实现 (耦合)
import { UserApiImpl } from '../../feature_user/impl/UserApiImpl';
class OrderPage {
private api = new UserApiImpl(); // 直接 new 实现
}
// ✅ 依赖倒置: 上层依赖抽象接口
import { UserApi } from '../../feature_user/contract/UserApi';
class OrderPage {
private api: UserApi;
constructor(api: UserApi) {
this.api = api; // 由外部注入(DI)
}
}
依赖倒置的收益:
- 替换实现零改动(mock/新实现);
- 测试可注入假实现;
- 依赖方向清晰可控。
3.3 路由解耦
原则:模块间跳转通过路由表,不直接 import 对方页面。
// 路由注册 (各模块注册自己的页面)
// module: feature_shop
routerMap.register('shop/home', () => {
return 'pages/ShopHome'; // 页面路径(可配合动态import)
});
routerMap.register('shop/detail', () => {
return 'pages/ShopDetail';
});
// 路由跳转 (任意模块, 只依赖路由表)
// module: entry
import { router } from '@kit.ArkUI';
function goShop(): void {
const url = routerMap.resolve('shop/home');
router.pushUrl({ url: url, params: { from: 'entry' } });
}
路由解耦的收益:
- 模块间零直接依赖;
- 新增/替换页面不改调用方;
- 可统一做登录拦截/埋点/降级。
3.4 DI 注入(轻量实现)
// 轻量 DI 容器
export class ServiceLocator {
private static services: Map<string, Object> = new Map();
static register<T>(key: string, impl: T): void {
this.services.set(key, impl as Object);
}
static get<T>(key: string): T {
return this.services.get(key) as T;
}
}
// 启动时注册实现
ServiceLocator.register<ShopService>('ShopService', new ShopServiceImpl());
// 任意模块获取(只依赖接口类型)
const shopService = ServiceLocator.get<ShopService>('ShopService');
shopService.getProducts('phone');
DI 纪律:
- 容器只存接口 → 实现映射;
- 注册集中在 App 启动期(组合根);
- 避免过度注入(对象越多,启动越慢)。
3.5 模块通信
| 场景 | 手段 | 说明 |
|---|---|---|
| 同进程模块间 | 接口直调 | 通过契约接口 |
| 页面跳转 | 路由表 | 解耦 |
| 跨模块事件 | 事件总线 | 解耦广播 |
| 跨进程/设备 | 分布式总线 | 第 27 篇联动 |
| 数据共享 | 公共数据层 | 单一数据源 |
四、企业级实战:多团队协作架构
4.1 模块划分方法论
| 步骤 | 动作 |
|---|---|
| ① 业务域分析 | 按业务域划分(电商: 商品/订单/支付/用户) |
| ② 通用层下沉 | 公共能力下沉 core(网络/数据/UI/工具) |
| ③ 依赖梳理 | 明确模块依赖 DAG, 消除循环 |
| ④ 契约先行 | 先定接口契约再并行开发 |
| ⑤ 交付模式 | 核心 HAR 静态, 低频业务 HSP 动态 |
4.2 团队与模块映射
团队A ──► feature_shop (商品/购物车)
团队B ──► feature_order (订单/售后)
团队C ──► feature_user (用户/会员)
团队D ──► core 基础层 (公共能力, 变更评审制)
协作规则:
- 各团队只改自己模块;
- core 变更需全局评审(影响所有人);
- 契约变更需双方团队确认;
- 每周模块依赖健康度巡检。
4.3 拆分收益评估
| 指标 | 拆分前 | 拆分后 |
|---|---|---|
| 增量编译 | 32 分钟 | 40 秒 |
| 模块独立测试 | 不支持 | 支持 |
| 代码冲突 | 高频 | 低频 |
| 复用 | 复制粘贴 | HAR 共享 |
| 交付 | 整体发布 | HSP 按需 |
4.4 架构治理
| 治理项 | 手段 |
|---|---|
| 依赖环检测 | CI 自动检测循环依赖 |
| 模块边界检查 | 禁止跨模块 import 实现 |
| 契约变更评审 | 契约变更走评审 |
| 模块健康度 | 依赖数/变更频率监控 |
| 架构守护 | 架构测试(ArchUnit 风格) |
五、排查与优化:架构问题定位
5.1 架构腐化信号
| 信号 | 危害 | 对策 |
|---|---|---|
| 循环依赖 | 构建警告/死锁 | 拆出公共依赖 |
| 上帝模块 | 单模块过大 | 拆分 |
| 跨层访问 | 业务直连数据库 | 分层约束 |
| 重复代码 | 多份实现 | 下沉 core |
| 依赖扩散 | 改一处动全身 | 契约收敛 |
5.2 高频坑点速查
- 模块间是否只通过接口通信?
- 是否存在循环依赖?
- 是否存在业务模块直连基础实现?
- 页面跳转是否走路由表?
- 依赖是否倒置(依赖抽象)?
- 契约接口是否有完整类型定义?
- 是否用 DI 解耦了实现?
- core 变更是否有评审机制?
- 低频业务是否用了 HSP 动态交付?
- CI 是否检测依赖环?
六、总结与进阶
6.1 收益模型(参考实测)
| 指标 | 拆分前 | 拆分后 | 提升 |
|---|---|---|---|
| 增量编译 | 32 分钟 | 40 秒 | -98% |
| 周代码冲突 | 45 次 | 4 次 | -91% |
| 模块独立测试 | 不支持 | 全支持 | 100% |
| 新功能交付 | 2 周 | 3 天 | -79% |
6.2 工程规范
- 契约先行:先定接口再开发;
- 依赖倒置:上层依赖抽象;
- 路由解耦:页面跳转走路由表;
- core 评审:基础层变更评审制;
- 架构守护:依赖环/边界 CI 自动检测(第 44 篇联动)。
6.3 进阶方向
- HSP 动态交付深度:按需下载与版本管理(第 35 篇联动);
- 微前端思路:页面级动态化交付;
- 架构测试:架构规则自动化断言;
- 多端模块复用:一套模块适配多设备(第 7 赛道联动)。
附:Demo 演示说明
| Tab | 演示内容 |
|---|---|
| 🧩 模块分层 | HAP/HAR/HSP 三类模块 + 分层架构图 |
| 🔗 依赖DAG | 依赖图优化前后对比(消除循环/收敛扩散) |
| 📜 接口契约 | 契约接口定义 + 实现 + 依赖倒置代码演示 |
| 🔄 路由解耦 | 路由注册/解析/跳转 流程演示 |
| 📊 架构治理 | 腐化信号清单 + 拆分收益对比 |
更多推荐



所有评论(0)