クリーンアーキテクチャという「型」の暴力 — 過剰な抽象化が現場を壊すメカニズム

@sside 氏が X で投稿した、クリーンアーキテクチャの過剰適用への批判が反響を呼んでいます。

クリーンアーキテクチャかぶれの糞プロジェクト、異なる会社で2度目撃しました。(どっちもNestJS)

この投稿は、@masuda220(増田亨)氏のツイートへの引用です。増田氏は以下のように述べています。

私の狭い観測範囲ではあるけれど、クリーンアーキテクチャを取り入れていると説明されたコードを見ると、過剰な変換コードと過剰な依存性の逆転をしているものが多い。実験目的であれば、やりすぎるのもありだと思うが、実プロダクトでは、不要な複雑さを持ち込んで苦しんでいるように見える。

2 人の指摘は、日本のエンジニアコミュニティで繰り返し議論されてきた「クリーンアーキテクチャのカーゴカルト問題」を改めて可視化しています。本記事では、クリーンアーキテクチャとは何か、カーゴカルトとは何かを整理した上で、なぜこの問題が繰り返し起きるのかを構造的に分析します。

クリーンアーキテクチャとは何か

起源と系譜

クリーンアーキテクチャは、Robert C. Martin(通称 Uncle Bob)が 2012 年にブログで提唱し、2017 年に書籍『Clean Architecture: A Craftsman’s Guide to Software Structure and Design』として体系化した設計思想です。

この思想は突然生まれたものではなく、先行するアーキテクチャパターンの集大成です。

アーキテクチャ提唱者核心
2005ヘキサゴナルアーキテクチャ(Ports and Adapters)Alistair Cockburnアプリケーションを外部から分離する
2008オニオンアーキテクチャJeffrey Palermo依存関係を内側に向ける
2012クリーンアーキテクチャRobert C. Martin上記を統合し SOLID 原則と結びつける

クリーンアーキテクチャが新たに発明したものは実はほとんどありません。ヘキサゴナルアーキテクチャとオニオンアーキテクチャのルールを包含し、SOLID 原則(特に依存性逆転の原則)を軸に再構成したものです。

同心円図と依存性ルール

書籍で最も有名なのが、4 層の同心円図です。

┌─────────────────────────────────────────┐
│  Frameworks & Drivers(外側)            │
│  ┌───────────────────────────────────┐  │
│  │  Interface Adapters                │  │
│  │  ┌─────────────────────────────┐  │  │
│  │  │  Application Business Rules │  │  │
│  │  │  ┌───────────────────────┐  │  │  │
│  │  │  │  Enterprise Business  │  │  │  │
│  │  │  │  Rules(中心)         │  │  │  │
│  │  │  └───────────────────────┘  │  │  │
│  │  └─────────────────────────────┘  │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘

依存性ルール: すべての依存は外側から内側に向かう(→ 中心)

依存性ルールがこのアーキテクチャの柱です。外側の層(フレームワーク、DB、UI)が内側の層(ビジネスロジック)に依存し、逆方向の依存は許されません。これにより、ビジネスロジックはフレームワークやデータベースの変更に影響されず、テスト可能で長寿命なコードになるとされています。

本来の目的

クリーンアーキテクチャが解決しようとしている問題は明確です。

  • フレームワークへの依存: フレームワークのバージョンアップや移行でビジネスロジックまで書き直す羽目になる
  • テスト困難: DB や外部 API と密結合しており、ビジネスロジック単体でテストできない
  • 関心の混在: UI のコード、ビジネスルール、データアクセスが 1 つのファイルに混在している

これらは実際のプロジェクトで起きる問題であり、クリーンアーキテクチャの思想自体には正当性があります。問題は、この思想の適用方法にあります。

カーゴカルトとは何か

起源 — 南太平洋の滑走路

「カーゴカルト(積荷崇拝)」は、第二次世界大戦中に南太平洋の島々で起きた現象に由来します。

戦時中、米軍が島に飛行場を建設し、飛行機で物資(カーゴ)を運び入れました。戦争が終わり米軍が去ると、島民は物資の供給を再開させようとしました。彼らは竹で滑走路を作り、木でヘッドホンを彫り、ヤシの葉で管制塔を建て、飛行機が来るのを待ちました。形式は完璧に模倣されていましたが、飛行機は来ませんでした。

ファインマンの「カーゴカルト・サイエンス」

