HarmonyOS开发:架构约束检查

举报
Jack20 发表于 2026/06/24 16:37:12 2026/06/24
【摘要】 HarmonyOS开发:架构约束检查📌 核心要点:架构不是画在PPT里的图,是代码里必须遵守的规则——没有自动化检查的架构约束,跟没有约束一样。 背景与动机你有没有见过这种项目?架构文档写得漂漂亮亮:分层清晰、模块独立、依赖单向。但打开代码一看——UI层直接调数据库、公共模块依赖业务模块、循环依赖满天飞。架构图和代码完全是两个世界。为什么会这样?因为架构约束只靠文档和口头约定,根本守不住...

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[DTODO必须转换]
    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在架构约束检查方面的改进:

  1. 模块依赖图可视化:DevEco Studio 6内置了模块依赖图可视化工具,可以直观地看到HAP/HSP/HAR之间的依赖关系,循环依赖用红色标记。

  2. 构建时依赖检查:HarmonyOS 6的构建系统在编译阶段就会检查模块依赖规则,违反规则的依赖直接编译失败,不用等到运行时。

  3. 架构模板与约束:新建模块时可以选择架构模板(MVC/MVVM/Clean Architecture),模板自带架构约束规则,从源头避免架构退化。

  4. API版本约束检查:新增了对API版本兼容性的架构约束检查——如果模块A依赖的API版本高于模块B支持的版本,会报错。

  5. 架构规则市场:华为开发者社区提供了架构规则模板,团队可以直接使用或在此基础上定制。

总结

架构约束检查是防止架构退化的最后一道防线。没有自动化检查,架构文档写得再漂亮也是白搭。

核心要点:

  • 架构规则必须可检查:不能检查的规则等于没有
  • 分层是基础:UI→业务→数据→公共,依赖方向必须单向
  • 循环依赖零容忍:一旦出现,立即修复
  • 持续监控:架构健康度不是一次性的,需要持续跟踪
  • 规则要务实:从实际代码出发,不是从理想架构出发
维度 评分 说明
学习难度 ⭐⭐⭐⭐ 需要理解架构设计+AST分析+依赖图算法
使用频率 ⭐⭐⭐ CI每次构建都跑,但开发者日常感知不强
重要程度 ⭐⭐⭐⭐⭐ 架构退化的克星,长期项目必备
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。