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.