Search code examples
scalascala-catscats-effect

How should I wrap this code correctly to use with Cats Effect 3? Should I use a resource?


Is the below code correct to work with cats effect and IO?

Should this be using a resource, if so, can someone help me as I haven't used resource before.

object AWS {
  val client = AWSClientBuilder.defaultClient()

  def blah(...): IO[Unit] = IO {
     client.submitJob(new AwsRequest(....))
  }
}

Solution

  • Technically not, since client (and thus AWS) are shared mutable state.
    However, the refactoring is not only using Resource, but also not using a global object and passing down the dependency explicitly.

    Like this:

    // Make AWS an interface that defines the operations it provides.
    trait AWS {
      def foo(...): IO[Unit]
    }
    
    // In the companion object put a factory method that instantiates the resources.
    object AWS {
      def instance(...): Resource[IO, AWS] =
        Resource
          .make(IO(AWSClientBuilder.defaultClient))(client => IO(client.close()))
          .map(client => new AWSImpl(client))
    }
    
    // Have a private implementation of the interface.
    private[pckg] final class AWSImpl (client: AWSClient) extends AWS {
      def blah(...): IO[Unit] = IO {
        client.submitJob(new AwsRequest(...))
      }
    }
    
    // Whatever was using AWS globally before now must require its dependency.
    final class Domain(aws: Aws) {
      def bar(...): IO[Unit] =
        aws.foo(...)
    }
    
    // Assembly the dependency graph on the main.
    final class Main extends IOApp.Simple {
      override final val run: IO[Unit] =
        Aws.instance(...).use { aws =>
          val domain = new Domain(aws)
          domain.bar(...)
        }
    }
    

    This is a very general idea, don't follow blindly.