HarmonyOS开发:架构约束检查
HarmonyOS开发:架构约束检查
📌 核心要点:架构不是画在PPT里的图,是代码里必须遵守的规则——没有自动化检查的架构约束,跟没有约束一样。
背景与动机
你有没有见过这种项目?架构文档写得漂漂亮亮:分层清晰、模块独立、依赖单向。但打开代码一看——UI层直接调数据库、公共模块依赖业务模块、循环依赖满天飞。架构图和代码完全是两个世界。
为什么会这样?因为架构约束只靠文档和口头约定,根本守不住。新来的同事不知道分层规则,赶工期的时候架构师顾不上Review,慢慢地架构就退化成了"大泥球"。
架构退化的后果很严重:改一个功能要动十几个模块,加一个需求要理解整个系统,新人上手要三个月才能干活。这不是夸张,这是很多项目的真实写照。
架构约束检查要解决的核心问题就是:用机器检查代替人工监督,让架构退化在发生时就被发现,而不是积累到不可收拾才发现。
鸿蒙项目尤其需要架构约束检查。为什么?鸿蒙的模块化体系(HAP/HSP/HAR)天然要求清晰的模块边界和单向依赖。一旦出现循环依赖或层次混乱,不仅代码难维护,编译和打包都会出问题。
核心原理
架构约束的五个维度
graph TD
A[架构约束检查] --> B[层次约束]
A --> C[模块约束]
A --> D[依赖约束]
A --> E[接口约束]
A --> F[数据约束]
B --> B1[UI层不能直接访问数据层]
B --> B2[业务层不能依赖UI组件]
B --> B3[数据层不能依赖业务逻辑]
C --> C1[模块间只能通过接口通信]
C --> C2[公共模块不能依赖业务模块]
C --> C3[特性模块间不能直接依赖]
D --> D1[禁止循环依赖]
D --> D2[依赖方向必须单向]
D --> D3[跨层依赖必须通过接口]
E --> E1[公共API必须有文档]
E --> E2[废弃API必须标记@deprecated]
E --> E3[接口变更需要版本管理]
F --> F1[数据模型不能混用层]
F --> F2[DTO与DO必须转换]
F --> F3[敏感数据不能跨层传递]
classDef mainStyle fill:#FF6B6B,stroke:#C0392B,color:#fff,font-weight:bold
classDef subStyle fill:#4ECDC4,stroke:#1ABC9C,color:#fff
classDef detailStyle fill:#FFE66D,stroke:#F39C12,color:#333
class A mainStyle
class B,C,D,E,F subStyle
class B1,B2,B3,C1,C2,C3,D1,D2,D3,E1,E2,E3,F1,F2,F3 detailStyle
架构约束检查流程
graph LR
A[定义架构规则] --> B[扫描代码结构]
B --> C[构建依赖图]
C --> D[规则匹配验证]
D --> E{是否违反?}
E -->|是| F[生成违规报告]
E -->|否| G[通过检查]
F --> H[阻断CI/阻断合并]
classDef startStyle fill:#A8E6CF,stroke:#2ECC71,color:#333
classDef processStyle fill:#4ECDC4,stroke:#1ABC9C,color:#fff
classDef decisionStyle fill:#6C5CE7,stroke:#8E44AD,color:#fff
classDef resultStyle fill:#FF6B6B,stroke:#E74C3C,color:#fff
class A startStyle
class B,C,D processStyle
class E decisionStyle
class F,G,H resultStyle
代码实战
基础用法:定义架构分层规则
首先,你得把架构规则写下来——不是写在文档里,是写成代码能检查的规则:
// architecture-rules.ts
// 架构分层规则定义
// 分层定义
enum Layer {
UI = 'ui', // UI层:页面、组件
BUSINESS = 'business', // 业务层:服务、逻辑
DATA = 'data', // 数据层:仓库、数据源
COMMON = 'common', // 公共层:工具、常量
}
// 层次依赖规则:定义允许的依赖方向
const LAYER_DEPENDENCY_RULES: Map<Layer, Layer[]> = new Map([
// UI层可以依赖:业务层、公共层
[Layer.UI, [Layer.BUSINESS, Layer.COMMON]],
// 业务层可以依赖:数据层、公共层
[Layer.BUSINESS, [Layer.DATA, Layer.COMMON]],
// 数据层可以依赖:公共层
[Layer.DATA, [Layer.COMMON]],
// 公共层不能依赖任何其他层
[Layer.COMMON, []],
]);
// 目录与层次的映射
const DIRECTORY_LAYER_MAP: Map<string, Layer> = new Map([
['pages', Layer.UI],
['views', Layer.UI],
['components', Layer.UI],
['widgets', Layer.UI],
['services', Layer.BUSINESS],
['viewmodels', Layer.BUSINESS],
['managers', Layer.BUSINESS],
['repositories', Layer.DATA],
['datasources', Layer.DATA],
['models', Layer.DATA],
['utils', Layer.COMMON],
['constants', Layer.COMMON],
['types', Layer.COMMON],
]);
// 检查层次违规
function checkLayerViolation(
fromDir: string,
toDir: string
): { violated: boolean; message: string } {
const fromLayer = findLayerByDir(fromDir);
const toLayer = findLayerByDir(toDir);
if (!fromLayer || !toLayer) {
return { violated: false, message: '' };
}
const allowedDeps = LAYER_DEPENDENCY_RULES.get(fromLayer) || [];
if (!allowedDeps.includes(toLayer)) {
return {
violated: true,
message: `${fromDir}(${fromLayer}) 不允许依赖 ${toDir}(${toLayer})。` +
`允许的依赖: [${allowedDeps.join(', ')}]`,
};
}
return { violated: false, message: '' };
}
function findLayerByDir(dir: string): Layer | undefined {
for (const [pattern, layer] of DIRECTORY_LAYER_MAP) {
if (dir.includes(pattern)) {
return layer;
}
}
return undefined;
}
进阶用法:模块依赖规则验证
鸿蒙项目的模块化体系(HAP/HSP/HAR)有严格的依赖规则:
// module-dependency-checker.ts
// 模块依赖规则验证器
interface ModuleInfo {
name: string; // 模块名
type: 'hap' | 'hsp' | 'har'; // 模块类型
dependencies: string[]; // 依赖的其他模块
layer: Layer; // 所属层次
}
interface DependencyViolation {
from: string;
to: string;
rule: string;
message: string;
severity: 'error' | 'warning';
}
class ModuleDependencyChecker {
private modules: Map<string, ModuleInfo> = new Map();
private violations: DependencyViolation[] = [];
// 注册模块
registerModule(module: ModuleInfo): void {
this.modules.set(module.name, module);
}
// 从oh-package.json5解析模块信息
parseModuleFromConfig(configPath: string, type: 'hap' | 'hsp' | 'har'): ModuleInfo {
// 实际项目中读取oh-package.json5文件
// 这里用模拟数据演示
const config = {
name: 'entry',
dependencies: {
'@ohos/common': '1.0.0',
'@ohos/network': '1.0.0',
'@ohos/database': '1.0.0',
},
};
const module: ModuleInfo = {
name: config.name,
type,
dependencies: Object.keys(config.dependencies),
layer: Layer.UI, // 根据模块类型推断
};
this.registerModule(module);
return module;
}
// 规则1:HAP可以依赖HSP和HAR,但不能依赖其他HAP
checkHapDependencyRule(): void {
for (const [name, module] of this.modules) {
if (module.type !== 'hap') continue;
for (const depName of module.dependencies) {
const dep = this.modules.get(depName);
if (dep && dep.type === 'hap') {
this.violations.push({
from: name,
to: depName,
rule: 'HAP-NO-HAP-DEP',
message: `HAP模块${name}不能依赖其他HAP模块${depName}`,
severity: 'error',
});
}
}
}
}
// 规则2:HSP不能依赖HAP
checkHspDependencyRule(): void {
for (const [name, module] of this.modules) {
if (module.type !== 'hsp') continue;
for (const depName of module.dependencies) {
const dep = this.modules.get(depName);
if (dep && dep.type === 'hap') {
this.violations.push({
from: name,
to: depName,
rule: 'HSP-NO-HAP-DEP',
message: `HSP模块${name}不能依赖HAP模块${depName}`,
severity: 'error',
});
}
}
}
}
// 规则3:公共模块不能依赖业务模块
checkCommonModuleRule(): void {
for (const [name, module] of this.modules) {
if (module.layer !== Layer.COMMON) continue;
for (const depName of module.dependencies) {
const dep = this.modules.get(depName);
if (dep && dep.layer !== Layer.COMMON) {
this.violations.push({
from: name,
to: depName,
rule: 'COMMON-NO-BUSINESS-DEP',
message: `公共模块${name}不能依赖非公共模块${depName}`,
severity: 'error',
});
}
}
}
}
// 规则4:检测循环依赖
checkCircularDependency(): void {
const visited = new Set<string>();
const recursionStack = new Set<string>();
const dfs = (moduleName: string, path: string[]): boolean => {
visited.add(moduleName);
recursionStack.add(moduleName);
const module = this.modules.get(moduleName);
if (!module) return false;
for (const depName of module.dependencies) {
if (!visited.has(depName)) {
if (dfs(depName, [...path, depName])) {
return true;
}
} else if (recursionStack.has(depName)) {
// 发现循环依赖
const cycleStart = path.indexOf(depName);
const cycle = [...path.slice(cycleStart), depName].join(' → ');
this.violations.push({
from: moduleName,
to: depName,
rule: 'CIRCULAR-DEPENDENCY',
message: `检测到循环依赖: ${cycle}`,
severity: 'error',
});
return true;
}
}
recursionStack.delete(moduleName);
return false;
};
for (const name of this.modules.keys()) {
if (!visited.has(name)) {
dfs(name, [name]);
}
}
}
// 执行所有检查
runAllChecks(): DependencyViolation[] {
this.violations = [];
this.checkHapDependencyRule();
this.checkHspDependencyRule();
this.checkCommonModuleRule();
this.checkCircularDependency();
return this.violations;
}
// 生成报告
generateReport(): string {
const violations = this.runAllChecks();
let report = '=== 模块依赖检查报告 ===\n\n';
if (violations.length === 0) {
report += '✅ 所有模块依赖规则检查通过\n';
return report;
}
const errors = violations.filter(v => v.severity === 'error');
const warnings = violations.filter(v => v.severity === 'warning');
report += `❌ 错误: ${errors.length} | ⚠️ 警告: ${warnings.length}\n\n`;
for (const v of violations) {
const icon = v.severity === 'error' ? '❌' : '⚠️';
report += `${icon} [${v.rule}] ${v.from} → ${v.to}\n`;
report += ` ${v.message}\n\n`;
}
return report;
}
}
// 使用示例
const checker = new ModuleDependencyChecker();
// 注册模块
checker.registerModule({
name: 'entry',
type: 'hap',
dependencies: ['@ohos/common', '@ohos/network'],
layer: Layer.UI,
});
checker.registerModule({
name: '@ohos/common',
type: 'har',
dependencies: [],
layer: Layer.COMMON,
});
checker.registerModule({
name: '@ohos/network',
type: 'hsp',
dependencies: ['@ohos/common'],
layer: Layer.DATA,
});
console.log(checker.generateReport());
完整示例:架构退化检测器
架构退化不是一天发生的,是日积月累的。我们需要一个工具来持续监控架构健康度:
// architecture-health-monitor.ts
// 架构健康度监控器
interface ArchitectureMetric {
name: string;
value: number;
threshold: number; // 阈值
status: 'healthy' | 'warning' | 'critical';
}
interface ArchitectureSnapshot {
timestamp: string;
metrics: ArchitectureMetric[];
overallScore: number; // 0-100
}
class ArchitectureHealthMonitor {
private snapshots: ArchitectureSnapshot[] = [];
private maxSnapshots = 30; // 保留最近30次快照
// 计算耦合度:模块间的依赖数量
calculateCoupling(modules: Map<string, string[]>): number {
let totalDeps = 0;
for (const deps of modules.values()) {
totalDeps += deps.length;
}
// 耦合度 = 平均每个模块的依赖数
return totalDeps / modules.size;
}
// 计算内聚度:模块内部文件的关联程度
calculateCohesion(moduleFiles: Map<string, string[]>): number {
let totalCohesion = 0;
let moduleCount = 0;
for (const [moduleName, files] of moduleFiles) {
if (files.length <= 1) continue;
// 简化的内聚度计算:文件名前缀相同的比例
const prefixes = new Set(files.map(f => {
const parts = f.split('/');
return parts.length > 1 ? parts[0] : f;
}));
// 前缀种类越少,内聚度越高
const cohesion = 1 - (prefixes.size - 1) / files.length;
totalCohesion += Math.max(0, cohesion);
moduleCount++;
}
return moduleCount > 0 ? totalCohesion / moduleCount : 1;
}
// 计算循环依赖数
countCircularDependencies(modules: Map<string, string[]>): number {
let count = 0;
const visited = new Set<string>();
const stack = new Set<string>();
const dfs = (name: string): void => {
visited.add(name);
stack.add(name);
const deps = modules.get(name) || [];
for (const dep of deps) {
if (!visited.has(dep)) {
dfs(dep);
} else if (stack.has(dep)) {
count++;
}
}
stack.delete(name);
};
for (const name of modules.keys()) {
if (!visited.has(name)) {
dfs(name);
}
}
return count;
}
// 计算层次违规数
countLayerViolations(
imports: Array<{ from: string; to: string }>
): number {
let count = 0;
for (const imp of imports) {
const result = checkLayerViolation(imp.from, imp.to);
if (result.violated) {
count++;
}
}
return count;
}
// 生成架构快照
createSnapshot(
modules: Map<string, string[]>,
moduleFiles: Map<string, string[]>,
imports: Array<{ from: string; to: string }>
): ArchitectureSnapshot {
const coupling = this.calculateCoupling(modules);
const cohesion = this.calculateCohesion(moduleFiles);
const circularDeps = this.countCircularDependencies(modules);
const layerViolations = this.countLayerViolations(imports);
const metrics: ArchitectureMetric[] = [
{
name: '耦合度',
value: coupling,
threshold: 5, // 每个模块平均不超过5个依赖
status: coupling > 8 ? 'critical' : coupling > 5 ? 'warning' : 'healthy',
},
{
name: '内聚度',
value: cohesion,
threshold: 0.6,
status: cohesion < 0.4 ? 'critical' : cohesion < 0.6 ? 'warning' : 'healthy',
},
{
name: '循环依赖',
value: circularDeps,
threshold: 0,
status: circularDeps > 3 ? 'critical' : circularDeps > 0 ? 'warning' : 'healthy',
},
{
name: '层次违规',
value: layerViolations,
threshold: 0,
status: layerViolations > 5 ? 'critical' : layerViolations > 0 ? 'warning' : 'healthy',
},
];
// 计算综合分数
const healthyCount = metrics.filter(m => m.status === 'healthy').length;
const overallScore = Math.round((healthyCount / metrics.length) * 100);
const snapshot: ArchitectureSnapshot = {
timestamp: new Date().toISOString(),
metrics,
overallScore,
};
this.snapshots.push(snapshot);
if (this.snapshots.length > this.maxSnapshots) {
this.snapshots.shift();
}
return snapshot;
}
// 检测架构退化趋势
detectDegradation(): string[] {
if (this.snapshots.length < 2) return [];
const warnings: string[] = [];
const latest = this.snapshots[this.snapshots.length - 1];
const previous = this.snapshots[this.snapshots.length - 2];
// 检查综合分数是否下降
if (latest.overallScore < previous.overallScore) {
warnings.push(
`架构健康度下降: ${previous.overallScore} → ${latest.overallScore}`
);
}
// 检查各项指标是否恶化
for (const metric of latest.metrics) {
const prevMetric = previous.metrics.find(m => m.name === metric.name);
if (prevMetric && metric.value > prevMetric.value &&
metric.name !== '内聚度') { // 耦合度、违规数越低越好
warnings.push(
`${metric.name}恶化: ${prevMetric.value} → ${metric.value}`
);
}
if (prevMetric && metric.name === '内聚度' &&
metric.value < prevMetric.value) {
warnings.push(
`内聚度下降: ${prevMetric.value} → ${metric.value}`
);
}
}
return warnings;
}
// 生成趋势报告
generateTrendReport(): string {
let report = '=== 架构健康度趋势报告 ===\n\n';
if (this.snapshots.length === 0) {
report += '暂无数据\n';
return report;
}
const latest = this.snapshots[this.snapshots.length - 1];
report += `📊 最新架构健康度: ${latest.overallScore}/100\n\n`;
// 各项指标状态
for (const metric of latest.metrics) {
const icon = metric.status === 'healthy' ? '✅' :
metric.status === 'warning' ? '⚠️' : '❌';
report += `${icon} ${metric.name}: ${metric.value.toFixed(2)} (阈值: ${metric.threshold})\n`;
}
// 退化检测
report += '\n';
const degradations = this.detectDegradation();
if (degradations.length > 0) {
report += '🚨 架构退化警告:\n';
for (const warning of degradations) {
report += ` - ${warning}\n`;
}
} else {
report += '✅ 架构健康度稳定\n';
}
// 历史趋势(最近5次)
if (this.snapshots.length > 1) {
report += '\n📈 最近趋势:\n';
const recent = this.snapshots.slice(-5);
for (const snapshot of recent) {
const date = new Date(snapshot.timestamp).toLocaleDateString();
const bar = '█'.repeat(Math.floor(snapshot.overallScore / 5));
report += ` ${date} ${bar} ${snapshot.overallScore}\n`;
}
}
return report;
}
}
踩坑与注意事项
坑1:架构规则定义过于理想化
架构师定义的规则完美无缺,但实际代码根本做不到。比如"UI层完全不能访问数据层"——有些简单的查询页面,绕过业务层直接读数据明显更合理。
解决方案:
- 架构规则要有"豁免机制":特殊情况可以申请豁免,但必须注释说明原因
- 区分"强制规则"和"建议规则":强制规则不能违反,建议规则可以豁免
- 规则要务实:从实际代码出发定义规则,不是从理想架构出发
坑2:检查粒度太粗或太细
太粗:只检查模块级依赖,文件级的层次违规检测不到。太细:每个import都检查,误报太多。
解决方案:
- 模块级检查:验证HAP/HSP/HAR之间的依赖关系
- 目录级检查:验证分层规则(UI层不依赖数据层)
- 文件级检查:只检查关键文件(如入口文件、公共接口)
坑3:架构检查拖慢CI
大型项目全量架构检查可能要跑几分钟。
解决方案:
- 增量检查:只检查变更文件影响的模块
- 分级检查:PR只跑快速检查,夜间构建跑全量检查
- 缓存依赖图:上次构建的依赖图缓存起来,只更新变更部分
坑4:架构规则变更缺乏版本管理
架构规则改了,但历史代码来不及改,检查一直报错。
解决方案:
- 架构规则走版本管理,变更需要评审
- 新规则先设为warning,给团队适应期,再升级为error
- 老代码标记为"已知违规",新代码不允许新增违规
坑5:忽视架构退化的渐进性
架构不是突然退化的,是慢慢退化的。今天多一个层次违规,明天多一个循环依赖,积累下来就不可收拾了。
解决方案:
- 建立架构健康度基线,持续监控
- 设置退化阈值:违规数超过基线的120%就告警
- 每周生成架构健康度报告,团队Review
HarmonyOS 6适配说明
HarmonyOS 6在架构约束检查方面的改进:
-
模块依赖图可视化:DevEco Studio 6内置了模块依赖图可视化工具,可以直观地看到HAP/HSP/HAR之间的依赖关系,循环依赖用红色标记。
-
构建时依赖检查:HarmonyOS 6的构建系统在编译阶段就会检查模块依赖规则,违反规则的依赖直接编译失败,不用等到运行时。
-
架构模板与约束:新建模块时可以选择架构模板(MVC/MVVM/Clean Architecture),模板自带架构约束规则,从源头避免架构退化。
-
API版本约束检查:新增了对API版本兼容性的架构约束检查——如果模块A依赖的API版本高于模块B支持的版本,会报错。
-
架构规则市场:华为开发者社区提供了架构规则模板,团队可以直接使用或在此基础上定制。
总结
架构约束检查是防止架构退化的最后一道防线。没有自动化检查,架构文档写得再漂亮也是白搭。
核心要点:
- 架构规则必须可检查:不能检查的规则等于没有
- 分层是基础:UI→业务→数据→公共,依赖方向必须单向
- 循环依赖零容忍:一旦出现,立即修复
- 持续监控:架构健康度不是一次性的,需要持续跟踪
- 规则要务实:从实际代码出发,不是从理想架构出发
| 维度 | 评分 | 说明 |
|---|---|---|
| 学习难度 | ⭐⭐⭐⭐ | 需要理解架构设计+AST分析+依赖图算法 |
| 使用频率 | ⭐⭐⭐ | CI每次构建都跑,但开发者日常感知不强 |
| 重要程度 | ⭐⭐⭐⭐⭐ | 架构退化的克星,长期项目必备 |
- 点赞
- 收藏
- 关注作者
评论(0)