模块化设计
概述
HCompass 的核心设计理念是模块化,以"功能包"为核心单元,通过组合不同功能包快速构建应用。这种设计使得代码更加清晰、可维护,并且功能包可以跨项目复用。
功能包(Package)
什么是功能包
功能包是 HCompass 中的核心概念,它是一个独立的业务模块,包含完整的业务逻辑、页面、服务和数据模型。
功能包的特点
- 独立性:每个功能包都是独立的,可以单独开发、测试和部署
- 可复用:功能包可以在不同项目间复用
- 解耦:功能包之间通过契约解耦,不直接依赖
- 完整性:功能包包含完整的业务逻辑,从 UI 到数据层
功能包结构
packages/
└── user/ # 用户功能包
├── navigation/ # 导航构建器
├── services/ # 服务
│ └── UserServiceImpl.ets
├── view/ # 页面
│ ├── UserProfilePage.ets
│ └── UserSettingsPage.ets
├── viewmodels/ # ViewModel
│ ├── UserProfileViewModel.ets
│ └── UserSettingsViewModel.ets
├── models/ # 数据模型(可选)
│ └── UserModel.ets
├── components/ # 组件(可选)
│ └── UserAvatar.ets
└── UserModule.ets # 功能包生命周期依赖注入(DI)
为什么需要 DI
依赖注入是实现模块解耦的关键技术。通过 DI,功能包可以:
- 不直接依赖其他功能包的实现
- 通过契约(接口)获取服务
- 方便进行单元测试(可以注入 Mock 对象)
DI 的使用
1. 定义契约
在 Shared 层定义服务契约:
typescript
// shared/contracts/IUserService.ets
export interface IUserService {
getUserInfo(): Promise<UserInfo>;
updateUserInfo(info: UserInfo): Promise<void>;
}2. 实现服务
在功能包中实现服务:
typescript
// packages/user/services/UserServiceImpl.ets
import { IUserService } from "@shared/contracts";
export class UserServiceImpl implements IUserService {
async getUserInfo(): Promise<UserInfo> {
// 实现逻辑
}
async updateUserInfo(info: UserInfo): Promise<void> {
// 实现逻辑
}
}3. 注册服务
在功能包的生命周期中注册服务:
typescript
// packages/user/UserModule.ets
import { UserServiceImpl } from "./services/UserService";
import { IUserService } from "@shared/contracts";
/**
* 用户模块
* 实现 FeatureModule 接口,支持自动注册
*/
export class UserModule implements FeatureModule {
/**
* 注册 DI 服务
* @param container DI 容器
*/
registerServices(container: Container): void {
// 注册本模块服务
container.register<IUserService>("IUserService", () => new UserServiceImpl());
}
}4. 使用服务
在其他功能包中通过 DI 获取服务:
typescript
// packages/main/viewmodels/MainViewModel.ets
import { getContainer } from "@core/di";
import { IUserService } from "@shared/contracts";
export class MainViewModel {
private userService: IUserService;
constructor() {
this.userService = getContainer.resolve<IUserService>("IUserService");
}
async loadUserInfo(): Promise<void> {
const userInfo = await this.userService.getUserInfo();
// 使用用户信息
}
}契约层(Shared)
契约的作用
契约层定义了功能包之间的通信协议,它:
- 定义服务接口
- 定义数据结构
- 定义共享状态
契约的设计原则
- 接口隔离:每个契约只定义必要的方法
- 稳定性:契约一旦定义,应保持稳定
- 类型安全:充分利用 TypeScript 的类型系统
契约示例
typescript
// shared/contracts/IAuthService.ets
export interface IAuthService {
login(username: string, password: string): Promise<LoginResult>;
logout(): Promise<void>;
isLoggedIn(): boolean;
}
// shared/types/AuthTypes.ets
export interface LoginResult {
success: boolean;
token?: string;
message?: string;
}功能包的生命周期
1. 创建功能包
在 packages 目录下创建新功能包。
2. 定义契约
在 Shared 层定义功能包需要对外提供的服务契约。
3. 实现功能
在功能包内实现业务逻辑、页面、服务等。
4. 注册功能包
在 Entry 层注册功能包。
5. 使用功能包
其他功能包通过 DI 注入使用该功能包提供的服务。
功能包的复用
跨项目复用
功能包可以直接复制到其他项目使用:
- 复制功能包目录到新项目的
packages/目录 - 复制相关的契约定义到新项目的
shared/目录 - 在新项目中注册功能包
- 开始使用
模块化的优势
1. 代码组织清晰
每个功能包都有明确的职责和边界,代码组织清晰。
2. 易于维护
功能包独立,修改一个功能包不会影响其他功能包。
3. 提高复用性
功能包可以在不同项目间复用,减少重复劳动。
4. 并行开发
不同团队可以并行开发不同的功能包,提高开发效率。
5. 易于测试
功能包独立,可以单独进行单元测试和集成测试。
最佳实践
1. 保持功能包的独立性
- 功能包不应直接依赖其他功能包的实现
- 通过契约和 DI 注入实现功能包之间的通信
2. 合理划分功能包
- 按业务领域划分功能包
- 每个功能包职责单一,不要过大或过小
3. 定义清晰的契约
- 契约应该简洁明了
- 契约应该稳定,不频繁变动
4. 使用 TypeScript 类型系统
- 充分利用 TypeScript 的类型系统
- 确保类型安全
5. 编写文档
- 为每个功能包编写文档
- 说明功能包的用途、API 和使用方法
新增模块流程
以下示例以创建 goods 模块为例,演示在 DevEco Studio 中新增模块的操作流程。
新增 feature 模块
- 在
feature/目录上点击右键,选择 New -> Module...。

- 在模板选择界面选择 Static Library,点击 Next。

- 填写模块名称(如
goods)与设备类型,点击 Finish 完成创建。
