鸿蒙大型项目高级架构:MVVM/MVI分层设计/DI容器依赖注入/模块化治理方案
一、前置思考随着应用从Demo级演进到企业级代码量膨胀、模块增多、职责混乱——这些问题在鸿蒙项目中同样适用且更加棘手因为ArkTS的类型系统限制了某些传统设计模式的直接应用无反射、无装饰器元数据、无动态代理。如何在ArkTS的约束下设计出可维护、可测试、可扩展的大型项目架构本文将分模块化治理、DI容器、MVVM分层、Repository模式四个维度展开。企业级架构要解决的核心问题改动隔离修改A功能B功能不受影响高内聚低耦合可测试性业务逻辑可以脱离UI在单元测试中验证团队协作多人并行开发不同模块不产生代码冲突演进能力新增功能、替换技术栈时成本可控二、文件结构治理2.1 按特性分层 vs 按类型分层按类型分层初创项目常用不适合大型项目entry/src/main/ets/ ├── models/ → 散落各处的数据类找不到归属 ├── pages/ → 页面文件50个时找文件像大海捞针 ├── components/ → 通用组件和业务组件混在一起 └── utils/ → 各种工具函数堆砌按特性分层推荐entry/src/main/ets/ ├── model/ # 数据模型Entity/Interface/Enum ├── viewmodel/ # 视图模型ObservedV2驱动UI ├── repository/ # 数据仓储本地/远程数据源抽象 ├── service/ # 业务服务层单例/工厂 ├── component/ # 通用UI组件与业务无关 │ ├── SmartCard/ │ ├── GradientButton/ │ └── ProgressRing/ ├── pages/ # 页面Entry薄薄一层 ├── di/ # 依赖注入容器 ├── theme/ # 主题管理 ├── router/ # 路由管理 └── utils/ # 纯工具函数 ├── LoggerUtil.ets ├── ToastUtil.ets └── DateUtil.ets两种分层的关键区别按类型分层文件找得快所有model放一起但改动要跨多个目录按特性分层单特性改动在一个目录内完成新增/删除特性也干净2.2 模块间依赖规则┌─────────────────────────────────────┐ │ PagesEntry薄薄一层组合 │ │ 依赖 → viewmodel component │ ├─────────────────────────────────────┤ │ ViewModelObservedV2UI状态逻辑 │ │ 依赖 → repository service │ ├─────────────────────────────────────┤ │ Repository数据抽象单一真相源 │ │ 依赖 → model │ ├─────────────────────────────────────┤ │ Service业务逻辑可跨Repository │ │ 依赖 → model repository │ ├─────────────────────────────────────┤ │ Model纯数据无依赖 │ └─────────────────────────────────────┘依赖铁律Model不依赖任何层最底层Repository不依赖ViewModel/PagePage只依赖ViewModel不直接调Repository所有跨层通信通过接口不通过具体实现三、依赖注入DI完整实现3.1 轻量DI容器设计ArkTS中无法使用reflect-metadata装饰器体系但可以通过工厂注册表实现轻量DIclassDIContainer{privatestaticregistry:Mapstring,ObjectnewMap();// 注册实例通常为单例staticregisterT(key:string,instance:T):void{DIContainer.registry.set(key,instanceasObject);}// 获取实例staticgetT(key:string):T|undefined{returnDIContainer.registry.get(key)asT;}// 检查是否已注册statichas(key:string):boolean{returnDIContainer.registry.has(key);}// 获取所有已注册的key用于调试staticgetAllKeys():string[]{constkeys:string[][];DIContainer.registry.forEach((_:Object,k:string){keys.push(k);});returnkeys;}// 移除注册staticremove(key:string):void{DIContainer.registry.delete(key);}}3.2 DI容器使用流程// 应用启动时app.ets 的 onCreate// 注册数据层constnoteRepo:NoteRepositorynewNoteRepository();DIContainer.register(NoteRepository,noteRepo);// 注册服务层constuserService:UserServicenewUserService();DIContainer.register(UserService,userService);// 页面/ViewModel 中使用 classNoteViewModel{privaterepo:NoteRepository;constructor(){// 从DI容器获取依赖consttemp:NoteRepository|undefinedDIContainer.getNoteRepository(NoteRepository);this.repotempasNoteRepository;}getNotes():Note[]{returnthis.repo.getAll();}}3.3 DI容器 vs 单例模式维度单例模式直接newDI容器依赖关系硬编码在代码中运行时配置单元测试难Mock单例本身就是具体类易Mock注册时注入Mock实例生命周期管理只有全局单例可控单例/工厂/作用域依赖发现散落各处不可见集中在注册点实际收益假设你有一个NoteRepository单元测试时需要Mock掉数据库调用。用单例你只能修改源码用DI你只需// 测试环境DIContainer.register(NoteRepository,newMockNoteRepository());// 生产环境DIContainer.register(NoteRepository,newNoteRepository());同一个ViewModel代码一行不改。3.4 DI最佳实践集中注册所有register调用集中在一个di/DISetup.ets文件中启动时调用命名规范key用类名‘NoteRepository’不要用魔法字符串‘nr’null检查get()返回undefined时必须处理不能假设一定有值注册时机在Ability.onCreate或首个aboutToAppear中注册保证使用时已就绪四、MVVM分层深度4.1 三层职责View (Page/ComponentV2) ┃ 职责UI渲染 事件分发 ┃ 不包含业务逻辑、数据获取 ┃ 依赖ViewModel通过 Param/Local/ObjectLink ↓ 用户操作 ViewModel (ObservedV2 类) ┃ 职责UI状态管理 业务逻辑调度 ┃ 不包含数据持久化、网络请求 ┃ 依赖Repository Service ↓ 数据请求 Repository ┃ 职责数据获取/存储的抽象 ┃ 不包含UI逻辑、业务校验 ┃ 依赖DataSource本地DB/云端API4.2 Model 数据层数据模型应该是纯数据结构不包含任何业务逻辑// 基础实体接口interfaceEntity{id:number;}// 业务实体interfaceNoteextendsEntity{title:string;content:string;createdAt:string;}// 应用配置独立模型interfaceAppConfig{appName:string;version:string;}模型设计原则用interface而非class定义数据结构ArkTS中interface更轻量继承基础接口Entity确保ID一致性不包含方法纯数据结构方法放在Repository/Service中4.3 Repository 数据仓储Repository是数据源的单一真相源classNoteRepository{privatedata:Mapnumber,NotenewMap();privatenextId:number1001;constructor(){// 预填充初始数据生产环境应从DB/API加载constinitNotes:Note[][{id:1001,title:架构设计文档,content:MVVM DI Repository 三层架构方案,createdAt:2026-07-20},{id:1002,title:API接口规范,content:RESTful API设计规范 v2.0,createdAt:2026-07-18}];for(leti:number0;iinitNotes.length;i){constnote:NoteinitNotes[i];this.data.set(note.id,note);}}getAll():Note[]{constresult:Note[][];this.data.forEach((note:Note){result.push(note);});returnresult;}getById(id:number):Note|undefined{returnthis.data.get(id);}add(title:string,content:string):Note{constnote:Note{id:this.nextId,title:title,content:content,createdAt:this.getCurrentDate()};this.data.set(this.nextId,note);this.nextId;returnnote;}delete(id:number):boolean{returnthis.data.delete(id);}privategetCurrentDate():string{constnow:DatenewDate();constyear:numbernow.getFullYear();constmonth:stringString(now.getMonth()1).padStart(2,0);constday:stringString(now.getDate()).padStart(2,0);returnyear-month-day;}}Repository设计要点对外暴露语义化方法getAll、getById、add、delete隐藏存储细节内部可以切换存储实现当前用Map未来可以切换到关系型数据库对外接口不变返回的是数据副本或直接引用取决于场景4.4 ViewModel 状态管理ObservedV2classNoteListViewModel{Tracenotes:Note[][];Traceloading:booleanfalse;Traceerror:string;privaterepo:NoteRepository;constructor(){consttemp:NoteRepository|undefinedDIContainer.getNoteRepository(NoteRepository);this.repotempasNoteRepository;this.loadNotes();}loadNotes():void{this.loadingtrue;this.error;// 生产环境这里可能是异步API调用this.notesthis.repo.getAll();this.loadingfalse;}deleteNote(id:number):void{this.repo.delete(id);this.notesthis.repo.getAll();// 刷新列表}}ViewModel设计要点用ObservedV2Trace让属性变更自动通知UI刷新通过DI容器获取Repository依赖而非直接new暴露加载/错误状态loading、error让UI层做兜底展示4.5 View 层View层Page/ComponentV2的代码应该极薄EntryComponentV2struct ArchitectureDemo{LocalviewModel:NoteListViewModelnewNoteListViewModel();LocalselectedNoteId:number-1;build(){Column(){// 加载指示器if(this.viewModel.loading){LoadingProgress().width(40).height(40)}// 错误提示if(this.viewModel.error!){Text(this.viewModel.error).fontColor(#FF5252).fontSize(14)}// 列表List(){ForEach(this.viewModel.notes,(note:Note){ListItem(){Text(note.title).fontSize(16).fontColor(#FFFFFF)}.onClick((){this.selectedNoteIdnote.id;})})}}}}View层铁律❌ 不包含数据库操作❌ 不包含网络请求❌ 不包含复杂业务逻辑✅ 只做三件事渲染UI、分发事件、订阅ViewModel状态五、错误处理架构5.1 统一错误处理策略用户操作 → ViewModel.foo() → try { Repository.bar() } catch (e) { → 记录日志LoggerUtil.error → 更新ViewModel.error状态 → toast提示用户 → 可选上报错误到监控平台 }5.2 错误分类错误类型处理策略用户提示网络错误自动重试3次 → 提示“网络连接失败请检查网络”数据校验失败阻断操作 → 高亮错误字段“标题不能为空”权限不足引导授权“需要相机权限请前往设置”未知异常记录堆栈 → 上报“出了点问题请稍后重试”六、代码规范与工具链6.1 命名规范文件命名PascalCase → UserRepository.ets、SmartCard.ets 接口/类PascalCase → NoteRepository、AppConfig 变量/方法camelCase → noteTitle、getAllNotes() 常量UPPER_SNAKE → MAX_RETRY_COUNT Entry页面以Demo/Page结尾 → ThemeArchitectureDemo6.2 文件内声明顺序按照workspace规范严格排序1. imports 2. interface / type / enum / class (按依赖顺序) 3. const 常量 4. Builder 函数 5. Entry / ComponentV2 struct: 5.1 状态变量 (Local / Param / Provide) 5.2 生命周期 (aboutToAppear / aboutToDisappear) 5.3 Builder 方法 5.4 build()七、避坑速查坑现象原因解决DI使用前未注册启动闪退get()返回undefined后直接使用在启动最早时机集中注册get()后判nullViewModel直接操作UI业务逻辑中出现UI引用违反了MVVM分层ViewModel只管理状态不引用组件Repository返回可变对象外部修改了内部数据Map/Array返回的是引用返回时做浅拷贝或使用不可变数据循环依赖编译报错或启动卡死A依赖BB依赖A引入中间接口解耦ViewModel臃肿单个ViewModel超过500行职责不单一按页面/功能拆分ViewModel八、总结大型项目的架构目标不是用的设计模式最多而是改动成本最低。核心原则依赖倒置DIP高层不依赖低层两者都依赖抽象DI容器就是那个抽象单一职责SRPPage只管渲染、ViewModel只管状态、Repository只管数据接口隔离ISP不强迫调用方依赖它不需要的方法一个文件只做一件事200行以内最优500行开始有异味MVVM DI Repository三层足够覆盖90%的企业级场景。剩下10%的复杂场景跨页面状态共享、复杂表单联动、数据流追溯可以在此基础上叠加Provide/Consume、EventHub等机制。对应Demo文件entry/src/main/ets/pages/ArchitectureDemo.ets

