InferDI を選ぶ理由
手動のコンストラクター注入は堅実な出発点です。依存が見え、TypeScript は個々の new 呼び出しを検査できます。グラフが数行の組み立てを超え、ライフタイム、スコープ、非同期の準備状態、モジュール、破棄も扱う段階で InferDI が役立ちます。
構成を一つのグラフとして検査する
各登録は新しいコンテナー型を返します。その型には既知のキー、サービス型、ライフタイム、非同期状態、不足しているスコープ入力が記録されます。後続の登録は、未知のキー、不適合なコンストラクター引数、重複キー、短いライフタイムを保持するシングルトンを受け付けません。
import { Container } from '@inferdi/inferdi'
class Database {
find(id: string) {
return { id }
}
}
class UserService {
constructor(readonly database: Database) {}
}
const app = new Container()
.registerClass('database', Database, [])
.registerClass('users', UserService, ['database'])
const users = app.get('users')この累積状態を指して、InferDI は「グラフが型になる」と説明しています。構成を複数ファイルへ分けても、モジュールが同じ要件を保ちます。
手動配線に加わるもの
手動の new UserService(database) も引数を検査します。ただし、登録のライフタイム、依存による非同期状態の伝播、再利用モジュールの要件、スコープが所有するキャッシュ資源までは追跡しません。InferDI は選択を明示したまま、グラフ全体の契約とライフサイクル管理を追加します。
組み立てコードは残ります。レビューする人は、選んだ実装と依存の順序をその場で確認できます。
実際の仕事をする小さなランタイム
コアの実行時依存はゼロです。デコレーター、metadata reflection、Proxy ベースの解決も使いません。本番バンドルには gzip 3 KiB 未満という強制予算があります。キャッシュヒットは 1 回の Map.get() から始まり、明示的な undefined は内部マーカーで表現されます。
型はコンパイル後に消えますが、コンテナーは登録、オブジェクト生成、キャッシュ、実行時診断、所有資源の破棄を行います。デフォルトモードは循環とライフタイムの診断を保持します。{ fast: true } は一部の検査を省く、固定グラフ向けの別契約です。
ビジネスロジックは通常のコードのまま
ドメインサービスが受け取るのは通常のコンストラクター引数や関数引数です。InferDI の import も resolver も必要ありません。コンテナーは構成ルートとライフサイクル境界に置き、そこで実装を選びスコープを開きます。
単体テストでは偽の依存を渡してサービスを直接作れます。InferDI を交換するなら登録とフレームワーク接続は書き換える必要がありますが、境界を守っていれば業務ルールはそのままです。構成ルートでファイル分割を確認できます。
明示的なスコープと所有権
スコープは HTTP リクエスト、ジョブ、テナント操作など、境界を持つ作業単位を表せます。コンテナーは自分が所有してキャッシュしたクラスとファクトリーの結果を破棄します。値、オーバーライド、スコープ入力、トランジェント結果は呼び出し側の所有です。親子コンテナーが互いを自動破棄することはありません。
アダプターはコアへフレームワーク固有の意味を加えずに、この規則を React、Fastify、Hono、Koa、Express、Elysia のライフサイクルへ接続します。
保証の境界
TypeScript は構造的型付けです。同じ形の値はブランド型を使わない限り交換可能で、any やキャストは検査を回避できます。広い実行時キーはグラフの精度を下げます。await 後の動的な依存読み取りは、同期呼び出しスタックによる循環追跡に参加できません。
InferDI はアーキテクチャを発見せず、循環を修復せず、すべてのリークを防がず、あらゆるアプリケーションを高速化するものでもありません。宣言した関係を検査し、実行時機構を小さく保ちます。次はクイックスタートか、実際の診断を載せた型安全性へ進んでください。
