今年は新しいリポジトリで開発する機会があり、先輩方から教えてもらいながらReact、testなどの分野の知識を増やすことができた1年でした。
その中で一番難しかったのがアーキテクチャです。
自分がアーキテクチャの知識ゼロだったため、様々な概念を1つずつ理解する必要がありました。
今回はクリーンアーキテクチャにも登場する「依存性逆転の法則」について実装例を用いて整理してみました。
「依存性逆転の法則」とは
上位レイヤー(ビジネスロジック)は下位レイヤー(データアクセス)の具体的な実装に依存せず、抽象(インターフェース)に依存すべきという原則。
どうやらビジネスロジックはデータアクセスではなく、インターフェースに依存するようです。
正直イメージがわかないので実装例を見てみます。
😒 従来の依存
普通に実装していると「ビジネスロジック」が「データアクセス」を参照している(依存している)状態になります。
ビジネスロジック (上位レイヤー)
↓
データアクセス(下位レイヤー)
下記のような例で確認してみます。
- idを使ってユーザーの名前を取得する処理
- データアクセス(インフラ層)で外部APIからユーザー名を取得
- ビジネスロジック(ユースケース層)ではユーザー名の前に"Hello"をつけるような処理
// 1. データアクセス(下位レイヤー)
class ApiUserNameRepository {
async getUserName(id: string): Promise<string> {
// const response = await fetch(...); // 実際はAPI呼び出し
return "Taro from API";
}
}
// 2. ビジネスロジック(上位レイヤー)
class UserInteractor {
// ⚠️データアクセス(下位レイヤー)に依存
constructor() {
this.repository = new ApiUserNameRepository()
}
async execute(id: string): Promise<string> {
const name = await this.repository.getUserName(id);
return `Hello, ${name}`;
}
}
// --- 実行コード(Main) ---
const interactor = new UserInteractor();
console.log(await interactor.execute("123")); // 出力: Hello, Taro from API
✅「依存性逆転の法則」適用後の場合
依存性逆転の法則が適用されると、ビジネスロジックはデータアクセスに依存しない形となります。
またデータアクセス層がインターフェースを実装(implements)することで、矢印の向きが内側に向きます。
ビジネスロジック (上位レイヤー)
↓
インターフェース
↑(実装 / implements)
データアクセス(下位レイヤー)
先ほどの例を「依存性逆転の法則」に適用させてみます。
// 1. 抽象(インターフェース)を定義
interface IUserNameRepository {
getUserName(id: string): Promise<string>;
}
// 2. ビジネスロジック(上位レイヤー)
// データアクセスのクラスではなく、【インターフェース(抽象)】に依存している
class UserInteractor {
constructor(private repository: IUserNameRepository) {}
async execute(id: string): Promise<string> {
const name = await this.repository.getUserName(id);
return `Hello, ${name}`;
}
}
// 3. データアクセス(下位レイヤー)
class ApiUserNameRepository implements IUserNameRepository {
async getUserName(id: string): string {
// const response = await fetch(...); // 実際ではAPI呼び出し
return "Taro from API";
}
}
// --- 実行コード(Main) ---
// ここで初めて「どの実装を使うか」を決めて注入する
// DI(Dependency Injection / 依存性の注入)
const repository = new ApiUserNameRepository();
const interactor = new UserInteractor(repository);
console.log(await interactor.execute("123")); // 出力: Hello, Taro from API
なんのメリットがあるのか?
-
テスト容易:
「本物のAPI」の代わりにモックデータを渡すだけで、ビジネスロジック単体のテストができます。 -
保守性:
API実装変更してもビジネスロジックは不変なので修正箇所を限定できます。
これは新しいRepository実装を追加した時も同様です。
クリーンアーキテクチャとの関係
この「依存性逆転の法則」はクリーンアーキテクチャでも使われています。
クリーンアーキテクチャにおける依存の方向は、
- 常に【外側→内側】
- 頻繁に改修されるデータアクセスやUI部分が外側、ビジネスロジックが内側
となってます。
データアクセスやUI(外側)→ ビシネスロジック(内側)
もし、ビジネスロジックがデータアクセスやUIに依存している場合(内側→外側)になってしまうと
データアクセスやUIを変更する度にビジネスロジックも改修範囲になってしまいます。
これを避けるために先ほど記載した「依存性逆転の法則」が使われます。
先ほどの例のようにインターフェースを経由することで、ビジネスロジックはデータアクセスやUIに依存しない状態となります。
さいごに
トラストバンクでは、一緒に働く仲間を絶賛募集しています。
少しでも気になった方は、是非ご連絡ください。