本节目标
- 掌握大型 HarmonyOS 应用的模块拆分策略,理解按业务域、按功能层、按团队边界三种拆分维度的适用场景
- 掌握依赖治理的核心原则,能够使用 ohpm 的 override、resolve_conflict 和依赖分析工具控制依赖数量与版本一致性
- 掌握构建优化的关键配置,能够通过增量编译、并行构建、HSP 共享和编译缓存缩短构建时间
- 掌握团队协作规范,包括代码分层约定、命名规范、提交规范和 Code Review 检查清单
- 掌握多环境配置管理方案,能够通过 product/flavor 机制管理开发、测试、生产环境的差异化配置
- 掌握 CI/CD 流水线的搭建方法,能够将编译、测试、性能门禁和签名打包集成到自动化流程
- 能够为一个中大型应用设计可维护、可扩展、可协作的工程架构
一、模块拆分策略
1.1 按业务域拆分
按业务域拆分是最直观的拆分方式,每个业务域对应一个独立的模块。例如一个电商应用可以拆分为首页模块、商品模块、购物车模块、订单模块、个人中心模块等。每个业务模块内部高内聚,包含该业务域的所有页面、组件、数据模型和业务逻辑。
适用场景:业务边界清晰、团队按业务线划分、需要独立开发和测试的大型应用。
核心原则:模块之间通过明确定义的接口通信,尽量减少直接依赖。业务模块之间不应存在循环依赖,可以通过依赖注入或事件总线解耦。
1.2 按功能层拆分
按功能层拆分是将应用按技术职责分为 UI 层、业务逻辑层、数据访问层和基础设施层。这种拆分方式与架构设计的分层模型一一对应。
适用场景:技术栈统一、需要严格控制分层边界、追求架构一致性的团队。
核心原则:上层依赖下层,下层不感知上层。UI 层只负责展示和交互,业务逻辑层负责规则编排,数据访问层负责持久化与网络,基础设施层提供通用工具和日志。
1.3 按团队边界拆分
按团队边界拆分是康威定律在软件架构中的直接体现------系统架构应与组织结构保持一致。每个团队负责的模块可以独立开发、独立测试、独立发布。
适用场景:多团队协作、需要并行开发、模块间接口稳定的大型项目。
核心原则:模块的所有权明确,接口变更需要经过双方评审。每个模块有独立的负责人和 Code Review 流程。
1.4 三种维度的组合使用
实际项目中,三种拆分维度通常组合使用。推荐的结构是:先按业务域拆分为顶层模块,每个业务域内部再按功能层拆分为子模块,最后根据团队边界确定模块的归属和维护责任。
text
entry/ # 主入口(HAP)
├── features/
│ ├── home/ # 首页业务域(HAR)
│ │ ├── ui/ # UI 层
│ │ ├── viewmodel/ # 业务逻辑层
│ │ └── data/ # 数据访问层
│ ├── product/ # 商品业务域(HAR)
│ ├── cart/ # 购物车业务域(HAR)
│ └── order/ # 订单业务域(HAR)
├── commons/
│ ├── base/ # 基础工具(HAR)
│ ├── network/ # 网络封装(HAR)
│ ├── storage/ # 持久化封装(HAR)
│ └── ui-components/ # 通用 UI 组件(HAR)
└── shared/
└── constants/ # 全局常量(HAR)
二、依赖治理
2.1 依赖数量的控制
依赖数量直接影响构建时间和包体积。每个第三方依赖都会增加编译时间、增大包体积、引入潜在的安全风险。建议遵循以下原则:
优先使用系统能力:HarmonyOS 已经提供了丰富的系统 Kit,如网络、存储、媒体、AI 等,优先使用系统能力而非引入第三方库。
评估依赖的必要性:引入一个依赖前,评估它是否真的必要,是否可以用少量代码自行实现,是否有更轻量的替代方案。
定期清理无用依赖:项目迭代过程中,部分依赖可能已经不再使用。定期使用依赖分析工具扫描未使用的依赖并清理。
2.2 依赖版本的统一
多模块项目中最常见的问题是依赖版本不一致。模块 A 依赖了 lodash 1.0,模块 B 依赖了 lodash 2.0,打包时两个版本都会被包含,导致包体积膨胀和潜在的运行时冲突。
使用 ohpm 的 override 机制强制统一版本:
json5
// oh-package.json5(工程级)
{
"overrides": {
"lodash": "2.0.0",
"axios": "1.6.0"
}
}
开启 resolve_conflict 自动解决冲突:
json5
// .ohpmrc
{
"resolve_conflict": true
}
2.3 依赖分析工具
使用 ohpm list 查看工程的依赖树,识别重复依赖和版本冲突。使用 DevEco Studio 的 Build Analyzer 分析各依赖的体积占比,识别体积大户。
2.4 依赖分层的约束
在大型项目中,需要对依赖关系施加约束,避免模块间随意引用导致的架构腐化。推荐使用以下约束:
业务模块不能反向依赖主入口:features 层的模块不能依赖 entry 模块。
业务模块之间禁止直接依赖:features 层的模块之间如需通信,通过 commons 层的接口或事件总线解耦。
公共模块不能依赖业务模块:commons 层的模块不能依赖 features 层的模块。
三、构建优化
3.1 增量编译与并行构建
HarmonyOS 的模块化编译基于 ES Module 的 Bundleless 编译模式,API 10 及以上版本的 Stage 模型工程默认开启。修改单个模块代码无需整包编译构建,增量编译构建时间极大减少。
hvigor 构建系统默认开启并行构建,可以利用多核 CPU 加速构建。在 hvigorfile.ts 中可以调整并行度配置。
3.2 编译缓存
DevEco Studio 支持编译缓存,将编译产物缓存到本地,下次构建时复用。缓存生效的前提是源文件未变化。建议在 CI 环境中配置缓存目录,加速重复构建。
3.3 HSP 共享减少重复编译
在多模块项目中,将公共代码和资源抽取为 HSP,消除 HAR 导致的重复拷贝。但需要注意:大量使用 HSP 替代 HAR,会在编译引用这些 HSP 共享包的模块时触发更多的语言编译任务,导致编译耗时和编译内存占用增加。需要综合评估包体积收益和编译时间成本。
3.4 构建配置优化
在 build-profile.json5 中配置以下选项加速构建:
json5
{
"app": {
"products": [
{
"name": "default",
"signingConfig": "default",
"compatibleSdkVersion": "5.0.0(12)",
"buildOption": {
"strictMode": {
"caseSensitiveCheck": true,
"useNormalizedOHMUrl": true
}
}
}
]
}
}
useNormalizedOHMUrl 开启后可以优化依赖解析效率。
四、团队协作规范
4.1 代码分层约定
明确各层的职责边界,避免职责混淆:
页面层(pages/views) :只负责 UI 展示和用户交互,不包含业务逻辑。通过 ViewModel 获取数据,通过事件回调触发业务操作。
ViewModel 层(viewmodel) :负责业务逻辑编排、状态管理、数据转换。不直接操作 UI,不直接访问数据库或网络。
数据层(data) :负责持久化和网络请求,提供统一的数据访问接口。不包含业务逻辑。
工具层(utils) :提供无状态的通用工具函数,不依赖任何业务模块。
4.2 命名规范
文件命名:页面文件以 Page 结尾(如 HomePage.ets),组件文件以组件名命名(如 TodoItem.ets),工具文件以功能命名(如 DateUtil.ets),模型文件以 Model 结尾(如 TodoModel.ets)。
变量命名:使用小驼峰(如 userName),常量使用全大写下划线分隔(如 MAX_RETRY_COUNT),类名使用大驼峰(如 TodoViewModel)。
接口命名:以 I 开头或使用描述性名称(如 UserInfo、TodoRepository)。
4.3 提交规范
推荐使用 Angular 提交规范,格式为 type(scope): subject:
type 类型:feat(新功能)、fix(修复)、docs(文档)、style(格式)、refactor(重构)、perf(性能)、test(测试)、chore(构建/工具)。
scope 范围:模块名,如 home、cart、network。
subject 描述:简洁的描述,使用中文或英文,不超过 50 个字符。
示例:feat(home): 添加待办搜索功能、fix(cart): 修复购物车数量计算错误。
4.4 Code Review 检查清单
每次提交前自查以下项目:
- 代码是否符合分层约定,是否存在跨层调用
- 是否有硬编码的字符串、颜色、尺寸
- 是否处理了异常和边界情况
- 是否使用了正确的状态管理装饰器
- 是否有内存泄漏风险(未释放的定时器、未取消的监听)
- 是否遵循了命名规范
- 是否有必要的注释和文档
五、多环境配置管理
5.1 环境差异化的需求
一个应用通常需要在开发、测试、生产三个环境中运行,每个环境的 API 地址、日志级别、功能开关都不同。硬编码环境配置会导致每次切换环境都要修改代码,容易出错。
5.2 使用 product 机制
在 build-profile.json5 中定义多个 product,每个 product 对应一个环境:
json5
{
"app": {
"products": [
{
"name": "develop",
"signingConfig": "default",
"compatibleSdkVersion": "5.0.0(12)",
"buildOption": {
"arkOptions": {
"buildProfileFields": {
"API_BASE_URL": "https://dev-api.example.com",
"LOG_LEVEL": "debug",
"ENABLE_MOCK": true
}
}
}
},
{
"name": "release",
"signingConfig": "release",
"compatibleSdkVersion": "5.0.0(12)",
"buildOption": {
"arkOptions": {
"buildProfileFields": {
"API_BASE_URL": "https://api.example.com",
"LOG_LEVEL": "error",
"ENABLE_MOCK": false
}
}
}
}
]
}
}
在代码中通过 BuildProfile 访问:
typescript
import BuildProfile from 'BuildProfile';
const API_BASE_URL = BuildProfile.API_BASE_URL;
const LOG_LEVEL = BuildProfile.LOG_LEVEL;
const ENABLE_MOCK = BuildProfile.ENABLE_MOCK;
5.3 配置文件的组织
将环境相关的配置集中到一个文件,通过 BuildProfile 注入不同值:
typescript
// commons/config/AppConfig.ets
export class AppConfig {
static readonly apiBaseUrl: string = BuildProfile.API_BASE_URL;
static readonly logLevel: string = BuildProfile.LOG_LEVEL;
static readonly enableMock: boolean = BuildProfile.ENABLE_MOCK;
static isDebug(): boolean {
return AppConfig.logLevel === 'debug';
}
}
六、CI/CD 流水线
6.1 流水线的核心环节
一个完整的 HarmonyOS 应用 CI/CD 流水线包含以下环节:
代码检查:运行 Code Linter 静态检测,检查代码规范和潜在问题。
编译构建 :执行 hvigorw assembleHap 构建 HAP 包,开启 Release 模式获取完整优化。
单元测试:运行 Hypium 单元测试,输出测试报告。
UI 测试:在模拟器或真机上运行 UI 自动化测试。
性能门禁:使用 DevEco Testing 运行场景化性能测试,对比性能基线,低于基线则阻断合入。
签名打包:配置发布签名,构建正式 HAP 包。
上传分发:将 HAP 包上传到 AppGallery Connect 或内部测试平台。
6.2 流水线配置示例
以下是一个基于 Jenkins 或 GitLab CI 的流水线配置示例:
yaml
stages:
- lint
- build
- test
- performance
- package
lint:
stage: lint
script:
- hvigorw codeLinter
build:
stage: build
script:
- hvigorw assembleHap --mode module -p product=default
unit_test:
stage: test
script:
- hvigorw test --mode module -p module=entry@default
performance:
stage: performance
script:
- deveco-testing run --task performance --baseline baseline.json
package:
stage: package
script:
- hvigorw assembleApp --mode project -p product=release
artifacts:
paths:
- build/outputs/default/*.hap
6.3 性能门禁的集成
性能门禁是防止性能退化的关键手段。设定性能基线(如冷启动 ≤3000ms、页面切换 ≤1000ms、帧率 ≥60 帧),每次代码合入前自动运行性能测试,低于基线则阻断合入。
DevEco Testing 的性能基线管理能力可以自动记录内存占用、CPU 负载等关键指标,通过历史数据对比精准定位性能退化问题,在性能退化早期发出预警。
6.4 构建缓存与并行加速
在 CI 环境中配置构建缓存和并行执行,可以显著缩短流水线执行时间:
yaml
cache:
paths:
- .hvigor/cache/
- node_modules/
- oh_modules/
build:
stage: build
script:
- hvigorw assembleHap --parallel --daemon
七、多元化习题
习题 1(判断题)
题目:在模块拆分中,业务模块之间可以直接相互依赖,不需要通过公共模块解耦。
答案:错误
解读:业务模块之间禁止直接依赖,如需通信应通过 commons 层的接口或事件总线解耦。直接依赖会导致模块间耦合度过高,一个模块的修改会牵连其他模块,破坏模块的独立性和可维护性。
习题 2(单选题)
题目:以下哪种拆分维度是康威定律在软件架构中的直接体现?
A. 按业务域拆分
B. 按功能层拆分
C. 按团队边界拆分
D. 按技术栈拆分
答案:C
解读:按团队边界拆分是康威定律在软件架构中的直接体现------系统架构应与组织结构保持一致。每个团队负责的模块可以独立开发、独立测试、独立发布。
习题 3(多选题)
题目:关于多环境配置管理,以下说法正确的有(多选):
A. 使用 product 机制定义多个环境,每个 product 对应一套配置
B. 通过 BuildProfile 在代码中访问环境配置
C. 环境配置应硬编码在代码中以便于管理
D. 将环境相关的配置集中到 AppConfig 统一管理
答案:A、B、D
解读:使用 product 机制定义多个环境,每个 product 对应一套配置,选项 A 正确。通过 BuildProfile 在代码中访问环境配置,选项 B 正确。环境配置不应硬编码在代码中,硬编码会导致每次切换环境都要修改代码,容易出错,选项 C 错误。将环境相关的配置集中到 AppConfig 统一管理,选项 D 正确。
习题 4(代码填空题)
题目 :请补全以下 build-profile.json5 配置,使用 override 机制强制统一 lodash 的版本为 2.0.0。
json5
// oh-package.json5(工程级)
{
"______________": {
"lodash": "2.0.0"
}
}
答案 :overrides
解读 :overrides 用于强制统一依赖版本。当多个模块依赖同一库的不同版本时,使用 overrides 指定统一使用的版本号,避免多版本重复打包导致的包体积膨胀和潜在运行时冲突。
习题 5(代码改错题)
题目:以下代码存在架构分层问题,请指出问题并修正。
typescript
// features/home/ui/HomePage.ets
import { RdbHelper } from '../../../commons/storage/RdbHelper';
@Entry
@Component
struct HomePage {
@State todos: Todo[] = [];
aboutToAppear(): void {
// 页面层直接访问数据层
this.todos = RdbHelper.queryAll();
}
build() {
List() {
ForEach(this.todos, (todo: Todo) => {
ListItem() {
Text(todo.title)
}
})
}
}
}
答案:页面层直接访问数据层,违反了分层约定。页面层应通过 ViewModel 获取数据,不直接操作数据库。修正方案:
typescript
// features/home/ui/HomePage.ets
import { TodoViewModel } from '../viewmodel/TodoViewModel';
@Entry
@ComponentV2
struct HomePage {
@Local viewModel: TodoViewModel = new TodoViewModel();
aboutToAppear(): void {
this.viewModel.loadTodos();
}
build() {
List() {
ForEach(this.viewModel.todos, (todo: Todo) => {
ListItem() {
Text(todo.title)
}
})
}
}
}
解读:分层架构的核心价值在于职责分离。页面层只负责 UI 展示和用户交互,业务逻辑由 ViewModel 层处理,数据访问由数据层处理。页面层直接访问数据层会导致职责混淆,难以维护和测试。
习题 6(简答题)
题目:简述大型 HarmonyOS 应用模块拆分的三种维度,以及各自的适用场景。
答案:三种拆分维度包括:按业务域拆分,每个业务域对应一个独立的模块,适用于业务边界清晰、团队按业务线划分的大型应用。按功能层拆分,将应用按技术职责分为 UI 层、业务逻辑层、数据访问层和基础设施层,适用于技术栈统一、需要严格控制分层边界的团队。按团队边界拆分,是康威定律在软件架构中的直接体现,每个团队负责的模块可以独立开发、独立测试、独立发布,适用于多团队协作、需要并行开发的大型项目。实际项目中三种维度通常组合使用:先按业务域拆分为顶层模块,每个业务域内部再按功能层拆分为子模块,最后根据团队边界确定模块的归属和维护责任。
解读:模块拆分的核心目标是高内聚低耦合。拆分不是越细越好,过度拆分会导致模块数量膨胀、接口复杂度上升、维护成本增加。拆分应基于业务边界、技术边界和团队边界,找到合适的平衡点。
习题 7(简答题)
题目:简述 HarmonyOS 应用 CI/CD 流水线的核心环节,以及性能门禁在其中的作用。
答案:CI/CD 流水线的核心环节包括:代码检查(运行 Code Linter 静态检测)、编译构建(执行 hvigorw assembleHap 构建 HAP 包)、单元测试(运行 Hypium 测试)、UI 测试(在模拟器或真机上运行 UI 自动化测试)、性能门禁(运行场景化性能测试,对比性能基线)、签名打包(配置发布签名构建正式 HAP 包)、上传分发(将 HAP 包上传到 AppGallery Connect 或内部测试平台)。性能门禁的作用是防止性能退化,设定性能基线(如冷启动 ≤3000ms、页面切换 ≤1000ms、帧率 ≥60 帧),每次代码合入前自动运行性能测试,低于基线则阻断合入。DevEco Testing 的性能基线管理能力可以自动记录内存占用、CPU 负载等关键指标,通过历史数据对比精准定位性能退化问题,在性能退化早期发出预警。
解读:性能门禁是 CI/CD 流水线中防止性能退化的关键手段。没有性能门禁,代码合入后性能可能悄悄退化,等到用户反馈时已经影响大量用户。将性能测试自动化、常态化,在性能退化早期就发现并修复问题。
八、本节知识点总结
模块拆分策略
三种拆分维度:按业务域拆分(业务边界清晰)、按功能层拆分(技术栈统一)、按团队边界拆分(多团队协作)。实际项目中三种维度组合使用:先按业务域拆顶层,内部再按功能层拆子模块,最后按团队边界确定归属。
依赖治理
优先使用系统能力,评估依赖必要性,定期清理无用依赖。使用 overrides 强制统一版本,开启 resolve_conflict 自动解决冲突。业务模块之间禁止直接依赖,公共模块不能依赖业务模块。
构建优化
模块化编译默认开启增量编译,hvigor 支持并行构建。配置构建缓存和 useNormalizedOHMUrl 优化构建效率。HSP 共享减少重复编译但需评估编译时间成本。
团队协作规范
代码分层约定明确各层职责,页面层不包含业务逻辑,ViewModel 层不操作 UI,数据层不含业务逻辑。命名规范、提交规范(Angular 格式)、Code Review 检查清单保障代码质量。
多环境配置管理
使用 product 机制定义开发、测试、生产环境,通过 BuildProfile 在代码中访问配置。环境配置集中到 AppConfig 统一管理,避免硬编码。
CI/CD 流水线
核心环节:代码检查、编译构建、单元测试、UI 测试、性能门禁、签名打包、上传分发。性能门禁设定性能基线,低于基线则阻断合入,防止性能退化。配置构建缓存和并行执行加速流水线。
下节预告
第11课将进入 ArkUI 应用的安全与合规进阶的学习,涵盖数据加密与安全存储、网络安全传输、权限最小化、隐私合规检测以及应用加固方案。