Skip to content

構成ルート

構成ルートは、具体的な実装を選んで接続するアプリケーション境界です。ドメインコードは必要な契約を示し、インフラストラクチャが実装を提供します。InferDI は組み立てコードだけに現れます。

ドメインの契約

ユースケースが依存するインターフェースはドメイン側に置きます。このファイルはコンテナーもフレームワークも import しません。

ts
export interface UserStore {
  findName(id: string): Promise<string | undefined>
}

export class GetGreeting {
  constructor(private readonly users: UserStore) {}

  async execute(id: string) {
    const name = await this.users.findName(id)
    return name === undefined ? 'Hello, stranger' : `Hello, ${name}`
  }
}

インフラストラクチャ実装

インフラストラクチャがドメイン契約を実装します。実際のアダプターならデータベースクライアントを使いますが、この例では呼び出しを小さく保っています。

ts
import type { UserStore } from './domain'

export class PostgresUserStore implements UserStore {
  constructor(private readonly dsn: string) {}

  async findName(id: string) {
    console.info(`query ${this.dsn} for ${id}`)
    return id === '42' ? 'Ada' : undefined
  }
}

アプリケーションの構成

InferDI を import するのはこのファイルだけです。PostgresUserStore を選び、DSN を渡し、GetGreeting へ接続します。

ts
import { Container } from '@inferdi/inferdi'
import { GetGreeting } from './domain'
import { PostgresUserStore } from './infrastructure'

export const container = new Container()
  .registerValue('dsn', 'postgres://localhost/app')
  .registerClass('users', PostgresUserStore, ['dsn'])
  .registerClass('greeting', GetGreeting, ['users'])

登録タプルは各コンストラクターと照合されます。GetGreetingPostgresUserStore を変更すると、古くなった配線がこの境界でエラーになります。

境界でサービスを使う

HTTP ルート、CLI コマンド、キューコンシューマーがトップレベルのサービスを解決します。業務処理は通常のメソッドを呼ぶだけです。

ts
import { container } from './container'

export async function handleUser(id: string) {
  return container.get('greeting').execute(id)
}

処理にリクエストやジョブの入力が必要なら、ここで子スコープを開きます。同じ境界で破棄するか、フレームワークアダプターにライフサイクルとの接続を任せてください。

ドメインを直接テストする

単体テストはコンテナーを作りません。偽の UserStore を、本番と同じ GetGreeting クラスへ渡します。

ts
import { GetGreeting, type UserStore } from './domain'

const fakeUsers: UserStore = {
  async findName(id) {
    return id === '42' ? 'Ada' : undefined
  }
}

const service = new GetGreeting(fakeUsers)
const result = await service.execute('42')

if (result !== 'Hello, Ada') {
  throw new Error(`Unexpected greeting: ${result}`)
}

実際の構成グラフを動かしつつ 1 実装だけ差し替える統合テストでは .override() を使えます。単一のドメインサービスなら直接構築する方が明快です。続きはテストとオーバーライドを参照してください。