HarmonyOS APP《画伴梦工厂》开发第43篇-多模块架构设计——模块拆分与依赖管理

第6.1篇:多模块架构设计------模块拆分与依赖管理

难度 :⭐⭐⭐ 高级

前置知识 :第 1.1 篇 ArkTS 入门

涉及源文件build-profile.json5(根)、oh-package.json5(根)、products/default/oh-package.json5products/default/module.json5common/oh-package.json5features/adaptiveLayout/oh-package.json5features/responsiveLayout/oh-package.json5common/Index.ets


一、概述

在"画伴梦工厂"项目中,随着功能不断扩展------自适应布局、响应式布局、语音识别、3D 渲染、服务卡片等------单模块架构逐渐暴露出以下问题:

痛点 表现
编译速度慢 任何微小改动都需要重新编译整个工程
耦合严重 工具函数、常量、类型定义散落在各个页面目录下,难以复用
职责边界模糊 布局逻辑与业务逻辑混在一起,修改一个功能可能影响其他功能
不利于团队协作 多人同时修改同一模块极易产生代码冲突

为了解决这些问题,项目采用了多模块(Multi-Module)架构 。多模块架构是鸿蒙工程中一种成熟的代码组织方式,它将代码按职责拆分为多个独立的模块,每个模块拥有自己的 oh-package.json5 依赖声明和 build-profile.json5 注册项,模块之间通过 file: 协议建立显式的依赖关系。

本文将深入分析"画伴梦工厂"的多模块工程结构,从 build-profile.json5 的模块注册、oh-package.json5 的跨模块依赖声明、module.json5 的模块配置等维度,完整阐述鸿蒙多模块架构的设计思路与最佳实践。


二、四模块架构总览

项目共拆分为 4 个模块,职责划分如下:

复制代码
drawing-main/
├── common/                      # 共享基础库(Shared Library)
│   └── oh-package.json5         # name: @ohos/common
├── features/
│   ├── adaptiveLayout/          # 自适应布局特性(Feature Library)
│   │   └── oh-package.json5     # name: @ohos/adaptivelayout
│   └── responsiveLayout/        # 响应式布局特性(Feature Library)
│       └── oh-package.json5     # name: @ohos/responsivelayout
└── products/
    └── default/                 # 主应用入口模块(Entry Module)
        ├── oh-package.json5     # 依赖所有其他模块
        ├── module.json5         # type: entry
        └── src/main/ets/        # 应用代码

2.1 common ------ 共享基础库

common 模块是所有其他模块的公共依赖。它不包含任何业务逻辑,只提供项目范围内的共享基础能力:

能力 文件 说明
Logger common/src/main/ets/utils/Logger.ets 基于 @kit.PerformanceAnalysisKit 的日志封装,提供 debug/info/warn/error 分级输出
BreakpointSystem common/src/main/ets/utils/BreakpointSystem.ets 基于 mediaquery 的断点系统,监测屏幕尺寸变化并同步到 AppStorage
BreakPointType<T> 同上文件的导出类 泛型工具,根据当前断点名称返回对应的配置值

common 模块通过 Index.ets 作为统一出口导出所有公共 API:

typescript 复制代码
// common/Index.ets
export { Logger } from './src/main/ets/utils/Logger';
export { BreakpointSystem, BreakPointType } from './src/main/ets/utils/BreakpointSystem';

2.2 adaptiveLayout ------ 自适应布局特性

adaptiveLayout 模块封装了基于 BreakpointSystem 的自适应布局组件和工具函数。它依赖 common 模块获取断点状态,并对外暴露 adaptiveLayout 组件:

typescript 复制代码
// features/adaptiveLayout/Index.ets
export { adaptiveLayout } from './src/main/ets/pages/AdaptiveLayout';

2.3 responsiveLayout ------ 响应式布局特性

responsiveLayout 模块封装了基于 GridRow/GridCol 的响应式网格布局组件。同样依赖 common 模块,对外暴露 responsiveLayout 组件:

