はじめに
注文を登録する処理の中に、画面入力の変換、金額計算、SQL、決済サービスの呼び出し、メール送信まで書かれているとします。最初は一か所で処理を追えますが、データベースや外部サービスを変更するたびに、注文の業務処理まで修正することになります。
クリーンアーキテクチャは、層の名前やフォルダ構成をそろえるための方式ではありません。変更から守りたい業務ルールを外部技術から分離し、ソースコード上の依存関係を業務側へ向けるための考え方です。この記事では、基本原則と注文登録処理への適用例、導入時の注意点を解説します。
この記事で分かること
- クリーンアーキテクチャの目的
- 業務ルールと外部技術の分け方
- 依存関係を業務側へ向ける意味
- 制御の流れとコード上の依存方向の違い
- 適用によって得られる効果と増える複雑さ
クリーンアーキテクチャとは
クリーンアーキテクチャとは、業務ルールをUI、データベース、外部サービス、フレームワークなどの外部技術から分離し、技術の変更が業務処理へ波及しにくくする設計の考え方です。
中心となる原則は、ソースコード上の依存関係を業務ルールの側へ向けることです。内側には業務上の判断や処理を置き、外側には画面、通信、保存、フレームワークなどの実現手段を置きます。内側のコードは、外側で採用した製品名、データ形式、操作手順を直接知りません。
同心円で示される層の数や名称は固定ではありません。重要なのは図を再現することではなく、何を変更から守るのかを決め、外部の詳細が内側へ入り込まない依存方向を維持することです。
ただし、クリーンアーキテクチャを採用しただけで設計品質が高くなるわけではありません。分離の効果と維持コストが要求や制約に合うかは、設計品質とは?要求と制約から良い設計を判断する考え方で扱う観点から判断します。
外部技術へ直接依存すると変更が広がる
注文登録処理がSQL、決済サービス固有のAPI、メール送信ライブラリを直接呼び出していると、業務ルールと技術的な実装が同じ場所へ混在します。例えば決済サービスを変更するだけでも、注文を確定できる条件や失敗時の扱いを含む注文処理全体を確認しなければなりません。
テストにも実際のデータベースや外部サービスが必要になり、業務ルールだけを確認しにくくなります。問題は外部技術を使うことではなく、変更理由の異なる業務処理と外部技術が直接結び付き、技術の変更が内側まで波及することです。
変更の影響を必要な範囲へ限定する理由は、良い設計より「変更できる設計」が重要な理由で解説しています。
依存関係を業務ルールの側へ向ける
業務処理が必要とするのは、特定のSQLやAPIを実行することではありません。注文登録を例にすると、「注文を保存する」「決済する」「利用者へ通知する」という役割が必要です。
そこで、業務ルールの側で必要な役割を境界として定義し、データベース、決済サービス、メール送信などの外側の実装がその境界を実現します。業務処理と外部実装の両方が業務側の境界へ依存することで、外部技術を変更しても内側の処理を維持しやすくなります。
この考え方はSOLID原則の依存性逆転と共通します。依存性逆転の意味は、SOLID原則とは?5つの原則と設計での使い方をわかりやすく解説で説明しています。
制御の流れとコード上の依存方向は同じではない
実行時には、画面から注文登録処理へ入り、データベースや決済サービスへ処理が進みます。
画面 → 注文登録 → データベース・決済サービス
一方、ソースコード上では、注文登録側が保存や決済の境界を定義し、外側の実装がその境界へ依存する形にできます。
注文登録側の境界 ← データベース・決済サービスの実装
処理の実行先が外側にあっても、内側のコードが外側の具体的なクラスを参照する必要はありません。境界を内側に置き、外側の実装を実行時に組み合わせることで、制御の流れと反対向きの依存関係を作れます。
境界を越えるデータも外部形式から切り離す
依存はクラスの参照だけで生じるわけではありません。データベースの行オブジェクトやWebフレームワーク固有のリクエストを内側へ渡すと、型や項目を通じて外部技術への依存が入り込みます。
| 位置 | 主な内容 | 境界で意識すること |
|---|---|---|
| 内側 | 業務ルール、ユースケース、業務側が必要とする役割 | SQL、HTTP、画面項目名を直接扱わない |
| 境界 | 入出力に必要なインターフェースと単純なデータ | 外部形式を内側が扱いやすい形式へ変換する |
| 外側 | UI、データベース、外部API、フレームワーク | 内側の境界を実装し、外部形式との変換を担当する |
境界を設けても、内側が外部のデータ形式や例外、操作手順まで知っていれば依存は残ります。依存の強さだけでなく内容を評価する考え方は、結合度とは?高い・低いの違いと設計への影響をわかりやすく解説を参照してください。
注文登録処理へ適用する例
改善前の注文登録処理では、金額計算の後にSQLを組み立て、決済サービス固有の要求形式を作り、メール送信ライブラリを呼び出しているとします。改善後は、業務処理が業務上の順序と判断を担当し、外部技術の具体的な操作を境界の外へ移します。
| 対象 | 改善前 | 改善後 |
|---|---|---|
| 注文保存 | 注文処理がSQLとテーブル構造を知る | 注文処理は「注文を保存する」役割だけを利用する |
| 決済 | 注文処理が特定サービスのAPI形式を知る | 注文処理は「決済する」役割だけを利用する |
| 通知 | 注文処理がメール送信ライブラリを直接呼ぶ | 注文処理は「利用者へ通知する」役割だけを利用する |
テストでは、保存や決済の役割をテスト用実装へ置き換えられます。外部サービスを動かさずに、注文を確定できる条件、失敗時の扱い、処理順序などの業務ルールを確認できます。
重要なのはクラスやファイルを増やすことではありません。外部技術の変更から守る価値がある業務処理を特定し、その境界だけを設けることです。
クリーンアーキテクチャのメリットと代償
期待できる効果
- データベースや外部サービスの変更が業務処理へ波及しにくい
- 業務ルールを外部環境から切り離してテストしやすい
- 画面、保存方法、通信方法を交換しやすい
- 業務処理と技術固有のコードを別々に読み取れる
- 変更箇所と確認範囲を把握しやすい
これらは層名をそろえた結果ではなく、内側が外側の詳細を知らない状態を維持した結果です。
増える複雑さ
境界を設けると、インターフェース、データ変換、実装の組み合わせが増えます。単純な処理でも複数のファイルを移動することになり、処理の流れを理解する負担が増える場合があります。
変更やテスト上の必要性が低い部分まで分離すると、得られる効果より構造を維持するコストが大きくなります。小規模なツールや短期間だけ使う機能では、直接的な構造の方が要求と制約に合うこともあります。
クリーンアーキテクチャを適用する手順
既存システムへ適用する場合は、同心円の層を一度に作るのではなく、変更の影響が大きい処理から次の順で進めます。
- 変更から守りたい業務ルールを特定する
- 業務処理に混在するSQLや外部APIなどを見つける
- 業務側が必要とする役割を言葉で定義する
- 役割を境界として置き、外側へ具体的な実装を移す
- 外部実装を置き換えても業務処理が変わらないか確認する
既存コードの動作を維持しながら境界を作る作業は、小さな変更に分けて進めます。安全な改善の進め方は、リファクタリングとは?動作を変えずにコードを改善する考え方で解説しています。
適用するときの注意点
- 層の名前から設計を始めない: 最初に守る業務ルールと変更される外部詳細を確認します。
- すべてのクラスへインターフェースを作らない: 変更や置換の境界として効果がある場所へ限定します。
- 外部のデータ形式を内側へ持ち込まない: DB行やフレームワーク固有の型は境界で変換します。
- 業務ルールをフレームワークへ合わせない: 外部技術の都合が業務上の判断を決める状態を避けます。
- 将来の可能性だけで境界を増やさない: 現在の要求と具体的な変更を基準にします。
- 全面的な作り直しを前提にしない: 影響の大きい部分から段階的に分離します。
まとめ
クリーンアーキテクチャとは、業務ルールをUI、データベース、外部サービス、フレームワークなどの外部技術から分離し、ソースコード上の依存関係を業務側へ向ける設計の考え方です。
重要なのは、同心円の層名やフォルダ構成をそろえることではありません。変更から守る業務処理を特定し、外部技術が業務側の境界へ依存する形を作ります。一方で境界や変換処理を増やすほど構造は複雑になるため、変更頻度、影響範囲、テストの必要性を見て、効果がある部分から取り入れることが重要です。

コメント