Effect CommunityEC
Effect Community3y ago
54 replies
tomchristensen

Modelling Layers in Effect Veterans: Challenges and Patterns

How are Effect veterans modelling layers?

The recent change to some of the
Layer
APIs prompted me to revisit how we're modelling layers in our app, and I'm finding it hard to come up with a good pattern that let's us keep services configurable without ending up with a big mess of Layer.provide, Layer.provideMerge and Layer.mergeAll to mash together at the execution boundary.

Example:

We have a FulfillmentService which depends on OrderRepo, ProductRepo and CustomerRepo, all three of which depend on Database. The Database service is configurable, so the module exports a makeLayer function as follows:

declare const make: (config: DatabaseConfig) => Effect.Effect<Scope, never, Database>

// returns: Layer.Layer<never, ConfigError, Database>
export const makeLayer = (config: Config.Config.Wrap<DatabaseConfig>) =>
  Layer.scoped(
    Database,
    Effect.flatMap(Effect.config(Config.unwrap(config)), make),
  )


None of the 3 repos are configurable, so they all export a layer like this:

declare const make: Effect.Effect<Database, never, OrderRepo>

// type: Layer.Layer<Database, never, OrderRepo>
export const layer = Layer.effect(OrderRepo, make)


FulfillmentService is configurable in a similar way to Database, so we have:

declare const make: (config: FulfillmentServiceConfig) => Effect.Effect<OrderRepo | ProductRepo | CustomerRepo, never, FulfillmentService>

// returns: Layer.Layer<OrderRepo | ProductRepo | CustomerRepo, ConfigError, FulfillmentService>
export const makeLayer = (config: Config.Config.Wrap<FulfillmentServiceConfig>) =>
  Layer.effect(
    FulfillmentService,
    Effect.flatMap(Effect.config(Config.unwrap(config)), make),
  )


...continued below
Was this page helpful?