typescript 复制代码
// features/responsiveLayout/Index.ets
export { responsiveLayout } from './src/main/ets/pages/ResponsiveLayout';

2.4 default ------ 主应用入口

default 模块是 type: "entry" 的主应用模块,包含所有页面、Ability、服务卡片、权限声明等。它是唯一可以打包运行的模块,聚合了所有特性模块的能力。


三、模块依赖关系图

四个模块之间的依赖关系形成一个清晰的有向无环图(DAG)

复制代码
                    ┌──────────────────────────────────────┐
                    │          default (entry)              │
                    │  products/default/oh-package.json5    │
                    └──────┬────────────────────┬───────────┘
                           │                    │
                    depends on             depends on
                           │                    │
                           ▼                    ▼
              ┌────────────────────┐  ┌────────────────────────┐
              │  adaptiveLayout    │  │   responsiveLayout     │
              │ (feature library)  │  │  (feature library)     │
              └────────┬───────────┘  └───────────┬────────────┘
                       │                          │
                    depends on                 depends on
                       │                          │
                       ▼                          ▼
              ┌──────────────────────────────────────────┐
              │              common (shared)              │
              │         @ohos/common : 1.0.0              │
              └──────────────────────────────────────────┘

这个依赖图遵循了单向依赖原则:

  • common 不依赖任何模块
  • adaptiveLayoutresponsiveLayout 只依赖 common
  • default 依赖所有其他模块

任何两个模块之间都不存在循环依赖,这是多模块架构能够正常编译的基本前提。


四、根 build-profile.json5 ------ 模块注册中心

build-profile.json5 是鸿蒙工程的模块注册配置文件 ,它声明了项目包含的所有模块及其路径。在"画伴梦工厂"中,根 build-profile.json5 的核心结构如下:

json5 复制代码
{
  "app": {
    "products": [{
      "name": "default",
      "targetSdkVersion": "6.0.0(20)",
      "compatibleSdkVersion": "6.0.0(20)",
      "runtimeOS": "HarmonyOS"
    }],
    "buildModeSet": [{ "name": "debug" }, { "name": "release" }]
  },
  "modules": [
    { "name": "default", "srcPath": "./products/default" },
    { "name": "common", "srcPath": "./common" },
    { "name": "adaptiveLayout", "srcPath": "./features/adaptiveLayout" },
    { "name": "responsiveLayout", "srcPath": "./features/responsiveLayout" }
  ]
}

4.1 modules 数组

每一个 modules 条目定义了一个模块:

字段 说明
name 模块名称,在项目中必须唯一,用于在构建系统中标识模块
srcPath 模块源码根目录的相对路径(相对于项目根目录)

注意:模块的 nameoh-package.json5 中的 name 字段可以不同build-profile.json5 中的 name 是构建系统使用的标识符,而 oh-package.json5 中的 name 是跨模块依赖时引用的包名。

4.2 app.products 与 app.buildModeSet

  • products :定义了产品的构建产物配置。targetSdkVersioncompatibleSdkVersion 分别指定目标 SDK 版本和兼容 SDK 版本。runtimeOS 指定运行的操作系统。
  • buildModeSet :定义可用的构建模式,包括 debug(调试模式,包含调试符号和日志)和 release(发布模式,混淆和优化后发布)。

五、oh-package.json5 跨模块依赖声明

5.1 根 oh-package.json5

根目录的 oh-package.json5 声明了项目全局的依赖:

json5 复制代码
{
  "modelVersion": "6.0.0",
  "description": "Please describe the basic information.",
  "dependencies": {},
  "devDependencies": {
    "@ohos/hypium": "1.0.24",
    "@ohos/hamock": "1.0.0"
  }
}

devDependencies 中的 @ohos/hypium@ohos/hamock 是测试框架依赖,仅在开发环境使用,不会打包到最终产物中。

5.2 entry 模块的依赖声明

products/default/oh-package.json5 声明了主模块对三个子模块的依赖:

