Create a Managed Runtime for Scoped Resources
Guideline
For services that manage resources needing explicit cleanup (e.g., a database
connection), define them in a Layer using Layer.scoped. Then, use
Layer.launch to provide this layer to your application.
Rationale
Layer.launch is designed for resource safety. It acquires all resources,
provides them to your effect, and—crucially—guarantees that all registered
finalizers are executed upon completion or interruption.
Good Example
import { Effect, Layer } from "effect";
class DatabasePool extends Effect.Service<DatabasePool>()("DbPool", {
effect: Effect.gen(function* () {
yield* Effect.log("Acquiring pool");
return {
query: () => Effect.succeed("result"),
};
}),
}) {}
// Create a program that uses the DatabasePool service
const program = Effect.gen(function* () {
const db = yield* DatabasePool;
yield* Effect.log("Using DB");
yield* db.query();
});
// Run the program with the service implementation
Effect.runPromise(
program.pipe(Effect.provide(DatabasePool.Default), Effect.scoped)
);
Explanation:
Layer.launch ensures that resources are acquired and released safely, even
in the event of errors or interruptions.
Anti-Pattern
Do not use Layer.toRuntime with layers that contain scoped resources. This
will acquire the resource, but the runtime has no mechanism to release it,
leading to resource leaks.