相关新闻

VideoSrt终极指南:5分钟快速生成专业视频字幕的免费神器

VideoSrt终极指南:5分钟快速生成专业视频字幕的免费神器

VideoSrt终极指南:5分钟快速生成专业视频字幕的免费神器 【免费下载链接】video-srt-windows 这是一个可以识别视频语音自动生成字幕SRT文件的开源 Windows-GUI 软件工具。 项目地址: https://gitcode.com/gh_mirrors/vi/video-srt-windows VideoSrt是一款专…

2026/7/26 17:13:00阅读更多 →
BG3ModManager完全手册:从零开始打造你的博德之门3模组体验

BG3ModManager完全手册:从零开始打造你的博德之门3模组体验

BG3ModManager完全手册:从零开始打造你的博德之门3模组体验 【免费下载链接】BG3ModManager A mod manager for Baldurs Gate 3. This is the only official source! 项目地址: https://gitcode.com/gh_mirrors/bg/BG3ModManager BG3ModManager是博德之门3&a…

2026/7/26 17:13:00阅读更多 →
闪链X网盘助手 - 免费云解析工具不限速下载拉满带宽

闪链X网盘助手 - 免费云解析工具不限速下载拉满带宽

在日常处理云端文件传输与下载的过程中,不少用户都会遇到原本顺畅的解析响应突然变得迟缓的情况。这种情况往往由多重因素交织而成。以下从网络环境、服务调度、硬件负载等多个维度,解析造成速度下降的核心原因与对应的优化思路。 https://www.pandown.…

