Created
June 25, 2023 06:15
-
-
Save igor-vovk/538b904939b1f37c32aee18982043397 to your computer and use it in GitHub Desktop.
Dependency Injection with cats-effect Resource Monad
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import cats.effect.* | |
| import cats.effect.unsafe.IORuntime | |
| import org.postgresql.core.ConnectionFactory | |
| // repository, two services, and api: | |
| class Repository(conn: ConnectionFactory) {} | |
| class ServiceA(repo: Repository) {} | |
| class ServiceB(repo: Repository) {} | |
| class HttpServerTask(serviceA: ServiceA, serviceB: ServiceB) { | |
| def run: IO[Unit] = ??? | |
| } | |
| class Allocator(implicit runtime: IORuntime) { | |
| // Ref that will keep track of finalizers | |
| private val shutdown: Ref[IO, IO[Unit]] = Ref.unsafe(IO.unit) | |
| // Method to allocate dependencies | |
| def allocate[A](resource: Resource[IO, A]): A = | |
| resource.allocated.flatMap { case (a, release) => | |
| // Shutdown this resource, and after shutdown all previous | |
| shutdown.update(release *> _).map(_ => a) | |
| }.unsafeRunSync() | |
| // Shutdown dependencies | |
| def shutdownAll: IO[Unit] = { | |
| shutdown.getAndSet(IO.unit).flatten | |
| } | |
| } | |
| // Dependency Injection part: | |
| object Dependencies { | |
| // Safe method to create dependencies: | |
| def apply(runtime: IORuntime): Resource[IO, Dependencies] = | |
| Resource.make { | |
| IO(unsafeCreate(runtime)) | |
| } { | |
| _.allocator.shutdownAll | |
| } | |
| // Unsafe method, use it carefully as no shutdown is executed: | |
| def unsafeCreate(runtime: IORuntime): Dependencies = | |
| new Dependencies(new Allocator()(runtime)) | |
| } | |
| class Dependencies(val allocator: Allocator) { | |
| lazy val conn: ConnectionFactory = allocator.allocate { | |
| ??? | |
| } | |
| lazy val repo = new Repository(conn) | |
| lazy val serviceA = new ServiceA(repo) | |
| lazy val serviceB = new ServiceB(repo) | |
| lazy val server = HttpServerTask(serviceA, serviceB) | |
| } | |
| // Runner class | |
| object Main extends IOApp.Simple { | |
| // Now we can use any dependency and not just `server` as all of them are exposed: | |
| private val dependencies = Dependencies(IORuntime.global) | |
| override def run = dependencies.use(_.server.run) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment