Go のコードと共に依存性逆転を整理する
自分用メモ
「Clean Architecture 達人に学ぶソフトウェアの構造と設計」を読んでいる。
本書で書かれている依存関係の逆転を自分なりに整理。
## 依存性が逆転するとはどういうことか
モジュール間の制御の流れと依存関係が逆転すること。 例えば次のようなモジュールとインターフェースの依存関係を考える。
このときモジュール間の制御の流れは以下のようになる。
main -> RegisterArticle -> (ArticleDB).Insert -> ArticleStoreMySQL.Insert
しかし、モジュール間の依存関係は以下のようになっている。
main -> RegisterArticle -> (ArticleDB).Insert <- ArticleStoreMySQL.Insert
(ArticleDB).Insert
、ArticleStoreMySQL.Insert
間の依存関係は制御の流れと反対である。
これを依存関係の逆転という。
## 依存性逆転のモチベーション
ポリモーフィズム以前では、制御の流れと依存関係を合わせる必要があり、モジュール設計に制約が生じてしまっていた。 しかし、依存関係の逆転を行うことによって任意の依存関係を逆転させる事ができるため、この制限がなくなりモジュール設計がアーキテクトの思うがままになる。
例えば、ビジネスロジックを扱うモジュールとデータベースを扱うモジュールの関係性を考える。 ビジネスロジックでは本当はどのようなデータベースにデータが保存されるのか、例えばそれが Relational DB なのか NoSQL なのかには興味がなく意識をする必要がない。 しかし、制御の流れに沿ってビジネスロジックのモジュールが具体的なデータベースを扱うモジュールに依存してしまうと、 データベースを変える度にビジネスロジックのコードを変更しなくてはならない。
ここに対して依存関係の逆転を行うと、ビジネスロジックのモジュールが具体的なデータベースを扱うモジュールに依存するのではなく、インターフェースに依存するようになる。 こうすると、データベースを変える際にもインターフェースを実装しているモジュールを変更するだけで済み、ビジネスロジックのコードは変更しなくて良くなる。
## 依存性の逆転を Go のコードで理解する
### 依存性が逆転できていない状態
モジュールの依存関係が制御の流れと一致している状態。
例えば、次のコードのように制御の流れは main 関数から上位モジュールが呼ばれて、さらにそのなかで下位モジュールが呼ばれていく。 このとき main 関数は RegisterArticle というビジネスロジックモジュールを呼び出しており、依存している。 さらにビジネスロジックモジュールはデータベースを扱うモジュール InsertArticleToMySQL を呼び出しており、依存している。
package main
func main() {
RegisterArticle("バズる記事", "バズる内容")
}
// ビジネスロジックモジュール
func RegisterArticle(title string, body string) {
// クールなビジネスロジック
// 1. 登録前に title, body のスペルチェックを行う
// 2. body を HTML に変換する
// データベースを扱うモジュールを呼び出す
InsertArticleToMySQL(title, body)
}
// データベースを扱うモジュール
func InsertArticleToMySQL(title string, body string) {
// MySQL に対して INSERT クエリを実行し記事を追加
}
### 依存性が逆転した状態
この依存関係を逆転するためにポリモーフィズムを利用する。 Go では interface を利用することでポリモーフィズムを実現できる。
ビジネスロジックモジュールである、 RegisterArticle は ArticleDB というインターフェースに依存している。 これによりビジネスロジックは DB がどのようなものなのか、その詳細は意識せずにただ、インターフェースを呼び出せば良い状態になっている。
データベースを扱うモジュールも ArticleDB というインターフェースを依存している(知っている状態)になっている。
package main
func main() {
RegisterArticle(&ArticleStoreMySQL{}, "バズる記事", "バズる内容")
}
// ビジネスロジックモジュール
func RegisterArticle(db ArticleDB, title string, body string) {
// クールなビジネスロジック
// 1. 登録前に title, body のスペルチェックを行う
// 2. body を HTML に変換する
// データベースへの保存を呼び出す
db.Insert(title, body)
}
// データベースを扱うモジュールのインターフェース
type ArticleDB interface {
Insert(title string, body string)
}
// データベースを扱うモジュール
type ArticleStoreMySQL struct {}
func (r *ArticleStoreMySQL) Insert(title string, body string) {
// MySQL に対して INSERT クエリを実行し記事を追加
}
このとき、 制御の流れを考えるとのようになっているが、
main -> RegisterArticle -> (ArticleDB).Insert -> ArticleStoreMySQL.Insert
依存関係は
main -> RegisterArticle -> (ArticleDB).Insert <- ArticleStoreMySQL.Insert
となっている。
繰り返しになるが、このように制御の流れと依存関係が逆転している状態を依存関係の逆転と呼ぶ。