これは何
オニオンアーキテクチャやクリーンアーキテクチャなどでは、インフラストラクチャ層の依存関係を一番外側に持っていくことが多いです。
↓のような感じです。
最近「インフラストラクチャ層を外側に持っていく必要性が何かよくわからない」という話を聞いたので、必要性を説明できたらと思います。
そもそも設計でいうインフラストラクチャとは何を指しているか
「インフラストラクチャ層を一番外側に持っていく」、という言葉を聞いたときに、どういう印象を持ちますか?
僕は初めて聞いたとき「どういうこと?」となりました。
この言葉を聞いたときの僕のインフラのイメージは「サーバー」でした。
つまり、アプリケーションサーバーやDBサーバーなどです。
これを一番外側に持っていく、ってどういうこと!?というのが僕の最初の感想でした。
インフラストラクチャ層が示している「インフラストラクチャ」は正確にいうと、「アプリケーション外のシステムにアクセスするためのコード」のことです。
具体的なものは以下の通りです。
- DBクライアント
- KVSクライアント
- メールクライアント
- APIクライアント
- etc
設計の話をしているときは、インフラストラクチャの名前が出てきたときはそういう認識でいると色々なものが理解しやすいと思います。
インフラストラクチャ層を一番外側に持っていくとはどういうことか
前項で説明した通り、設計の世界で話すインフラストラクチャとは、「アプリケーション外のシステムにアクセスするためのコード」です。
インフラストラクチャのコードには、たいていアプリケーション層の中で参照する必要があります。
DBへの永続化やメールの送信、APIを叩くなどは、アプリケーション層で行われるためです。
これを愚直に書くと以下のようになると思います。
今回はユーザー一覧を取得する処理を想定します。(ライブラリなどは擬似です)
def UserRepository
def find_all
DBClient.execute(`SELECT * FROM users`)
end
end
def ApplicationService
def initialize
@user_repository = UserRepository.new
end
def get_users
@user_respository.find_all
end
end
def ApplicationController
def index
service = ApplicationService.new
@users = service.get_users
end
end
上のコードだと、ApplicationService
はDBClient
に依存しています。つまり依存関係的にDBClient
はApplicationService
の内側に来てしまっています。
そこで、インフラストラクチャである、DBClient
を依存関係の外側に持っていきます。
使う手法は依存性の注入です。
def UserRepository
def find_all
DBClient.execute(`SELECT * FROM users`)
end
end
def ApplicationService
def initialize(user_repository)
@user_repository = user_repository
end
def get_users
@user_respository.find_all
end
end
def ApplicationController
def index
user_repository = UserRepository.new
service = ApplicationService.new(user_repository)
@users = service.get_users
end
end
このようにすることで、ApplicationService
からのUserRepository
への依存は無くなりました。
これがなぜ必要なのか?
前項までで、インフラストラクチャとは何か、依存を外側に持っていくとは何かを説明しました。
この項で、今回の記事の目的でもある「なぜいいなのか」を説明していきたいと思います。
簡単にいうと、「アプリケーションコード外のシステムを状況によって変えられるようになるため」です。
状況はおもに以下の二つの条件で変わります。
- アプリケーション環境の実行環境(production, development, test等)
- 使うサービス、システムの変更
1について一番わかりやすい例で言うと、テスト時のモックです。テストを行う際は、各テストケースにおいて正しいロジックで動作するかの検証を行うと思います。そこにDBやメーラーなど、アプリケーションコード外のシステムまで絡むとテストで気にしないといけないところが増えてしまいます。インフラストラクチャ層との依存を断ち切り、インフラストラクチャ層のコードをモックすることで、ロジック部分と、インフラコードが正しく動作しているかの検証を分けて行うことができるようになります。
2については、そもそも使っているシステムが変わる場合です。システムが変わればインフラストラクチャ層のコードも大きく変わることは多いとおもいます。このコードが依存の内側に入っていると、アプリケーションはそのままでは簡単には動きそうもありません。
しかし、インフラストラクチャ層を外に出しておき、インターフェースをしっかり作っておければ、インフラストラクチャ層のコードの修正のみに集中することができるようになります。
まとめ
設計の世界では、変化が少ない(概念に近いコード部分)ものの依存をできるだけ内側に、変化の大きい(具体的なシステムに近いコード部分)の依存を外側に持っていくことで、変更に対して閉じているコードを作ることを目指します。
インフラストラクチャ層を外側に持っていくことの意義をこの記事で感じていただけたら幸いです。