LoginSignup
11
3

なぜインフラストラクチャ層の依存を外側に持っていくといいのかを説明する

Last updated at Posted at 2022-06-20

これは何

オニオンアーキテクチャやクリーンアーキテクチャなどでは、インフラストラクチャ層の依存関係を一番外側に持っていくことが多いです。
↓のような感じです。

最近「インフラストラクチャ層を外側に持っていく必要性が何かよくわからない」という話を聞いたので、必要性を説明できたらと思います。

そもそも設計でいうインフラストラクチャとは何を指しているか

「インフラストラクチャ層を一番外側に持っていく」、という言葉を聞いたときに、どういう印象を持ちますか?
僕は初めて聞いたとき「どういうこと?」となりました。
この言葉を聞いたときの僕のインフラのイメージは「サーバー」でした。
つまり、アプリケーションサーバーや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

上のコードだと、ApplicationServiceDBClientに依存しています。つまり依存関係的にDBClientApplicationServiceの内側に来てしまっています。

そこで、インフラストラクチャである、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への依存は無くなりました。

これがなぜ必要なのか?

前項までで、インフラストラクチャとは何か、依存を外側に持っていくとは何かを説明しました。
この項で、今回の記事の目的でもある「なぜいいなのか」を説明していきたいと思います。

簡単にいうと、「アプリケーションコード外のシステムを状況によって変えられるようになるため」です。
状況はおもに以下の二つの条件で変わります。

  1. アプリケーション環境の実行環境(production, development, test等)
  2. 使うサービス、システムの変更

1について一番わかりやすい例で言うと、テスト時のモックです。テストを行う際は、各テストケースにおいて正しいロジックで動作するかの検証を行うと思います。そこにDBやメーラーなど、アプリケーションコード外のシステムまで絡むとテストで気にしないといけないところが増えてしまいます。インフラストラクチャ層との依存を断ち切り、インフラストラクチャ層のコードをモックすることで、ロジック部分と、インフラコードが正しく動作しているかの検証を分けて行うことができるようになります。

2については、そもそも使っているシステムが変わる場合です。システムが変わればインフラストラクチャ層のコードも大きく変わることは多いとおもいます。このコードが依存の内側に入っていると、アプリケーションはそのままでは簡単には動きそうもありません。
しかし、インフラストラクチャ層を外に出しておき、インターフェースをしっかり作っておければ、インフラストラクチャ層のコードの修正のみに集中することができるようになります。

まとめ

設計の世界では、変化が少ない(概念に近いコード部分)ものの依存をできるだけ内側に、変化の大きい(具体的なシステムに近いコード部分)の依存を外側に持っていくことで、変更に対して閉じているコードを作ることを目指します。

インフラストラクチャ層を外側に持っていくことの意義をこの記事で感じていただけたら幸いです。

11
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
3