2026/7/26 17:11:00阅读更多 →
Python上下文管理器在资源管理中的性能影响:__enter__与__exit__的开销分析

Python上下文管理器在资源管理中的性能影响:__enter__与__exit__的开销分析

Python上下文管理器在资源管理中的性能影响:__enter__与__exit__的开销分析Python的with语句和上下文管理器协议(Context Manager Protocol)是管理资源(文件、锁、数据库连接)的标准方式。但上下文管理器的__enter__和…

2026/7/26 18:43:22阅读更多 →
大模型落地 6 个月:从 PoC 到生产的差距比想象大

大模型落地 6 个月:从 PoC 到生产的差距比想象大

大模型落地 6 个月:从 PoC 到生产的差距比想象大 基础设施不需要漂亮话。 2024 年初,我们团队启动了一个大模型落地项目,目标是用大模型优化客服系统的自动回复能力。PoC 阶段 2 周就跑通了,但真正上线到生产环境,花了…

2026/7/26 18:43:22阅读更多 →
Kubernetes 集群迁移:从自建到托管平台的平滑切换

Kubernetes 集群迁移:从自建到托管平台的平滑切换

Kubernetes 集群迁移:从自建到托管平台的平滑切换 基础设施不需要漂亮话。 2024 年上半年,我负责了一个 K8s 集群迁移项目:从自建 K8s 集群(基于 kubeadm 部署)迁移到托管 K8s 平台(阿里云 ACK)…

