Define layers inside Context.Tag

We saw how convenient it is to use class and Context.tag. Since we are working with a class we are able to define additional methods and attributes directly inside it.

With this pattern we have all services implementations defined as separate static parameters:

export class PokeApi extends Context.Tag("PokeApi")<
  PokeApi,
  Effect.Effect.Success<typeof make>
>() {
  static readonly Live = Layer.effect(this, make).pipe(
    Layer.provide(Layer.mergeAll(PokemonCollection.Live, BuildPokeApiUrl.Live))
  );

  static readonly Mock = Layer.succeed(
    this,
    PokeApi.of({
      getPokemon: Effect.succeed({
        id: 1,
        height: 10,
        weight: 10,
        name: "my-name",
        order: 1,
      }),
    })
  );
}

Swapping layers for testing, mocking, development, and production becomes a (less than) 1 line change:

const MainLayer = Layer.mergeAll(
  PokeApi.Mock,
);

Furthermore, we can access all layers, methods, and even helper functions inside the same class with a single import:

import { Effect, Layer } from "effect";
// 👇 Single import for all layers, methods, and helper functions (`Live`, `Mock`, etc.)
import { PokeApi } from "./PokeApi";

const MainLayer = Layer.mergeAll(
  PokeApi.Mock,
);

This pattern is common in effect and used also for internal modules.


What are the advantages of all these Layer and Config?

This implementation makes all the services composable. We are going to see composability in action in the next module about testing 🚀