json5 复制代码
{
  "name": "default",
  "version": "1.0.0",
  "main": "",
  "dependencies": {
    "@ohos/adaptivelayout": "file:../../features/adaptiveLayout",
    "@ohos/responsivelayout": "file:../../features/responsiveLayout",
    "@ohos/common": "file:../../common"
  }
}

这里的 file: 协议是鸿蒙工程中跨模块依赖的关键语法。"file:../../features/adaptiveLayout" 表示依赖指向 features/adaptiveLayout 目录下的模块,构建系统会根据该目录下的 oh-package.json5 中的 name 字段解析包名。

5.3 common 模块的依赖声明

json5 复制代码
{
  "name": "@ohos/common",
  "version": "1.0.0",
  "main": "Index.ets",
  "dependencies": {}
}

关键点:

  • name@ohos/common,这是其他模块依赖它时使用的包名
  • main 指定为 Index.ets,这是模块的入口文件,其他模块通过 import { ... } from '@ohos/common' 导入时,实际上导入的是 Index.ets 中导出的内容
  • dependencies 为空,common 是依赖链的最底层

5.4 特性模块的依赖声明

adaptiveLayout 为例:

json5 复制代码
{
  "name": "@ohos/adaptivelayout",
  "version": "1.0.0",
  "main": "Index.ets",
  "dependencies": {
    "@ohos/common": "file:../../common"
  }
}

responsiveLayout 的配置与之类似,同样依赖 @ohos/common

5.5 file: 协议详解

file: 协议是 HarmonyOS 提供的本地模块引用机制 ,语法为 "file:<相对路径>"。其核心特性包括:

特性 说明
路径解析 相对路径从当前 oh-package.json5 所在目录开始计算
自动依赖传递 依赖 A 的模块自动获得 A 所依赖的 B 的能力,无需额外声明
编译时链接 构建系统在编译阶段将本地模块的代码链接到最终产物中
版本无关 本地模块的 version 字段不影响依赖解析,始终使用本地源码

六、module.json5 模块配置详解

products/default/src/main/module.json5entry 模块的核心配置文件,它定义了模块的类型、支持的设备、页面路由、Ability 组件以及权限声明:

json5 复制代码
{
  "module": {
    "name": "default",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "DefaultAbility",
    "deviceTypes": ["phone", "tablet", "2in1"],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "requestPermissions": [
      { "name": "ohos.permission.INTERNET" },
      {
        "name": "ohos.permission.CAMERA",
        "reason": "$string:camera_reason",
        "usedScene": { "abilities": ["DefaultAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "$string:microphone_reason",
        "usedScene": { "abilities": ["DefaultAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "reason": "$string:distributed_data_reason",
        "usedScene": { "abilities": ["DefaultAbility"], "when": "inuse" }
      }
    ],
    "abilities": [
      {
        "name": "DefaultAbility",
        "srcEntry": "./ets/defaultability/DefaultAbility.ets",
        "description": "$string:ability_desc",
        "icon": "$media:layered_image",
        "label": "$string:ability_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [{
          "entities": ["entity.system.home"],
          "actions": ["ohos.want.action.home"]
        }]
      }
    ],
    "extensionAbilities": [
      {
        "name": "CreationFormAbility",
        "srcEntry": "./ets/creationformability/CreationFormAbility.ets",
        "type": "form",
        "metadata": [{ "name": "ohos.extension.form", "resource": "$profile:form_config" }]
      },
      {
        "name": "DefaultBackupAbility",
        "srcEntry": "./ets/defaultbackupability/DefaultBackupAbility.ets",
        "type": "backup",
        "exported": false,
        "metadata": [{ "name": "ohos.extension.backup", "resource": "$profile:backup_config" }]
      }
    ]
  }
}

6.1 关键字段解读

字段 含义
name "default" 模块名称,与 build-profile.json5 中的 name 对应
type "entry" 模块类型,entry 表示可独立运行的应用入口模块
deviceTypes ["phone", "tablet", "2in1"] 目标设备类型,支持手机、平板和二合一设备
pages "$profile:main_pages" 页面路由配置,指向 resources/base/profile/main_pages.json
mainElement "DefaultAbility" 应用入口 Ability

6.2 requestPermissions ------ 安全权限声明

项目声明了四种权限:

  1. INTERNET:网络访问权限,用于图片上传和 API 调用
  2. CAMERA :相机权限,用于拍照采集画作,仅在 inuse(前台使用时)申请
  3. MICROPHONE:麦克风权限,用于语音识别功能
  4. DISTRIBUTED_DATASYNC:分布式数据同步权限,用于跨设备数据共享

每个危险权限都附带了 reason 字符串资源,在权限申请弹窗中向用户说明使用目的,这符合鸿蒙的安全最佳实践。

6.3 extensionAbilities ------ 扩展能力

  • CreationFormAbility (type: form):服务卡片 Ability,提供桌面小组件功能
  • DefaultBackupAbility (type: backup):数据备份 Ability,支持应用数据的云备份与恢复

七、模块类型详解:entry / feature / shared

在鸿蒙多模块架构中,模块按职责分为三种类型:

7.1 entry 模块

json5 复制代码
{ "type": "entry" }
  • 用途 :应用的主入口模块,包含 Ability、页面路由、权限声明等应用级配置
  • 特性
    • 只能有一个 entry 模块(在 build-profile.json5products 中定义)
    • 必须包含 module.json5type"entry"
    • 可以引用所有 feature 和 shared 模块
    • 最终生成可安装的 HAP 包

在项目中,products/default 就是 entry 模块,它负责聚合所有特性模块并提供最终的 UI 页面和应用生命周期管理。

7.2 feature 模块

feature 模块没有独立的 module.json5,而是通过 oh-package.json5 声明自身并提供导出。

  • 用途:封装独立的业务特性或功能,如自适应布局、响应式布局
  • 特性
    • 可以有多个 feature 模块
    • 可以依赖 shared 模块和其他 feature 模块
    • 不包含 Ability 或页面路由配置
    • 通过 Index.ets 对外暴露 API

项目中 features/adaptiveLayoutfeatures/responsiveLayout 都是 feature 模块,两者职责清晰、互不依赖。

7.3 shared 模块

  • 用途:提供跨模块共享的基础能力,如工具类、常量、日志等
  • 特性
    • 可以有多个 shared 模块
    • 不应依赖任何 feature 模块(保持最底层位置)
    • 通过 Index.ets 作为模块入口统一导出

项目中的 common 模块是典型的 shared 模块,它不依赖任何其他业务模块,其他所有模块都可以引用它。


八、Index.ets ------ 模块入口约定

在鸿蒙多模块架构中,每个通过 oh-package.json5 导出的模块都需要指定一个入口文件main 字段)。common 模块的入口文件如下:

typescript 复制代码
// common/Index.ets
export { Logger } from './src/main/ets/utils/Logger';
export { BreakpointSystem, BreakPointType } from './src/main/ets/utils/BreakpointSystem';

这种做法形成了清晰的门面模式(Facade Pattern)

  • 对外隐藏内部实现细节 :其他模块只需关心 Index.ets 中导出了什么,无需了解 utils/ 目录的组织结构
  • 统一引用入口import { Logger } from '@ohos/common' 即可获取日志能力
  • 控制可访问性Index.ets 中只导出需要暴露的 API,内部类和常量不对外可见

九、common 模块核心能力剖析

9.1 Logger ------ 统一日志服务

typescript 复制代码
import { hilog } from '@kit.PerformanceAnalysisKit';

export class Logger {
  private readonly prefix: string = 'testTag';
  private readonly domain: number = 0xFF00;
  private readonly format: string = '%{public}s, %{public}s';

  public debug(...args: string[]): void {
    hilog.debug(this.domain, this.prefix, this.format, args);
  }

  public info(...args: string[]): void {
    hilog.info(this.domain, this.prefix, this.format, args);
  }

  public warn(...args: string[]): void {
    hilog.warn(this.domain, this.prefix, this.format, args);
  }

  public error(...args: string[]): void {
    hilog.error(this.domain, this.prefix, this.format, args);
  }
}

Logger 封装了鸿蒙的 hilog 日志接口,提供了 debuginfowarnerror 四个等级的输出方法。所有模块统一使用这个 Logger,避免了日志输出格式不一致的问题。

9.2 BreakpointSystem ------ 媒体查询断点系统

BreakpointSystem 是自适应布局的核心基础设施。它通过 mediaquery.MediaQueryListener 监听屏幕宽度变化,并在断点切换时更新 AppStorage 中的全局状态:

typescript 复制代码
export class BreakpointSystem {
  private readonly breakpoints: Breakpoint[] = [
    { name: 'sm', size: 320 },
    { name: 'md', size: 600 },
    { name: 'lg', size: 840 },
    { name: 'xl', size: 1080 }
  ];

  public register(uiContext: UIContext): void {
    // 为每个断点范围注册媒体查询监听器
    this.breakpoints.forEach((breakpoint, index) => {
      let condition = '';
      if (index === this.breakpoints.length - 1) {
        condition = `(${breakpoint.size}vp<=width)`;
      } else {
        condition = `(${breakpoint.size}vp<=width<${this.breakpoints[index + 1].size}vp)`;
      }
      // ... 注册监听
    });
  }
}

common 模块还将 BreakPointType<T> 泛型工具类一并导出,使得特性模块可以根据当前断点名称获取对应的配置值(如间距、字号等),实现真正的响应式布局。

9.3 为什么不把这些放在 entry 模块?

将 Logger 和 BreakpointSystem 放在 common 模块而非 default 模块,是基于以下架构考量:

  1. 避免循环依赖 :如果 Logger 放在 default 模块中,那么 adaptiveLayout 要使用 Logger 就必须依赖 default,而 default 又依赖 adaptiveLayout,形成循环依赖,违反 DAG 原则。

  2. 模块复用性common 模块可以独立复用。如果将来项目需要增加一个 wearable 产品线,只需在 build-profile.json5 中注册新的 entry 模块,并复用已有的 commonfeatures/* 模块。

  3. 编译效率common 模块的内容不常变更,编译一次后可以被缓存,后续重新编译其他模块时无需重新编译 common,显著提升增量编译速度。


十、架构决策原理与最佳实践

10.1 为什么选择多模块架构?

复制代码
单模块架构(不采用)         多模块架构(采用)
┌─────────────────────┐    ┌─────────────────────┐
│ 所有代码混在一起      │    │  clear: entry        │
│ 大到难以维护          │    │  │                   │
│ 编译需要 5 分钟       │    │  ├─ feature: 布局     │
│ 改一行全局重编        │    │  ├─ feature: 语音     │
│ 新人上手困难          │    │  └─ shared: common    │
└─────────────────────┘    │ 编译只需 1-2 分钟     │
                           │ 模块职责清晰           │
                           │ 可独立开发和测试       │
                           └─────────────────────┘

10.2 模块拆分原则

在实际项目中,建议遵循以下原则进行模块拆分:

原则 说明 项目中的体现
单一职责 每个模块只做一件事 common 只做共享基础库,adaptiveLayout 只做自适应布局
无环依赖 依赖图必须为 DAG common ← adaptiveLayout ← default
接口隔离 通过 Index.ets 控制导出范围 只导出需要公开的 API
稳定方向依赖 更稳定的模块被更低层依赖 common 最稳定,位于底层
共同复用 一起变化的类放在同一模块 布局相关组件集中在各自 feature 模块

10.3 常见的架构陷阱

陷阱一:过度拆分

模块的数量不是越多越好。通常 3~8 个模块对于中型项目是比较合理的区间。如果模块粒度过细(每个工具类一个模块),反而会增加依赖管理的复杂度。

陷阱二:循环依赖

鸿蒙构建系统不支持循环依赖。如果 A 依赖 B、B 依赖 C、C 又依赖 A,编译会直接报错。解决方法是:将循环双方的共同依赖抽离到一个新的 shared 模块中。

陷阱三:跨模块直接引用内部文件

typescript 复制代码
// ❌ 错误做法------跨模块引用内部路径
import { Logger } from '../../common/src/main/ets/utils/Logger';
typescript 复制代码
// ✅ 正确做法------通过模块名引用
import { Logger } from '@ohos/common';

直接引用内部路径会绕过模块的 Index.ets 入口,导致模块的封装性被破坏。如果以后重构内部文件结构,所有直接引用的地方都需要修改。

10.4 构建性能优化

多模块架构带来的一个核心收益是增量编译 。在鸿蒙 DevEco Studio 中,当修改 features/adaptiveLayout 的代码时:

  1. 构建系统检测到 adaptiveLayout 的源文件发生变更
  2. 仅重新编译 adaptiveLayout 模块及其直接依赖者(如 default
  3. commonresponsiveLayout 模块无需重新编译

对于大型项目,这种增量编译可以将构建时间从数分钟降低到数十秒。


总结

本文通过"画伴梦工厂"的实际项目代码,完整分析了鸿蒙多模块架构的设计与实现:

知识点 对应文件/配置 核心要点
模块注册 build-profile.json5 modules 数组注册所有模块,products 定义构建产物
依赖声明 oh-package.json5 file: 协议声明本地模块依赖,name/main 定义模块入口
模块配置 module.json5 type: entry 定义入口模块,deviceTypes 指定支持设备
模块类型 三者搭配使用 entry(主应用)、feature(特性库)、shared(共享库)
模块入口 Index.ets 门面模式统一导出,隐藏内部实现
共享能力 common 模块 提供 Logger、BreakpointSystem 等跨模块基础设施
依赖图 整体结构 DAG 单向依赖,无循环引用
最佳实践 架构原则 单一职责、接口隔离、稳定方向依赖

多模块架构是鸿蒙工程从"能跑就行"迈向"可维护、可扩展"的关键一步。它为项目的持续迭代奠定了坚实的工程基础,也是团队协作开发的必备架构模式。

下一篇: 第 6.2 篇将探讨鸿蒙工程的构建配置进阶------多产物构建、签名管理与 CI/CD 流水线集成,敬请期待。

相关推荐
HarmonyOS_SDK2 小时前
一次开发,多端高效协同:解码HarmonyOS SDK窗口的响应式设计哲学
harmonyos
心中有国也有家4 小时前
AtomGit Flutter 鸿蒙客户端:E-Brufen 架构设计
学习·flutter·华为·harmonyos
hqzing5 小时前
鸿蒙 PC 底层开发技术详解(七):二进制自签名算法的实现
算法·华为·harmonyos
心中有国也有家6 小时前
鸿蒙 Flutter 本地存储实战:Hive CE 从入门到精讲
人工智能·hive·flutter·华为·harmonyos
绝世番茄6 小时前
鸿蒙原生ArkTS布局方式之Grid+scrollBar滚动条布局深度解析
华为·harmonyos·鸿蒙
绝世番茄7 小时前
鸿蒙原生ArkTS布局方式之Grid固定列数网格深度解析(HarmonyOS NEXT API 24)
华为·harmonyos·鸿蒙
爱吃大芒果8 小时前
AI 智能体工作流设计蓝图:将非结构化情绪记录转化为高精度的模型 Prompt 上下文
人工智能·华为·prompt·harmonyos
●VON8 小时前
HarmonyKit | HarmonyOS Kit 模块化体系与零依赖架构实践
ui·华为·架构·harmonyos·鸿蒙·新特性
在书中成长8 小时前
HarmonyOS 小游戏《对战五子棋》开发第9篇 - GomokuEngine核心引擎设计(四):悔棋功能与历史记录管理
华为·harmonyos