ノーベル物理学賞受賞者の Richard Feynman は、1974 年のカリフォルニア工科大学卒業式講演でこの比喩を用いました。

科学の見た目をすべて備えている — 形式も、手順も、あらゆる外面的な体裁も。しかし、何か本質的なものが欠けている。なぜなら、飛行機が降りてこないからだ。

Feynman が指摘したのは、形式を模倣しても、背後にある原理を理解していなければ結果は得られないということです。

ソフトウェア開発におけるカーゴカルト

この概念がソフトウェア開発に適用されたのが「カーゴカルトプログラミング」です。1991 年の Jargon File(コンピュータスラング辞典)に初出が確認されています。

ソフトウェアにおけるカーゴカルトとは、デザインパターンやアーキテクチャを、その背後にある理由を理解せずに儀式的に適用することを指します。

カーゴカルトの構造:

  成功企業の実践         →   模倣者の行動
  ─────────────         ─────────────
  [問題を分析]            [スキップ]
  [解決策を設計]          [スキップ]
  [アーキテクチャを選択]   [ここだけコピー] ← 形式の模倣
  [成果を検証]            [スキップ]

重要なのは、カーゴカルトは「間違った技術を使う」ことではなく、**「正しい技術を、理由を理解せずに使う」**ことです。クリーンアーキテクチャ自体は優れた設計思想ですが、なぜその設計が必要なのかを理解せずに適用すると、複雑さだけが残ります。

目撃された 3 つのアンチパターン

@sside 氏が報告した具体的な症状は、過剰適用の典型例です。

1. 交換不可能なものの抽象化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// やってはいけない例: 決済サービスの抽象化 Interface
interface PaymentGateway {
  charge(amount: number, currency: string): Promise<ChargeResult>;
  refund(chargeId: string): Promise<RefundResult>;
}

// 実装は Stripe 一択。今後も変わる予定なし
class StripePaymentGateway implements PaymentGateway {
  // Stripe 固有の処理が大量に...
}

クリーンアーキテクチャの教科書は「外部サービスを抽象化せよ」と説きます。しかし、決済サービスの移行は API の差し替えだけでは済みません。データモデル、Webhook の仕様、エラーハンドリング、コンプライアンス要件 — すべてが変わります。Interface を作っても、移行時にはどのみち全面書き直しです。

YAGNI 原則(You Aren’t Gonna Need It) が示す通り、「将来交換するかもしれない」という仮定のために今日の複雑さを増やすのは、投資対効果が合いません。

2. DB テーブル単位のサービスクラス分割

services/
  UserService.ts        ← users テーブルに対応
  OrderService.ts       ← orders テーブルに対応
  OrderItemService.ts   ← order_items テーブルに対応
  ProductService.ts     ← products テーブルに対応
  PaymentService.ts     ← payments テーブルに対応

これはクリーンアーキテクチャではなく、CRUD 層をクラスに分けただけです。ビジネスロジックが複数テーブルにまたがる場合(注文 = 注文 + 注文明細 + 在庫 + 決済)、ロジックがサービス間に散らばり、どこに何が書いてあるか分からなくなります。

ドメイン駆動設計(DDD)であれば「注文」という集約(Aggregate)単位で分割します。テーブル単位の分割は、ドメインの構造を無視したデータベース中心の設計であり、クリーンアーキテクチャの意図とは真逆です。

3. フレームワークの DI を無視した手動注入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// NestJS の DI を使えば数行で済むところを...
const providers = [
  { provide: 'UserRepository', useClass: UserRepositoryImpl },
  { provide: 'OrderRepository', useClass: OrderRepositoryImpl },
  { provide: 'PaymentGateway', useClass: StripePaymentGateway },
  // ... 数百行続く
  { provide: 'NotificationService', useClass: EmailNotificationService },
];

// 文字列キーでの手動注入
constructor(@Inject('UserRepository') private readonly userRepo: UserRepository) {}

NestJS は強力な DI コンテナを内蔵しています。クラスデコレータだけで依存関係を解決できるのに、あえて文字列キーで手動注入するのは「フレームワークに依存しないため」という教条主義の産物です。

皮肉なことに、クリーンアーキテクチャが「フレームワークからの独立」を説く一方で、NestJS の DI を無視する行為はフレームワークの最大の利点を捨てています。

なぜ「カーゴカルト」が起きるのか

