mcpField
E
effect-advanced

effect-advanced

github.com/trancong12102開発746 回インストール5 週間前に更新
リポジトリ

effect-advanced について

effect-advancedは、本番向けEffect-TSアプリの構築規約を示すスキルです。API全体の説明ではなく、型付きエラー、依存性注入、リソーススコープ、並行処理、よくある落とし穴に焦点を当てます。

主な機能

  1. 01Effect.genによるビジネスロジック規約
  2. 02型付きエラー処理パターン
  3. 03ServiceとLayerのアーキテクチャ
  4. 04スコープ付きリソース管理
  5. 05高水準の並行処理API
  6. 06落とし穴とリファレンスガイド

活用シーン

  1. 01Effect-TSアプリのサービス構成
  2. 02型付き失敗と欠陥の処理
  3. 03依存Layerの合成
  4. 04リソースのライフサイクルと並行処理の管理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/trancong12102/agentskills --skill effect-advanced

インストール後、Claude Code で/effect-advancedを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
concurrency.md
dependency-injection.md
error-handling.md
platform.md
resource-management.md
schema.md
stream.md
testing.md

詳細

Effect Advanced:パターン、規約、落とし穴

このスキルでは、本番環境向けの Effect-TS アプリケーションを構築するためのルール、規約、アーキテクチャ上の決定事項を定めます。よくある落とし穴を防ぎ、規模の拡大にも対応できるパターンを徹底するため、意図的に特定の方針を採用しています。

API の詳細なドキュメントについては、ほかの適切なツール(ドキュメント検索、ウェブ検索など)を使用してください。このスキルでは、API 全体ではなく、Effect を慣用的に使う方法と理由に焦点を当てます。

基本規約

ビジネスロジックには Effect.gen を使用する

ジェネレーターは同期コードのように読めるため、単純な合成を超える処理では、長い .pipe / .flatMap チェーンよりも強く推奨されます。

const program = Effect.gen(function* () {
  const config = yield* ConfigService;
  const user = yield* UserRepo.findById(config.userId);
  return user;
});

pipe はデータ変換パイプラインや短いコンビネータチェーンに使用してください。

例外をスローしない — Effect のエラーチャネルを使用する
代わりに…使用するもの
throw new Error()Effect.fail(new MyError())
Promise の try/catchEffect.tryPromise({ try, catch })
コールバック APIEffect.async((resume) => ...)
回復不能なクラッシュEffect.die(defect)
メソッドより関数を使う

合成しやすく、ツリーシェイキングにも適しているため、e.pipe(Effect.map(f)) より Effect.map(e, f) を優先してください。アプリケーションではフラットインポート(import { Effect } from "effect")を使用しても問題ありませんが、ライブラリでは名前空間インポート(import * as Effect from "effect/Effect")のほうが適しています。

@effect/schema は非推奨です

Schema はコアの effect に統合されています。"effect" から直接インポートしてください。

import { Schema } from "effect";
// NOT: import { Schema } from "@effect/schema"
本番環境では NodeRuntime.runMain を使用する

Effect.runPromise は SIGINT / SIGTERM を適切に処理しません。

import { NodeRuntime } from "@effect/platform-node";
NodeRuntime.runMain(program.pipe(Effect.provide(AppLayer)));

エラーハンドリングの考え方

Failure と defect — 根本的な違い
項目Failure(想定内)Defect(想定外)
APIEffect.fail(new MyError())Effect.die(new Error())
型チャネルE で追跡されるE に現れない(never)
回復catchTag、catchAll、retryシステム境界でのみ
目安呼び出し元で処理する想定があるバグ、または発生しえない状態
タグ付きエラーを必ず使用する

通常の Error や文字列の Failure では、Effect の型付きエラーチャネルの利点を活かせません。

class UserNotFound extends Data.TaggedError("UserNotFound")<{
  readonly id: string;
}> {}

// Tagged errors are yieldable — no Effect.fail wrapper needed
const program = Effect.gen(function* () {
  const user = yield* db.findUser(id);
  if (!user) yield* new UserNotFound({ id });
  return user;
});
catchAll は defect を捕捉しません

エラーハンドリングで最もよくある間違いです。

Effect.catchAll(program, handler); // catches E only — NOT defects
Effect.catchAllCause(program, handler); // catches everything (E + defects + interrupts)

catchAllCause / catchAllDefect は、システム境界(トップレベルのエラーハンドラー、HTTP レスポンスのマッパー)でのみ使用してください。


依存性注入のアーキテクチャ

Service → Layer → Provide(一度だけ)
1. Define services with Context.Tag  →  "what do I need?"
2. Implement via Layers              →  "how is it built?"
3. Provide once at entry point       →  "wire it all together"
Service メソッドの R は never にする

依存関係はメソッドのシグネチャではなく、Layer の合成で扱います。

// WRONG: leaks dependency to callers
findById: (id: string) => Effect.Effect<User, UserNotFound, Database>;

// RIGHT: Database is wired in the Layer
findById: (id: string) => Effect.Effect<User, UserNotFound>;
Layer の合成 — 演算子を理解する
操作使用場面動作
Layer.merge(A, B)独立したサービス両方を並行して構築する
Layer.provide(downstream, upstream)A が B に供給されるupstream を先に構築する
Layer.fresh(layer)新しいインスタンスを強制メモ化を回避する

重要: Layer.merge は構築の順序を制御しません。B が A に依存する場合は、Layer.merge ではなく Layer.provide を使用してください。

エントリーポイントで Effect.provide を一度だけ使用する

provide を分散させると、依存関係が隠れたり、Layer が重複したりします。

// WRONG: provide scattered throughout codebase
const getUser = UserRepo.findById(id).pipe(Effect.provide(DbLayer));

// RIGHT: compose and provide once
const main = program.pipe(Effect.provide(AppLayer));
NodeRuntime.runMain(main);

リソースとスコープのルール

acquireRelease には Effect.scoped が必須

Effect.scoped を忘れることは、リソース管理で最もよくある落とし穴です。プログラムが終了するまでリソースが蓄積します。

// WRONG: scope never closes, connection leaks
const result = yield * getDbConnection;

// RIGHT: scope closes when block completes
const result =
  yield *
  Effect.scoped(
    Effect.gen(function* () {
      const conn = yield* getDbConnection;
      return yield* conn.query("SELECT 1");
    }),
  );
リリースファイナライザーは必ず実行されます

成功時、失敗時、そして中断時も、必ず実行されます。ファイナライザーには条件付きのクリーンアップに使える Exit 値が渡されます。

1つのスコープ内で複数のリソースを扱う
Effect.scoped(
  Effect.gen(function* () {
    const conn = yield* Effect.acquireRelease(openConn(), closeConn);
    const file = yield* Effect.acquireRelease(openFile(), closeFile);
    // both released when scope closes, in REVERSE acquisition order
  }),
);

並行性モデル

生のフォークより高レベル API を優先する
API用途
Effect.all([], { concurrency: N })並列実行数の制限
Effect.forEach(items, fn, { concurrency: N })ワーカープールパターン
Effect.race(a, b)最初に完了したものが勝ち、他は中断される
Effect.timeout(e, dur)任意のエフェクトに期限を設定

高レベル API では不十分な場合に限り、Effect.fork / Fiber を使用してください。

フォークの種類 — ライフサイクルを把握する
関数スコープクリーンアップ
Effect.fork親のスコープ親とともに自動で中断される
Effect.forkDaemonグローバルスコープクリーンアップされないため、自分で行う必要がある
Effect.forkScoped最も近い Scopeリソースのライフサイクルに紐付く

注意点: forkDaemon は、中断し忘れるとファイバーがリークします。


よくある落とし穴

  1. 実行されないエフェクト — エフェクトを作成しても、yield するか実行しなければ、気付かれにくいバグになります。ジェネレーター内の Effect.log("msg") は、yield* しない限り何も行いません。

  2. catchAll では欠陥を捕捉できません — すべての失敗を把握するには、システム境界で catchAllCause を使用してください。

  3. Effect.scoped の欠落 — スコープ境界のない acquireRelease は、プログラムが終了するまでリソースをリークします。

  4. Effect.provide の散在 — すべてのレイヤーを合成し、エントリーポイントで一度だけ提供してください。

  5. オーバーロードされた関数へのポイントフリー記法 — Effect.map(myOverloadedFn) は、ジェネリクスを気付かれずに消去します。明示的なラムダを使用してください:Effect.map((x) => myOverloadedFn(x))。

  6. Effect.async の再開関数を複数回呼び出す — 再開関数は必ず1回だけ呼び出してください。複数回呼び出すと、未定義の動作になります。

  7. orDie はエラーを隠します — 型付きの失敗を型なしの欠陥に変換します。代わりに、エラーを適切に処理してください。

  8. 依存関係のあるサービスに Layer.merge を使う — merge は構築の順序を制御しません。あるレイヤーが別のレイヤーの出力を必要とする場合は、Layer.provide を使用してください。

  9. Fiber.join と Fiber.await の違い — エッジケースでは、join によってファイナライザーが早期に実行されることがあります。リソースの安全性が重要な場合は、await を優先してください。

  10. 無限ストリームに runCollect を使う — 事前に take を呼び出さずに使用してはいけません。処理は終了せず、無制限にメモリを消費します。

  11. スコープ付きテストに it.effect を使う — Scope を必要とするエフェクトでは、it.effect ではなく it.scoped を使用してください。そうしないと、型エラーが発生します。


リファレンスファイル

特定の課題に取り組む際は、該当するリファレンスファイルをお読みください。

ファイル読むタイミング
references/error-handling.mdタグ付きエラー、Cause、欠陥からの復旧、エラーマッピングのパターン
references/dependency-injection.mdサービス、Layer、合成、メモ化、provide のパターン
references/concurrency.mdFiber、fork のバリエーション、Deferred、Semaphore、構造化並行性
references/resource-management.mdScope、acquireRelease、Layer リソース、fork と scope の相互作用
references/schema.mdSchema の定義、変換、ブランド型、再帰スキーマ
references/stream.mdStream オペレーター、チャンク化、バックプレッシャー、リソースを扱うストリーム
references/testing.md@effect/vitest、TestClock、Layer のモック、Config のモック
references/platform.mdHTTP クライアント、FileSystem、Command、ランタイム、フレームワークとの統合

コメント