Skip to content

クイックスタート

まずは通常のクラス 2 つと、明示的な構成チェーンで小さなグラフを作ります。リクエストスコープは基本を確認してから追加します。

インストール

bash
pnpm add @inferdi/inferdi
bash
npm install @inferdi/inferdi
bash
yarn add @inferdi/inferdi

グラフを構築する

ts
import { Container } from '@inferdi/inferdi'

export class Logger {
  info(message: string) {
    console.info(message)
  }
}

export class UserService {
  constructor(private readonly logger: Logger) {}

  find(id: string) {
    this.logger.info(`user=${id}`)
    return { id }
  }
}

export const root = new Container()
  .registerClass('logger', Logger, [])
  .registerClass('users', UserService, ['logger'])

export const users = root.get('users')

UserService は InferDI をインポートしません。構成コードが Logger を選び、登録に名前を付け、コンストラクター引数の順序を記述します。返された root の型には両方のサービスが含まれます。

コンストラクターを変更してグラフを更新し忘れると、アプリケーションを組み立てる場所でエラーになります。

ts
import { 
Container
} from '@inferdi/inferdi'
class
Logger
{
info
(
message
: string) {}
} class
UserService
{
constructor(readonly
logger
:
Logger
, readonly
region
: string) {}
} new
Container
()
.
registerClass
('logger',
Logger
, [])
.
registerClass
('users',
UserService
, ['logger'])

これがグラフ型状態の実際の働きです。登録がコンテナー型を絞り込み、後続の操作は宣言済みグラフに適合しなければなりません。

サービスを解決する

root.get('users')UserService を同期的に返します。デフォルトのライフタイムは singleton なので、2 回目以降はキャッシュ済みインスタンスを返します。

リクエストデータには短い境界が必要です。スコープ入力として宣言し、子スコープを開くときに渡します。

ts
import { Container } from '@inferdi/inferdi'

type RequestContext = {
  requestId: string
}

class RequestLog {
  constructor(readonly request: RequestContext) {}

  dispose() {
    console.info(`closed ${this.request.requestId}`)
  }
}

const root = new Container()
  .declareScopeInputs<{ request: RequestContext }>()
  .registerClass('requestLog', RequestLog, ['request'], 'scoped')

export async function handle(request: RequestContext) {
  const scope = root.createScope({ request })

  try {
    return scope.get('requestLog')
  } finally {
    await scope.dispose()
  }
}

リクエスト処理が失敗しても、finally が子スコープを閉じます。scope.dispose() はスコープ所有の RequestLog を破棄し、渡された request はアプリケーション所有のままです。TypeScript ツールチェーンが Explicit Resource Management を扱える場合は、同じ処理を await using でも書けます。

ライフタイムを選ぶ

ライフタイムインスタンス生成キャッシュ所有者破棄する側
singleton登録所有者ごとに 1 回ルートまたは登録所有者そのコンテナー
scoped解決するスコープごとに 1 回子スコープそのスコープ
transient解決のたびなし呼び出し側

registerValue.override()、スコープ入力で渡した値もアプリケーション所有です。シングルトンはスコープドまたはトランジェントなサービスへ直接依存できません。InferDI は型で拒否し、デフォルトの実行時契約でも再検査します。

次に読むページ

InferDI を選ぶ理由で設計上の判断を確認し、型安全性でグラフ検査を詳しく見てください。スコープとリソース破棄は所有権を、フレームワークアダプターはアプリケーションのライフサイクルとの接続を説明します。