06 项目目录结构与编码规范:三层架构与 DAO 模式实践

发布时间:2026/7/9 2:13:18
06 项目目录结构与编码规范:三层架构与 DAO 模式实践 06 项目目录结构与编码规范三层架构与 DAO 模式实践前言图06 项目目录结构与编码规范三层架构与 DAO 模式实践 运行效果截图HarmonyOS NEXT良好的目录结构和编码规范是大型项目可维护、可扩展、可协作的基石。随着业务逻辑增长没有规范的项目会变成意大利面条代码——任何人改动一处都可能牵一发动全身。鹿鹿·笔迹心理分析项目拥有约13,000 行 ArkTS 代码、50 个 .ets 文件在这样的规模下合理的架构和规范的命名尤为重要。本文将系统介绍项目的目录组织原则、三层架构设计和各层的编码规范。官方文档·项目目录结构developer.huawei.comArkTS 编码规范developer.huawei.com图鹿鹿项目三层架构——UI 表现层 → 业务功能层 → 公共基础设施层职责清晰层间依赖单向一、项目完整目录树1.1 顶层目录结构harmony-app/ ├── AppScope/ # 应用级资源全局共用 │ ├── app.json5 # 应用配置包名、版本等 │ └── resources/base/ │ ├── element/string.json # 应用名字符串 │ └── media/app_icon.svg # 应用图标 │ ├── entry/ # 主 Entry 模块唯一模块 │ ├── src/main/ │ │ ├── ets/ # ArkTS 源码根目录 │ │ ├── resources/ # 模块资源文件 │ │ └── module.json5 # 模块配置 │ ├── build-profile.json5 # 模块构建配置 │ ├── hvigorfile.ts # hvigor 构建脚本 │ └── oh-package.json5 # 模块依赖 │ ├── oh_modules/ # ohpm 依赖不提交 git ├── hvigor/ # hvigor 配置目录 ├── build-profile.json5 # 项目构建配置 ├── oh-package.json5 # 项目根依赖 └── hvigorfile.ts # 项目 hvigor 入口1.2 源码目录详细结构entry/src/main/ets/ ├── EntryAbility.ets # 应用入口 Ability │ ├── common/ # 公共基础设施层 │ ├── components/ # 可复用 UI 组件6 个 │ │ ├── EmotionTrendChart.ets # 情绪折线图 │ │ ├── EmptyState.ets # 空状态组件 │ │ ├── HandwritingRadar.ets # 6 维雷达图 │ │ ├── HmTitleBar.ets # 标题栏 │ │ ├── MatchRing.ets # 匹配度圆环 │ │ └── PosterTemplate.ets # 分享海报模板 │ └── theme/ # 设计令牌系统3 个文件 │ ├── AppAnimations.ets # 动画常量时长、曲线 │ ├── AppColors.ets # 颜色令牌20 颜色 │ └── AppFonts.ets # 字体/间距/圆角令牌 │ ├── features/ # 业务功能层 │ ├── ai/ # AI 推理模块4 个文件 │ │ ├── AiPipelineService.ets # 推理流水线统一入口 │ │ ├── EmotionClassifier.ets # 情绪分类器 │ │ ├── HandwritingFeatureExtractor.ets # 手写特征提取 │ │ └── OcrService.ets # OCR 文字识别 │ └── data/ # 数据访问层8 个文件 │ ├── ArchiveDao.ets # 档案 DAO │ ├── DatabaseService.ets # RDB 数据库单例 │ ├── DemoSeeder.ets # 演示数据播种器 │ ├── HandwritingDao.ets # 手写记录 DAO │ ├── Models.ets # 数据实体接口定义 │ ├── RelationDao.ets # 关系 DAO │ ├── ReportDao.ets # 报告 DAO │ └── UserDao.ets # 用户 DAO │ └── pages/ # UI 表现层16 个页面 ├── AnalyzingPage.ets # 分析中进度动画 ├── ArchivePage.ets # 档案管理 ├── BindRelationPage.ets # NFC 绑定伴侣 ├── CapturePage.ets # 拍照/相册选图 ├── CoupleHomePage.ets # 双人空间首页 ├── DuetReportPage.ets # 双人合盘报告 ├── EmptyPage.ets # 空状态/占位 ├── HomePage.ets # 应用主页 ├── LoginPage.ets # 华为账号登录 ├── MePage.ets # 个人中心 ├── PhoneLoginPage.ets # 手机号登录 ├── ReportDetailPage.ets # 报告详情 ├── SharePage.ets # 分享页面 ├── TrendPage.ets # 情绪趋势 ├── UnclassifiedArchivePage.ets # 未归类整理 └── WritePage.ets # 手写板二、三层架构设计2.1 架构分层原则项目采用经典的三层架构Three-Tier Architecture② 业务功能层 (features/)读写 AppStorage使用设计令牌调用推理③ 公共基础设施 (common/theme/)设计令牌系统AppColors · AppFonts · AppAnimations① UI 表现层 (pages/ common/components/)16 个 Entry 页面HomePage · CapturePage · WritePageAnalyzingPage · ReportDetailPage · ...6 个可复用组件HandwritingRadar · EmotionTrendChartMatchRing · HmTitleBar · EmptyStatedata/ — 数据访问层 (DAO)DatabaseService · UserDaoArchiveDao · ReportDao · ...ai/ — AI 推理层AiPipelineServiceOcrService · FeatureExtractorEmotionClassifier2.2 各层职责层次目录主要职责依赖方向UI 表现层pages/common/components/页面布局、用户交互、动画效果依赖 features/ 和 common/theme/业务功能层features/data/features/ai/数据 CRUD、AI 推理、业务逻辑依赖 common/theme/偶尔公共基础设施common/theme/设计令牌、动画常量无依赖最底层层间依赖原则只允许上层依赖下层禁止下层依赖上层features/data不应该 import 任何pages/中的内容。三、DAO 设计模式3.1 DAO 模式的统一结构项目中每个 DAO 类都遵循相同的接口约定exportclassXxxDao{// ① 私有 getStore获取 RDB 实例防止外部绕过 DatabaseServiceprivatestaticgetStore():relationalStore.RdbStore{returnDatabaseService.getInstance().getStore();}// ② 创建记录staticasynccreate(item:XxxEntity):Promisenumber{try{conststoreXxxDao.getStore();constvalues:relationalStore.ValuesBucket{/* 字段映射 */};returnawaitstore.insert(xxx_table,values);}catch(e){hilog.error(0x0000,TAG,create 失败: %{public}s,JSON.stringify(e));throwe;}}// ③ 按 ID 查询staticasyncfindById(id:number):PromiseXxxEntity|null{/* ... */}// ④ 条件查询列表staticasynclistBy(condition:string):PromiseXxxEntity[]{/* ... */}// ⑤ 更新staticasyncupdate(item:XxxEntity):Promisevoid{/* ... */}// ⑥ 删除staticasyncdeleteById(id:number):Promisevoid{/* ... */}// ⑦ 私有 parseRowResultSet → Entity 转换privatestaticparseRow(rs:relationalStore.ResultSet):XxxEntity{return{id:rs.getLong(rs.getColumnIndex(id)),// ... 字段映射};}}3.2 DAO 设计的优势这种统一的 DAO 模式带来了三个核心优势解耦UI 层不需要知道数据库的具体实现只调用 DAO 方法可测试DAO 是纯静态类可以单独测试不依赖 UI 框架一致性所有数据操作都经过DatabaseService.getStore()进行不会出现野生的数据库操作四、命名规范4.1 标识符命名约定类型规范示例类名PascalCaseDatabaseService、HandwritingRadar接口名PascalCase Entity/Service/ResultUserEntity、RadarScores方法名camelCase动词开头getInstance()、loadRecent()私有方法camelCase同上parseRow()、draw()State 变量camelCaseisLoading、recentItems常量UPPER_SNAKE_CASEDB_VERSION、TAG文件名PascalCase .etsAppColors.ets、HandwritingDao.ets数据库表名snake_casehandwriting_records、user_archives数据库字段snake_casearchive_id、created_at4.2 装饰器使用约定// ✅ 标准写法Entry 和 Component 分别一行EntryComponentstruct HomePage{// ✅ 公开 PropPascalCase 类型 赋默认值Proptitle:stringPropshowBack:booleanfalse// ✅ 内部 State一目了然的变量名StateisLoading:booleanfalseStateitems:ArchiveEntity[][]// ✅ Watch 与 State 放在一起StateWatch(onProgressChange)drawProgress:number0// ✅ 私有常量类顶部定义privatereadonlyTAGHomePageprivatereadonlyPAGE_SIZE20// ✅ 生命周期方法在前Builder 在后build() 最后aboutToAppear():void{...}aboutToDisappear():void{...}BuilderRecentCard(item:RecentItem){...}build(){...}}五、文件组织规范5.1 页面文件结构规范每个页面文件 (pages/XxxPage.ets) 按以下顺序组织代码// 1. import 语句按 kit 包 → 业务模块 → 组件顺序import{hilog}fromkit.PerformanceAnalysisKit;import{ArchiveDao}from../features/data/ArchiveDao;import{HmTitleBar}from../common/components/HmTitleBar;import{AppColors,AppFonts}from../common/theme/AppColors;// 2. 本地接口/类型定义interfaceRecentItem{reportId:number;name:string;personalityType:string;}// 3. 常量定义constTAGArchivePage;constPAGE_SIZE20;// 4. 页面组件EntryComponentstruct ArchivePage{// 4.1 Prop 属性外部传入// 4.2 State 属性内部状态// 4.3 私有成员变量// 4.4 生命周期方法// 4.5 私有业务方法// 4.6 Builder 方法// 4.7 build() 方法放最后}5.2 组件文件规范// common/components/HandwritingRadar.ets// 1. 类型定义放在组件文件内一起导出exportinterfaceRadarPoint{label:string;value:number;}// 2. 组件定义export structComponentexportstruct HandwritingRadar{// Prop 外部数据Propdata:RadarPoint[][];PropradarSize:number280;// 私有内部状态privatecontext:CanvasRenderingContext2D;StateWatch(onDrawProgressChange)drawProgress:number0;// 生命周期aboutToAppear():void{...}// Watch 回调onDrawProgressChange():void{...}// 私有绘图方法privatedraw():void{...}// build()build(){Canvas(this.context).width(this.radarSize).height(this.radarSize)}}六、设计令牌系统6.1 AppColors 颜色体系// common/theme/AppColors.ets — 颜色令牌不允许直接写 hex 魔数exportclassAppColors{// 品牌色系暖棕色系staticreadonlyBG:string#F1ECE2;// 背景色staticreadonlyCARD:string#F9F5EF;// 卡片背景staticreadonlyPRIMARY:string#A8907A;// 主色staticreadonlyPRIMARY_DEEP:string#8B7264;// 深主色staticreadonlyWARM:string#D9A689;// 暖橙色// 文字色系三级层次staticreadonlyTEXT:string#3D352E;// 主文字staticreadonlyTEXT_2:string#8B7E72;// 次要文字staticreadonlyTEXT_3:string#B5A99A;// 第三级文字// 辅助色系情绪色staticreadonlyQUIET:string#7B96C2;// 沉静蓝staticreadonlyROSE:string#D4849A;// 玫瑰粉staticreadonlyDEEP:string#6B5D52;// 深棕色// 线条/边框staticreadonlyLINE:string#E8E0D5;// 浅分割线}6.2 AppFonts 字体令牌// common/theme/AppFonts.ets — 字体尺寸令牌exportabstractclassAppFonts{// 字号系统6 级staticreadonlyTITLE_L:number28;// 大标题staticreadonlyTITLE_M:number20;// 中标题staticreadonlySUBTITLE:number16;// 副标题staticreadonlyBODY:number14;// 正文staticreadonlyCAPTION:number12;// 辅助staticreadonlyTINY:number10;// 极小// 字重常量staticreadonlyW_REGULAR:number400;staticreadonlyW_MEDIUM:number500;staticreadonlyW_BOLD:number700;}七、代码质量规范7.1 ArkTS 特有约束ArkTS 相对 TypeScript 有一些额外的限制需要注意禁止使用any类型必须明确声明类型禁止动态属性访问obj[key]应改为(obj as Recordstring, T)[key]禁止eval()ArkTS 不支持动态代码执行接口声明方式ArkTS 推荐使用interface而非type用于对象类型// ❌ TypeScript 写法在 ArkTS 中不推荐或不允许constvalueobj[dynamicKey];// 动态属性访问constx:anysomeValue;// any 类型// ✅ ArkTS 推荐写法constrecordobjasRecordstring,Object;constvaluerecord[dynamicKey];// 先类型断言再访问constx:number|stringsomeValue;// 明确类型7.2 错误处理规范// ✅ 推荐的错误处理模式staticasynccreate(item:ArchiveEntity):Promisenumber{try{conststoreArchiveDao.getStore();returnawaitstore.insert(archives,this.toValuesBucket(item));}catch(e){// 1. 记录日志包含方法名和原始错误hilog.error(0x0000,TAG,ArchiveDao.create 失败: %{public}s,JSON.stringify(e));// 2. 重新抛出让调用方决定如何处理thrownewError(ArchiveDao.create failed:${JSON.stringify(e)});}}7.3 注释规范/** * 双人匹配度圆环组件 * * 使用场景DuetReportPage 顶部的匹配度展示区域 * 特点Canvas 绘制 Watch 驱动动画从 0 到目标值顺时针生长 * * example * MatchRing({ matchValue: 78, ringSize: 200, label: 整体匹配度 }) */Componentexportstruct MatchRing{PropmatchValue:number78// 匹配度 0-100PropringSize:number200// 圆环尺寸宽高相等Proplabel:string匹配度// 中心文字标签}八、注意事项与常见问题8.1 开发注意事项在实际开发过程中需特别注意以下几点API 兼容性部分接口仅在特定 HarmonyOS NEXT 版本中可用需做版本条件判断权限模型采用静态声明module.json5 动态申请requestPermissionsFromUser的两阶段授权生命周期合理使用aboutToAppear()和aboutToDisappear()管理资源初始化与释放状态同步跨页面数据通过 AppStorage 共享组件内状态使用State/Prop/Link装饰器8.2 常见错误与解决方案开发过程中的高频问题汇总错误现象可能原因解决方案权限被系统拒绝未在module.json5中静态声明添加requestPermissions权限配置项页面跳转后白屏路由路径未在main_pages.json注册检查并补充路由配置文件UI 状态不刷新状态变量未添加响应式装饰器为变量加State/Observed装饰器数据库查询返回空查询条件与存储数据格式不匹配使用hilog.debug打印 SQL 条件排查相机预览黑屏运行时相机权限未获取先调用requestPermissionsFromUser申请总结项目的目录结构和编码规范遵循以下核心原则三层架构UI 层 → 业务层 → 基础设施层层间依赖单向职责清晰DAO 模式统一的数据访问接口解耦 UI 和数据库设计令牌AppColors/AppFonts/AppAnimations 集中管理禁止魔数命名规范PascalCase类/文件、camelCase方法/变量、UPPER_SNAKE常量文件组织import → 接口定义 → 常量 → 组件固定顺序保证可读性下一篇预告第07篇 ArkTS 基础类型与接口定义如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源项目目录结构指南ArkTS 编码规范HAP/HSP/HAR 模块类型ArkTS 与 TypeScript 差异说明第00篇 项目总览第04篇 依赖管理第07篇 ArkTS基础类型DevEco Studio 项目管理