2026/7/26 18:43:22阅读更多 →
Mathematics | Branch

Mathematics | Branch

注:本文为“遇见数学”翻译的 “数学分支概览” 两篇文章合辑。 数学世界的版图:主要分支概览(上) 原创 遇见数学 2025 年 04 月 03 日 12:02 河南 数学的分支(Areas of Mathematics) 在文艺复兴之前&am…

2026/7/26 18:43:22阅读更多 →
手把手教你学 Simulink—— A* 算法与 RRT 的混合路径搜索对比仿真

手把手教你学 Simulink—— A* 算法与 RRT 的混合路径搜索对比仿真

目录 手把手教你学 Simulink —— A* 算法与 RRT 的混合路径搜索对比仿真 一、A* 与 RRT 到底差在哪? 1.1 核心对比表 二、仿真总体架构(统一对比框架) 三、关键参数(公平对比默认) 四、Simulink 建模 Step‑by‑Step Step ① —— 统一地图表示 Step ② —— A*…

2026/7/26 18:43:22阅读更多 →
告别复杂UI开发:用Vizia构建响应式Rust桌面应用的全新范式

告别复杂UI开发:用Vizia构建响应式Rust桌面应用的全新范式

告别复杂UI开发:用Vizia构建响应式Rust桌面应用的全新范式 【免费下载链接】vizia A declarative GUI library written in Rust 项目地址: https://gitcode.com/gh_mirrors/vi/vizia 还在为Rust桌面应用开发中的状态管理与UI同步而烦恼吗?&#x…

2026/7/26 18:41:22阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/26 0:01:28阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/26 0:01:28阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/26 0:01:28阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/26 0:01:28阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/26 0:01:28阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/26 0:01:28阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/25 23:03:25阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/25 19:03:04阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/25 19:03:04阅读更多 →