425 words
2 minutes
[Effect Layers] 02. 服务间依赖
2025-08-30 18:20:09
2025-08-30 19:12:00

服务间依赖#

https://github.com/typeonce-dev/effect-getting-started-course

我们可以开始看到如何使用我们的服务来组合一个更有组织的应用:

  • BuildPokeApiUrl 依赖于 PokeApiUrl 来构建请求URL

  • PokeApi 依赖于 PokemonCollectionBuildPokeApiUrl 来构建带有宝可梦名称的URL

NOTE

服务定义的独立性

这些依赖关系不是在定义每个服务时创建的。每个服务定义都是独立的,可以在不阅读完整代码库的情况下理解或更新。

依赖关系是在实现每个服务时创建的。从没有依赖关系的服务开始更容易,在这种情况下是 PokemonCollectionPokeApiUrl

PokemonCollection.ts

export class PokemonCollection extends Context.Tag("PokemonCollection")< PokemonCollection, Array.NonEmptyArray<string> >() { static readonly Live = PokemonCollection.of(["staryu", "perrserker", "flaaffy"]); }

PokeApiUrl.ts

export class PokeApiUrl extends Context.Tag("PokeApiUrl")< PokeApiUrl, string >() { static readonly Live = Effect.gen(function* () { const baseUrl = yield* Config.string("BASE_URL"); return PokeApiUrl.of(`${baseUrl}/api/v2/pokemon`); }); }

BuildPokeApiUrl 使用 PokeApiUrl 来构建URL:

BuildPokeApiUrl.ts

export class BuildPokeApiUrl extends Context.Tag("BuildPokeApiUrl")< BuildPokeApiUrl, ({ name }: { name: string }) => string >() { static readonly Live = Effect.gen(function* () { const pokeApiUrl = yield* PokeApiUrl; // 👈 创建依赖 return BuildPokeApiUrl.of(({ name }) => `${pokeApiUrl}/${name}`); }); }

最后,PokeApi 使用 PokemonCollectionBuildPokeApiUrl

PokeApi.ts

export class PokeApi extends Context.Tag("PokeApi")<PokeApi, PokeApiImpl>() { static readonly Live = PokeApi.of({ getPokemon: Effect.gen(function* () { const pokemonCollection = yield* PokemonCollection; // 👈 创建依赖 const buildPokeApiUrl = yield* BuildPokeApiUrl; // 👈 创建依赖 // 👇 `buildPokeApiUrl` 是来自 `BuildPokeApiUrl` 的函数 const requestUrl = buildPokeApiUrl({ /// 👇 `pokemonCollection` 是一个非空的 `string` 列表 name: pokemonCollection[0], }); const response = yield* Effect.tryPromise({ try: () => fetch(requestUrl), catch: () => new FetchError(), }); if (!response.ok) { return yield* new FetchError(); } const json = yield* Effect.tryPromise({ try: () => response.json(), catch: () => new JsonError(), }); return yield* Schema.decodeUnknown(Pokemon)(json); }), }); }
IMPORTANT

依赖级别的区别

在这个实现中,是 getPokemon 函数依赖于 PokemonCollectionBuildPokeApiUrl而不是 PokeApi 服务本身

这是一个重要的区别。我们将在需要时将依赖关系提升到 PokeApi

[Effect Layers] 02. 服务间依赖
https://0bipinnata0.my/posts/course/effect-beginners-complete-getting-started/layers/02-dependencies-between-services/
Author
0bipinnata0
Published at
2025-08-30 18:20:09