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

服务间依赖#

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