Workflow di composition
Skill marzun9620/agent_skills/workflow/skills/workflow-di-composition
Layer registration in the DI layer (Composition Root). Covers adding to appLayer.ts, Layer dependency order, how to build test Layers, and using ManagedRuntime. Triggers: DI, Layer registration, appLayer, Composition Root, Layer.mergeAll, Layer.provide, test Layer, ManagedRuntime, dependency injection, service registration.From its SKILL.md
npx -y skills add marzun9620/agent_skills --skill workflow-di-compositionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.3 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it
DI Composition(Composition Root)手順
ADR-0004 準拠。src/di/appLayer.ts が唯一の Composition Root。
Layer 依存順
Level 1: Packages(config, db, observability, time)
↓
Level 2: DrizzleClient(DB接続)
↓
Level 3: Infrastructure(Repository, Gateway, Service)
↓
Level 4: AppLayer(全 Layer を統合)
ファイル: apps/datahub/src/di/appLayer.ts
// Level 1: Packages
const PackagesLayer = Layer.mergeAll(
AppConfig.Live,
SqlClient.Live,
ObservabilityLive,
CurrentTime.Live,
).pipe(Layer.provideMerge(AppConfig.Live));
// Level 2: Database
const DrizzleLayer = DrizzleClient.Live.pipe(
Layer.provide(PackagesLayer),
);
// Level 3: Infrastructure
const InfraLayer = Layer.mergeAll(
UserRepositoryLive,
IdGeneratorLive,
// ← 新しい Repository/Gateway/Service はここに追加
).pipe(Layer.provide(DrizzleLayer));
// Level 4: Full application
const AppLayer = InfraLayer.pipe(
Layer.provideMerge(DrizzleLayer),
Layer.provideMerge(PackagesLayer),
);
type AppLayer = Layer.Layer.Success<typeof AppLayer>;
新しい Layer の追加手順
1. Repository を追加する場合
// InfraLayer に追加
const InfraLayer = Layer.mergeAll(
UserRepositoryLive,
IdGeneratorLive,
FacilityRepositoryLive, // ← 追加
).pipe(Layer.provide(DrizzleLayer));
- DrizzleClient に依存する →
DrizzleLayerを provide 済みの InfraLayer に追加
2. Gateway を追加する場合
// InfraLayer に追加(AppConfig から API キーを取得するため PackagesLayer も必要)
const InfraLayer = Layer.mergeAll(
UserRepositoryLive,
IdGeneratorLive,
CapsuleCrmGatewayLive, // ← 追加
).pipe(
Layer.provide(DrizzleLayer),
// Gateway が AppConfig に依存する場合は明示的に provide
);
3. 技術サービスを追加する場合
// 外部依存がない場合は Layer.succeed で直接追加
const IdGeneratorLive = Layer.succeed(IdGenerator, {
randomUuid: Effect.try({
try: () => randomUUID(),
catch: (cause) => new IdGenerationError({ cause }),
}),
});
Layer の種類
| パターン | いつ使う | 例 |
|---|---|---|
Layer.effect(Tag, Effect.gen(...)) | 依存サービスが必要 | Repository, Gateway |
Layer.succeed(Tag, impl) | 依存なし、同期的 | IdGenerator |
Layer.scoped(Tag, Effect.gen(...)) | リソース管理が必要 | SqlClient |
テスト用 Layer
Unit Test(UseCase テスト)
Port を Layer.succeed で mock:
const mockUserRepository = Layer.succeed(UserRepository, {
findById: (id) => Effect.succeed(new User({ /* ... */ })),
save: () => Effect.void,
});
// テストで使用
const result = await Effect.runPromise(
Effect.either(
usecase(args).pipe(Effect.provide(mockUserRepository)),
),
);
Integration Test(Repository テスト)
本番と同じ Layer 構成だが、テスト用 DB を使用:
// tests/helpers/ にテスト用 AppLayer を定義
const TestAppLayer = Layer.mergeAll(
UserRepositoryLive,
IdGeneratorLive,
).pipe(
Layer.provide(TestDrizzleLayer),
Layer.provideMerge(TestPackagesLayer),
);
const runtime = ManagedRuntime.make(TestAppLayer);
ManagedRuntime
Adapter 層と Integration テストで使用:
// Adapter(effectRuntime.ts)
const runtime = ManagedRuntime.make(appLayer);
const result = await runtime.runPromise(effect);
// テスト
const runtime = ManagedRuntime.make(TestAppLayer);
afterAll(async () => { await runtime.dispose(); });
ManagedRuntime.make()で作成runtime.runPromise()で Effect を実行runtime.dispose()でリソース解放(テストのafterAllで必須)
ObservabilityLive(環境別)
// Production
const ObservabilityLive = Layer.mergeAll(
GcpLoggerLive,
LogLevelLive,
TracingLive,
);
// Development
const ObservabilityLive = Layer.mergeAll(
PrettyLoggerLive,
LogLevelLive,
TracingLive,
);
packages/logging/index.ts で環境変数に基づいて切り替え。
チェックリスト
- 新しい Layer を
InfraLayerのLayer.mergeAllに追加 - 依存順が正しい(Repository/Gateway → DrizzleLayer → PackagesLayer)
-
type AppLayerの型が更新されている(自動推論) - テスト用 Layer でも同じ Port を提供している
-
ManagedRuntime.dispose()がテストのafterAllで呼ばれている
参照ファイル
- Composition Root:
apps/datahub/src/di/appLayer.ts - AppConfig:
apps/datahub/src/packages/config/appConfig.ts - SqlClient:
apps/datahub/src/packages/db/sqlClient.ts - DrizzleClient:
apps/datahub/src/infrastructure/db/drizzleClient.ts - Observability:
apps/datahub/src/packages/logging/index.ts - EffectRuntime:
apps/datahub/src/adapter/http/middleware/effectRuntime.ts
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.