Zenn の記事「クリーンアーキテクチャの功罪」は、この問題の構造を的確に分析しています。

「クリーン」という命名の威力

「クリーン」という名前は強すぎます。「クリーンアーキテクチャを採用していない」と言うと、まるで「不潔なコードを書いている」かのような印象を与えます。この心理的圧力が、無批判な導入を後押しします。

同心円図の罠

Robert C. Martin の書籍で示された同心円図は、覚えやすく説明しやすいため、クリーンアーキテクチャの「全体」として誤認識されがちです。実際には書籍の内容はもっと広範で文脈依存ですが、同心円を忠実に再現することが目的化してしまいます。

カーゴカルトのメカニズム

1. 成功企業が「クリーンアーキテクチャを採用」と発信
   ↓
2. 「同じ構造にすれば同じ品質が得られるはず」と誤解
   ↓
3. 文脈(チーム規模、ドメインの複雑さ、変更頻度)を無視して導入
   ↓
4. 不要な複雑さが保守コストを増大させる
   ↓
5. 「やり方が悪い」と解釈し、さらに厳密に適用しようとする
   ↓
6. 悪循環

大規模スケールの企業の設計をコピーしても、大規模スケールの問題を持っていなければ、その設計は「滑走路を裏庭に作っても飛行機は来ない」のと同じです。

過剰な設計と適切な設計の境界線

「クリーンアーキテクチャ自体が悪い」のではなく、適用の文脈を無視することが悪いのです。

抽象化が正当化される条件

条件抽象化すべきしなくてよい
実装が今後変わる確証はい(2 つ以上の実装が存在)いいえ(仮定に基づく)
テストでの差し替えモックが必要な外部依存内部ロジックのみ
チーム間の境界異なるチームが開発する層同一チームが全層を担当
ドメインの複雑さビジネスルールが豊富CRUD が中心

NestJS プロジェクトでの実用的な判断基準

Q: この外部サービスを将来変更する具体的な計画があるか?
  → No → Interface 不要。直接実装を使う
  → Yes → Interface を切る価値がある

Q: テストでモックする必要があるか?
  → No → DI 不要。直接インスタンス化でよい
  → Yes → NestJS の標準 DI を使う(文字列キーは使わない)

Q: このサービスクラスは何の単位で分割しているか?
  → DB テーブル単位 → 再考。ビジネスユースケース単位に変更
  → ユースケース単位 → 適切

Vibe Coding 時代に問い直す設計の意味

AI がコードを生成する Vibe Coding の時代において、この問題は新しい文脈を持ちます。

2026 年の現在、AI コーディングツールは「アーキテクチャ対応」へと進化しています。Claude Code や Cursor は既存のコードベースのパターンを認識し、それに従ったコードを生成します。つまり、過剰に抽象化されたプロジェクトでは、AI もその過剰な抽象化を踏襲してコードを生成してしまうのです。

逆に言えば、シンプルで明確な設計のプロジェクトでは、AI は素直に良いコードを生成できます。設計のシンプルさは、人間の保守性だけでなく、AI との協働効率にも直結する時代になっています。

一方で、Vibe Coding で生成されたコードは放置すると「構造的ドリフト」を起こすという指摘もあります。明示的な境界と強制力が、AI 生成コードにおいても構造的な一貫性を保つ鍵になります。問題は「アーキテクチャを適用するかどうか」ではなく、「どの程度の粒度で、何を目的に適用するか」です。

まとめ

  • 交換不可能なものを抽象化しない: 将来の交換を仮定した Interface は YAGNI 違反。2 つ以上の実装が存在しない限り、直接実装を使う
  • テーブル単位ではなくユースケース単位で分割する: DB スキーマに引きずられたクラス設計は、ドメインの構造を無視したアンチパターン
  • フレームワークの DI を活用する: NestJS の DI コンテナを無視して手動注入するのは、フレームワークの最大の利点を捨てる行為
  • 「クリーン」の名前に惑わされない: 命名の心理的圧力と同心円図の覚えやすさが、無批判な導入を後押ししている
  • カーゴカルトを識別する: 成功企業の構造をコピーしても、同じ問題を持っていなければ複雑さだけが残る
  • AI 時代の設計はシンプルさが武器: 過剰な抽象化は人間だけでなく AI の生産性も落とす。明確で素直な設計が、人間と AI の両方にとって最善

参考