Skip to content

抽象度の違反 (Abstractness Violation)

ID: abstractness | 重要度: 中 (デフォルト)

このルールが検出するもの (TL;DR)

このルールは、以下のモジュールにフラグを立てます:

  • 過度に具体的で過度に安定している — 多くのファイルが具象クラスに依存している(安全に変更するのが困難)。
  • 過度に抽象的で過度に不安定 — 誰も依存していない抽象(過剰設計/YAGNI)。

要するに:
👉 安定したモジュール(基盤)は抽象的であるべきです。
👉 不安定なモジュール(末端)は具体的であるべきです。


「モジュール」とは何か?

archlint では、モジュールは少なくとも1つのシンボルをエクスポートする単一のソースファイル(.ts.tsx など)です。

  • 各ファイルは独立して分析されます。
  • Barrel ファイル(index.ts)は集約モジュールとして扱われます。
  • 再エクスポートとインポートは、モジュール間の依存関係としてカウントされます。

メトリクスと直感

1. 不安定性 (I)

モジュールが変更されやすい度合いを測定します。

I = エフェレント結合 (Ce) / (アフェレント結合 (Ca) + エフェレント結合 (Ce))

直感

  • 安定 (I ≈ 0):多くのファイルがあなたをインポートしますが、あなたはほとんど誰もインポートしません。あなたは基盤コンポーネントです。
  • 不安定 (I ≈ 1):あなたは多くのものをインポートしますが、誰もあなたをインポートしません。あなたはシステムの端にいます。

2. 抽象度 (A)

実際の使用に基づくセマンティック計算を使用します:

A = (インターフェース/型のみをインポートするクライアント) / (クライアント総数)

クラシックな A との重要な違い: ファイル内のキーワードやインターフェースを単にカウントするのではありません。代わりに、モジュールが実際にどのように使用されているかを測定します:

  • 具象 class をインポート → 具象依存
  • interface または type をインポート → 抽象依存

これは実際のアーキテクチャ結合を反映し、単なる構文ではありません。import type を使用することは、抽象的な意図の強いシグナルです。

3. 距離 (D)

A + I = 1 となる理想的な「メインシーケンス」線からの距離。

D = |A + I - 1|


リスクゾーン (解釈)

AI の値に基づいて、モジュールは特定のゾーンに分類されます:

🧱 苦痛の地帯

  • メトリクス:I ≈ 0–0.3(安定)、A ≈ 0–0.3(具象)。
  • 問題:誰もが具象実装に依存しています。変更することは危険です。なぜなら、それは硬直しており、高度に結合しているからです。

悪い例(具象依存):

typescript
// database.service.ts
export class DatabaseService {
  save(data: any) {
    /* 具象ロジック */
  }
}

// client.ts (100+ ファイルがこれを行っている)
import { DatabaseService } from './database.service'; // 直接クラスインポート
const db = new DatabaseService();

なぜフラグが立てられるか

  • Ca = 100+(非常に安定、I ≈ 0)。
  • A = 0(クライアントがクラスを直接インポート)。
  • D ≈ 1 → メインシーケンスからの最大距離。

💨 無用の地帯

  • メトリクス:I ≈ 0.7–1.0(不安定)、A ≈ 0.7–1.0(抽象)。
  • 問題:誰も使用しない過剰設計された抽象。

例:

typescript
// complex-plugin.interface.ts
export interface IComplexPlugin {
  execute(context: any): Promise<void>;
}
// 0 個の実装と 0 個のクライアントがこのインターフェースを使用。

なぜフラグが立てられるか

  • I ≈ 1(誰もそれに依存していない)。
  • A = 1(純粋に抽象的)。
  • D ≈ 1 → 目的なく抽象が存在する。

偽陽性を減らすためのヒューリスティック

静的解析はノイズが多い場合があります。これらのヒューリスティックは、ルールをアーキテクチャの決定に焦点を当て、偶発的なコードではなく:

  1. 安定性のしきい値 (Fan-in):少なくとも fan_in_threshold(デフォルト:10)の依存関係を持つモジュールのみが分析されます。少数のファイルのみがモジュールを使用する場合、そのアーキテクチャへの影響は低いです。
  2. DTO とエンティティ:メソッドのないクラス(データのみ)は無視されます。これらはデータキャリアであり、動作コンポーネントではありません。
  3. エラーError を拡張するクラスは無視されます。これらは設計上常に具象です。
  4. インフラストラクチャスクリプト:データベースマイグレーション(up/down)は無視されます。これらは手続き型スクリプトであり、長期的なアーキテクチャの一部ではないためです。

修正方法 (意思決定ガイド)

  1. モジュールは安定していますか(多くの依存関係がありますか)?

    • はいinterface を抽出します。クライアントが import type { ... } を使用することを確認します。依存性注入を使用します。
    • いいえ:抽象化は不要かもしれません。安定性が増すまで具体的に保ちます。
  2. 複数の実装がありますか?

    • いいえ:不安定な場合は、インターフェースの削除を検討してください(YAGNI)。
    • はい:インターフェースは正当化されますが、クライアントがクラスではなくインターフェースに依存することを確認します。

設定

yaml
rules:
  abstractness:
    severity: medium
    distance_threshold: 0.85 # 距離 D のトリガーしきい値
    fan_in_threshold: 10 # 分析をトリガーする最小の受信依存関係(Fan-in)

Released under the MIT License.