はじめに
クリーンアーキテクチャという言葉を聞くと、最初はレイヤー構成やフォルダ構成の話に見えるかもしれません。
もちろん、実際の開発ではController、UseCase、Repositoryのように役割を分けて実装することがあります。
ただ、最初から細かい構成を覚えようとすると、何のために分けているのかが分かりにくくなります。
まずは、クリーンアーキテクチャを「変更に強いコードにするために、業務ルールと技術的な手段を分ける考え方」として捉えると理解しやすいです。
システムはあとから変わる
システムは、作って終わりではない。
あとから画面の表示内容が変わったり、DBのテーブル構成が変わったり、通知方法がメールからSlackに変わったりする。
そのとき、コードの中で画面の都合、DBの都合、業務ルール、外部サービスの処理がぐちゃっと混ざっていると、少し変更しただけで別の場所まで壊れやすくなる。
たとえば、画面を少し変えただけなのに業務ロジックまで影響を受けたり、DBの保存方法を変えたいだけなのに複数の処理を修正する必要が出たりする。
クリーンアーキテクチャでは、こうした変更に対応しやすくするために、大事な業務ルールと、あとから変わりやすい技術の都合を分けて考える。
カレー作りで考えるルールと手段
たとえば、「カレーを作る」という業務があるとする。
このとき大事なルールは、下記。
- カレーには具材が必要
- 火を通す必要がある
- 味付けする必要がある
- 食べられる状態にする必要がある
一方で、次のようなものは手段。
- 鍋を使うのか
- フライパンを使うのか
- IHなのかガスなのか
- お皿に盛るのか弁当箱に入れるのか
「火を通す」「味付けする」「食べられる状態にする」は、カレーを作るうえで大事なルールである。
でも、鍋を使うかフライパンを使うかは手段。
手段は変わる可能性がある。
システム開発でも、これと同じように「本当に守るべきルール」と「実現するための手段」を分けて考えられる。
顧客登録で考える業務ルールと技術的な手段
システムの例として、「顧客を登録する」という処理を考える。
このとき大事なのは、下記のような業務ルールである。
- 顧客名は必ず必要
- 電話番号の形式を確認する
- 同じ顧客がすでに登録されていないか確認する
- 登録できたら顧客として扱える
一方で、次のようなものは技術的な手段。
- PostgreSQLに保存する
- 画面はReactで作る
- APIはSpring Bootで作る
- メール通知する
- CSVを出力する
PostgreSQLやReactやSpring Bootはシステムを作るうえで必要。
ただし、顧客登録の中心にあるのは「どんな顧客なら登録できるのか」という業務ルールである。
クリーンアーキテクチャでは、この業務ルールを中心に置き、DBや画面や外部サービスは外側に置くと考える。
業務ルールと技術の都合が混ざると何が困るのか
たとえば、顧客登録処理を次のように書いたとする。
async function registerCustomer(input) {
// 画面から来た値をそのまま受け取る
// DBの形式に合わせて加工する
// 顧客登録のルールもここに書く
// PostgreSQLに保存する
// メールも送る
}
この状態だと、画面の都合、DBの都合、業務ルール、通知処理が全部混ざっている。
あとから「メールではなくSlack通知にしたい」「DBのテーブル構造を変えたい」「顧客登録ルールを変えたい」となったとき、同じ関数を触ることになる。
一見まとまっていて分かりやすく見えるが、変更が入ったときに影響範囲が広がりやすい。
何をしたいのかを業務の言葉で考える
まずは、技術的な実装の前に「何をしたいのか」を業務の言葉で考える。
顧客を登録する処理
↓
顧客として正しいか確認する
↓
顧客を保存する
↓
必要なら通知する
この流れで見ると、中心にあるのは「顧客を登録する」という業務の処理だと分かる。
DBにどう保存するか、どの画面から呼ばれるか、どの通知サービスを使うかは、その業務を実現するための手段である。
大事なのは、業務ルールがDBや画面の事情に振り回されないようにすること。
DBに直接依存しない書き方を考える
業務ロジックの中で、ついDB処理を直接呼びたくなる。
function registerCustomer(input) {
const customer = new Customer(input.name, input.tel);
// 業務ロジックの中でPostgreSQL保存を直接呼ぶ
postgresCustomerRepository.save(customer);
}
この書き方では、顧客登録の処理がPostgreSQLに依存している。
つまり、顧客登録という業務の処理の中に、「PostgreSQLへ保存する」という具体的な技術の都合が入り込んでいる。
そこで、次のように考える。
function registerCustomer(input, customerRepository) {
const customer = new Customer(input.name, input.tel);
customerRepository.save(customer);
}
この書き方では、registerCustomerは「顧客を保存できるもの」を使っているだけである。
customerRepositoryがPostgreSQLなのか、MySQLなのか、テスト用の仮実装なのかまでは知らない。
顧客登録の処理から見ると、大事なのは「顧客を保存できること」であって、「PostgreSQLに保存すること」そのものではない。
こうすることで、業務ロジックが特定のDBに引っ張られにくくなる。
クリーンアーキテクチャで意識したいこと
クリーンアーキテクチャで大事なのは、業務ルールを中心に置くこと。
画面やDBから考えるのではなく、「このシステムで本当に守るべき業務ルールは何か」から考える。
顧客登録であれば、「どんな顧客なら登録できるのか」「重複をどう判断するのか」「誰が登録できるのか」といった部分が中心になる。
反対に、画面の表示方法、DBの保存方法、通知サービス、CSV出力などは外側の都合として扱う。
これらを完全に無視するわけではない。
ただし、業務ルールの中心に置きすぎない。
技術は変わる可能性がある。
PostgreSQLを使っていたものが別のDBに変わるかもしれないし、メール通知がSlack通知に変わるかもしれない。
そのたびに業務ルールまで大きく修正する状態だと、変更に弱いコードになってしまう。
分けすぎると逆に読みにくくなる
クリーンアーキテクチャを学ぶと、最初は何でもかんでも分けたくなる。
しかし、小さい機能で最初から細かく分けすぎると、逆に読みにくくなることがある。
たとえば、ほとんど変更されない小さな処理まで厳密に分けると、ファイルを行ったり来たりするだけで処理の流れが追いにくくなる。
そのため、まずは次のような部分から分けると考えやすい。
- 変更されそうなところ
- テストしたいところ
- 業務ルールとして大事なところ
- 外部サービスやDBに強く依存しているところ
クリーンアーキテクチャは「必ずこういうフォルダ構成にしなさい」というルールではない。
変更に強くするために、何を中心に置き、何を外側に分けるべきかを考えるためのもの。
まとめ
クリーンアーキテクチャは、難しいレイヤー構成を暗記する話ではないということを理解しました。
変更に強いコードにするために、業務ルールと技術的な手段を分ける考え方です。
業務ルールはシステムの中心に置き、DB、画面、通知、外部サービスなどの技術的な都合は外側に置く。
この考え方があると、画面が変わっても、DBが変わっても、通知方法が変わっても、業務ルールを守りやすくなることが分かりました。
最初から完璧にレイヤーを分ける必要はなく、まずは「これは業務ルールなのか、技術的な手段なのか」を分けて考えるところから始めることをやっていきます。