【ArkUI进阶练中学】第10课:架构设计与工程化最佳实践

本节目标

  • 掌握大型 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 应用的安全与合规进阶的学习,涵盖数据加密与安全存储、网络安全传输、权限最小化、隐私合规检测以及应用加固方案。

相关推荐
在猴站学算法1 小时前
(SQL注入学习)联合查询注入(UNION-Based)
sql·学习·网络安全
欣欣之王来了1 小时前
5G的行业应用:工业互联网、车联网、智慧医疗中的网络支撑
网络·学习·计算机网络·安全·教程
dtq04241 小时前
数据结构 - 树的概念
c语言·数据结构·学习·算法
李游Leo2 小时前
HarmonyOS 7 + Node.js-JSON Schema:审核测试路径与版本事实的一致性门禁【鸿蒙心迹】
node.js·json·harmonyos
inferno2 小时前
SVN学习笔记(一)
笔记·学习·svn
传奇开心果编程2 小时前
【ArkUI提高练中学】第7课:性能调优与稳定性治理
学习·ui·华为·harmonyos
艾莉丝努力练剑2 小时前
【QT】系统相关:多线程QThread基础
开发语言·网络·c++·qt·学习·大模型
sunshine22 girl2 小时前
Java学习五 面向对象高级5 内部类4-匿名内部类(重点)
java·学习
妄汐霜2 小时前
SSE,RocketMQ,MQTT不同之处
笔记·学习·rocketmq