diff --git a/docs/architecture.md b/docs/architecture.md index 992d018..a1551a9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,58 +1,209 @@ -# Architecture(架构) +# HarmonyOS NEXT 客户端 · 架构设计文档 -HarmonyOS NEXT 单 HAP 应用,API 22(HarmonyOS 6.0)。技术栈:ArkTS + ArkUI 声明式 UI + hvigor。 -Single-HAP HarmonyOS NEXT app, API 22 (HarmonyOS 6.0). Stack: ArkTS + ArkUI declarative UI + hvigor. +> 项目:`harmony-client`(ArkTS + ArkUI,SDK 6.0.2 / API 22,仅 phone) +> 业务形态:对接自有后端的通用业务客户端(HTTP + 鉴权 + CRUD) -## 模块结构 Module Structure +--- -- **AppScope/** — 应用级信息:`app.json5` 里定义 `bundleName`(`com.mingyuan.harmony_client`)、应用图标/标签(`$media:layered_image` / `$string:app_name`)。 -- **entry/** — 唯一主模块(HAP)。`module.json5` 声明 Ability、权限、入口。 +## 1. 背景与目标 -## Ability 生命周期 Ability Lifecycle +本工程从 DevEco 空脚手架起步,目标是建立一套**可持续演进、可测试、低耦合**的客户端架构骨架,为后续业务开发打底。设计依据来自一轮 deep-research 调研(交叉验证华为官方文档与社区实践,9 条结论全部通过对抗式验证)。 -**EntryAbility**(`entry/src/main/ets/entryability/EntryAbility.ets`)是应用入口,继承 `UIAbility`。关键回调: +核心决策: -| 回调 Callback | 用途 Purpose | -|--------------|-------------| -| `onCreate` | Ability 创建,初始化状态(**此时不要做 UI 操作**) | -| `onWindowStageCreate` | 窗口创建,在这里 `loadContent('pages/Index')` 加载首页 | -| `onForeground` / `onBackground` | 前后台切换 | -| `onDestroy` | 销毁,释放资源 | +| 决策点 | 选择 | 理由 | +|---|---|---| +| 工程结构 | 官方三层 commons / features / products | 单向依赖、官方白皮书推荐 | +| 模块化 | **单 HAP + 多 HAR** | HAR 编译期打包、可裁剪;HSP 仅按需加载 | +| 代码分层 | **MVVM + 独立 data 层** | 官方指南 + 网络/数据解耦 | +| 状态管理 | **全量 StateManagement V2** | API 22 下 V2 能力完整,字段级精准刷新 | -**EntryBackupAbility** — 应用备份扩展 Ability(备份/恢复数据)。 +--- -## 页面与路由 Pages & Routing +## 2. 模块划分与依赖 -- 页面组件用 `@Entry` 装饰,在 `entry/src/main/resources/base/profile/main_pages.json` 注册。 -- 页面跳转:`router.pushUrl()`(栈式导航),或 `Navigation` 组件(推荐,用于复杂导航)。 +``` +harmony-client/ +├── entry/ HAP 应用壳 + 业务功能(唯一入口,EntryAbility) +├── common/ HAR 基础设施层(网络 / 日志 / 状态 / 工具,无业务、无 UI 组件) +└── uikit/ HAR 通用 UI 组件库 + 主题(无业务、无网络依赖) +``` -## 目录分层 Layering Convention +依赖方向严格单向,禁止反向/循环: + +``` +entry ──依赖──▶ common +entry ──依赖──▶ uikit +common / uikit 各自独立,互不依赖 +``` + +- **common**:`HttpClient`、鉴权拦截器、统一错误模型、`Logger`、`SessionStore`(PersistenceV2 持久化)、工具函数。 +- **uikit**:主题 token(`AppColor/AppSpacing/AppFontSize`)、通用组件(`AppButton/LoadingView/EmptyView`)。 +- **entry**:应用壳装配、路由、DI 容器、MVVM 分层业务代码。 + +--- + +## 3. 分层与目录结构(entry 内) + +采用官方 MVVM 指南的**层优先**结构,叠加独立 `data/` 层与横切的 `router/`、`di/`: ``` entry/src/main/ets/ -├── entryability/ # Ability 入口(生命周期) -├── entrybackupability/ # 备份扩展 -├── pages/ # 页面(@Entry 组件,只做 UI 编排) -├── components/ # 可复用 UI 组件(@Component / @Reusable) -├── viewmodel/ # 状态管理(连接 model 与 UI) -├── service/ # 业务逻辑、数据访问(网络/存储) -├── model/ # 数据模型、类型定义 -└── common/ # 公共常量、工具函数 +├── entryability/ +│ └── EntryAbility.ets # 生命周期装配:Logger / ServiceLocator / SessionStore +├── router/ +│ ├── RouteMap.ets # 路由名常量 + name→页面 分发 +│ └── AppRouter.ets # Navigation 导航封装(push/replace/pop/...) +├── di/ +│ └── ServiceLocator.ets # 轻量服务定位器(ArkTS 无反射,手动注册/解析) +├── model/ # 领域模型:@ObservedV2 + @Trace 实体 +├── data/ +│ ├── api/ # DTO + 请求服务(调 common 的 HttpClient) +│ └── repository/ # 数据仓库契约(抽象类)+ 实现 +├── viewmodel/ # @ObservedV2 状态容器(data/loading/error 三态) +├── view/ # 可复用 UI 组件(@ComponentV2) +└── pages/ # 页面,仅编排(组合 view + viewmodel) ``` -依赖方向(只能从右往左引,禁止反向): -Dependency direction (only right→left, never reverse): -`pages → viewmodel → service → model`;`components` 不依赖业务层。 +**单向数据流**: -## 状态管理 State Management +``` +pages → viewmodel → repository(契约) → api → HttpClient → 后端 + ↑ 状态回传由 @Trace 触发自动刷新 +``` -- 组件内:`@State`(本地)、`@Prop`(父传子单向)、`@Link`(父子双向)。 -- 跨组件:`@Provide` / `@Consume`,或 `AppStorage`(全局)/ `LocalStorage`(页面级)。 -- 持久化:`PersistentStorage`(简单键值)。 -- 原则:**UI 由状态驱动**,不在组件外手动改 UI。 +--- -## 关键约定速查 Key Contracts +## 4. 状态管理约定(全量 V2,禁用 V1) -- 所有字符串/颜色/媒体走 `$r()` 资源引用。 -- 页面只做 UI 编排;数据获取/业务判断在 `service/`,类型在 `model/`。 -- 新增页面必须同步注册到 `main_pages.json`。 +| 场景 | 使用 | +|---|---| +| 组件/页面 | `@ComponentV2` | +| 组件内部状态 | `@Local` | +| 父→子输入 | `@Param` | +| 子→父输出/回调 | `@Event`(**不用 `@Link`**;需双向时用 `@Param` 的 `!!` 语法糖) | +| 类/模型可观察 | `@ObservedV2` 类 + `@Trace` 字段 | +| 跨层共享 | `@Provider` / `@Consumer` | +| 变化监听 | `@Monitor` | +| 全局会话 | `SessionStore`(`@ObservedV2` 单例,见 `common/state/SessionStore.ets`) | +| 轻量持久化 | `PersistenceV2.globalConnect(...)`;结构化数据后续用 relationalStore(RDB) | + +**核心约定**:ViewModel 是 `@ObservedV2` 类,持有 `@Trace` 的 `data/loading/error` 三态;页面只读状态、调方法,不直接碰 Repository / 网络层。 + +--- + +## 5. 网络与数据层 + +### 5.1 网络层(`common/src/main/ets/network/`) + +- `HttpClient`:封装 `@kit.NetworkKit` 的 http,统一超时/请求头,支持**拦截器链**。 +- `HttpInterceptor`:拦截器接口(`onRequest` / `onResponse`)。 +- `AuthInterceptor`:请求前注入 `Authorization: Bearer `;401 时清空会话。 + +### 5.2 鉴权流程 + +``` +请求 → AuthInterceptor.onRequest 注入 token → 发出 + → AuthInterceptor.onResponse 检查 401 + → (当前) 清空 SessionStore,由上层跳登录 + → (TODO) token 刷新 + 原请求重放(防并发刷新),需后端 refresh 接口约定后实现 +``` + +### 5.3 Repository 模式 + +- `data/repository/` 中定义**抽象类契约**,ViewModel 只依赖契约,实现可替换(真实 / mock)。 +- 用抽象类而非 interface,是为了在 `ServiceLocator` 中做 `Object → 契约` 强转时兼容 ArkTS。 +- `data/api/` 返回 DTO,Repository 负责 **DTO → 领域模型** 映射。 + +### 5.4 统一响应与错误 + +- `ApiResult`:`{ code, message, data }`,`code === 0` 为成功。 +- `ApiError`:区分网络异常 / HTTP 状态码 / 业务错误码。 + +--- + +## 6. 横切关注点 + +### 6.1 依赖注入 + +`di/ServiceLocator.ets`:`Map` 单例注册表。启动时在 `EntryAbility.onCreate` 注册 `HttpClient`、各 Repository、`SessionStore`;业务侧 `get(key)` 后强转。 + +> ArkTS 无反射,用字符串 key + 显式强转;若后续规模增大可引入类型安全的 key 常量或轻量注入容器。 + +### 6.2 导航 + +`Navigation` + `NavPathStack`(官方推荐,替代旧 Router)。`RouteMap.ets` 定义路由名常量与 `name→页面` 分发;`AppRouter` 持有全局 `NavPathStack` 提供 `push/replace/pop/popToName/clear`。HAR 不承载路由。 + +### 6.3 日志 + +`common/logging/Logger.ets`:封装 `hilog`,统一 DOMAIN,后续可加等级开关/上报。 + +### 6.4 持久化 + +- `PersistenceV2`:轻量 KV(token / 用户偏好),`@Trace` 字段自动落盘。 +- `relationalStore(RDB)`:结构化数据(订单、列表缓存等),后续引入。 + +--- + +## 7. 编码规范 + +1. **状态管理只用 V2**:新组件一律 `@ComponentV2`,禁止混入 V1 装饰器。 +2. **单向数据流**:父→子用 `@Param`,子→父用 `@Event`;页面不直接改 ViewModel 之外的全局状态。 +3. **页面只编排**:`pages/` 下只做组件组合与 ViewModel 调用,业务逻辑进 ViewModel,数据进 Repository/Api。 +4. **跨模块引用走 `Index.ets`**:entry 只 `import { ... } from 'common'/'uikit'`,不直接引 HAR 内部路径。 +5. **魔法值收敛到 `uikit/theme/Theme.ets`**:颜色/间距/字号统一取 token。 + +--- + +## 8. 演进指南 + +### 8.1 抽取 feature HAR(业务领域明确后) + +当某个业务领域(如订单、支付)足够独立时,新增 `feature_xxx` HAR: + +``` +feature_order/ +├── src/main/ets/{model,data,viewmodel,view,pages} +└── oh-package.json5 (name: feature_order, main: Index.ets) +``` + +- 根 `build-profile.json5` 的 `modules` 增加该模块。 +- 依赖它的模块在其 `oh-package.json5` 增加 `"feature_order": "file:../feature_order"`。 +- 依赖方向:`entry → feature_xxx → common/uikit`,禁止 feature 之间横向依赖(确实需共享时下沉到 `common`)。 + +### 8.2 迁移到 feature-first + +当 feature 数量增多、单层目录过深时,可将 `model/data/viewmodel/view/pages` 按 feature 重组: + +``` +ets/features//{model,data,viewmodel,view,pages} +ets/shared/… # 跨 feature 共享 +``` + +迁移原则:先把稳定边界抽 HAR,再在 HAR 内部做 feature 分组。 + +--- + +## 9. 调研结论摘要与引用 + +- 官方 MVVM(StateManagement V2)指南给出 `model/viewmodel/view/pages` 分层。 +- 官方三层工程结构:commons(禁止反向依赖)/ features / products(不可横向调用),打包形态 HAR/HSP/Feature HAP/Entry HAP。 +- V2 装饰器映射:`@State→@Local`、`@Prop→@Param`、`@Link→@Param+@Event`、`@Observed→@ObservedV2+@Trace`、`@Watch→@Monitor`、`AppStorage→AppStorageV2`、`PersistentStorage→PersistenceV2`。 +- V1/V2 不可混用(编译错误);API 19+ 提供 `UIUtils.enableV2Compatibility/makeV1Observed` 放宽混合约束。 + +主要引用: + +- https://developer.huawei.com/consumer/en/doc/harmonyos-guides/arkts-mvvm-v2 +- https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/arkts-decorator-overview +- https://device.harmonyos.com/en/docs/apiref/harmonyos-guides/arkts-v1-v2-update-difference +- https://github.com/openharmony/docs(arkts-v1-v2-migration-application / mixusage / inner-component) +- https://github.com/OpeNopEn2007/arkts-patterns + +--- + +## 10. 边界与后续 + +- 本次为**骨架级**交付,未实现真实后端对接与具体业务(保留示例链路)。 +- 未新增 feature HAR(无业务领域),仅写入抽取指南。 +- 未做多设备适配(已确认仅 phone)。 +- 后续待办:token 刷新策略、RDB 持久化、自动化测试基建、环境配置注入(BASE_URL)。