Dependency Handling with ZIO Zlayer and Slick

Effect based programming with ZIO

(λx.x)eranga
Effectz.AI
4 min readJul 4, 2022

--

Background

In my previous posts, I have discussed about Scala dependency handling with Cake Pattern and Monads. In this post I’m gonna discuss about the handling dependencies with ZIO and ZIO Zlayer library features. In here, I’m assuming you have some basic idea about ZIO. If you don’t, please go through these blog posts(Zlayer by rock the jvm, ZIO dependency handling by Adam, ZIO and doobie by Juliano, ZIO and http4s by Wiem) and get some basic idea first. All the source codes which related to this post available in gitlab. Please clone the repo and continue the post.

ZIO and Zlayer

ZIO is a Scala zero-dependency library for asynchronous and concurrent programming. It enables you to create applications that are scalable, resilient and responsive to your business’s demands. ZIO basically a functional effect system in Scala(there are several other functional effect systems libraries in the Scala, such as Cats Effect, Monix) which warps and manipulate side effects. ZIO is built around the ZIO data type. ZIO data type has three parameters and are referred to as ZIO[R, E, A]. The E type specifies the possible errors, with which the computation might fail. A specifies the type of the result of the computation, should it succeed. R describes the environment (or requirement) that is needed by the computation.

ZIO has two contextual data types, Has and Zlayer. Has data type is used to express an effect’s dependency on a specific service. ZLayer data type is used to construct a service from its dependencies. While ZIO[R, E, A] is the basic building block of any ZIO-based application, ZLayer[RIn, E, ROut] defines and manages the dependencies that a process or program needs. ZLayer[RIn, E, ROut] describes how to construct ROut dependencies, using RIn dependencies; this construction process might fail with an error of type E.

Scenario

In this scenario I’m discussing about implementing simple user subscription application with Zlayer. When a user subscribes, it simply persists the user in the database and send an email to the user. For data persistent I have used Scala Slick library. Email sending functions emulated with printing the message in the console. Following figure shows the dependency graph of the application. The top layer is the Subscriber service. It has two dependencies for Repository service and Notifier service. Repository service handles data persistent functions with Slick, Notifier service handling email sending functions. The Repository service has dependency for DatabaseProvider service which providers database access functions with Slick. The DatabaseProvider service further has two dependencies dbConfig and dbProfile. In Slick terms dbConfig provides the database configurations. In here I have used H2 in-memory database. So dbConfig provides H2 database configurations(e.g db name, driver etc). dbProfile provides JDBC profile type(e.g H2Profile, PostgresProfile etc). In here it used H2Profile.

Sbt Dependency

Following is the build.sbt. I have used several dependencies related to ZIO and Slick, 1) zio, 2) slick, 3) h2(h2 driver library).

Database Provider Service

DatabaseProvider service handles the database access functions with Slick. Basically it creates Slick JDBCBackend with given database configurations(dbConfig) and JDBC profile type(dbProfile). In here I have created the Zlayer(live) with ZLayer.fromServicesManaged. It is the place where I have initialized the Slick JdbcProfile and Database.

Repository Service

Repository service handles data persistent functions with Slick. Following are the domain model(User), Slick database table(UsersTable) and service layer functions. I have defined three functions in the service layer, 1) init(create database tables), 2) create(create user record), 3) find(find user with username). In here, the Zlayer(live) created with ZLayer.fromServiceM.

Notifier Service

Notifier service handles the user notification email sending functions. Email sending functions emulated with printing the message in the console. In here Zlayer(live) created with Zlayer.succeed.

Subscriber Service

Subscriber service do the wiring of all the functions defined in the other services. It first create database table(only if table not exits), then create user and send notification email to the user. In here Zlayer(live) created with Zlayer.fromServices. Other thing is, I have exposed the subscribe function to outside via higher-level API. That exposed function used in the run method of the zio.App(main method).

Run the App

Finally I have run the application inside the run method of the zio.App. In here I have defined the dependency graph(layers) and run the subscribe function in the Subscriber service. When defining the layers(dependencies) I have used both vertical and horizontal composition of Zlayer services. For an example (dbConfigLayer ++ dbProviderLayer) defines horizontal composition. It combines two Zlayers(dbConfig and dbProvider) and obtain bigger Zlayer. DatabaseProvider.live >>> (Repository.live) defines vertical composition. It defines the output of DatabaseProvider Zlayer as an input of Repository Zlayer.

Reference

  1. https://blog.softwaremill.com/managing-dependencies-using-zio-8acc1539e276
  2. https://juliano-alves.com/2020/06/15/streaming-all-the-way-zio-doobie-quill-http4s-fs2/
  3. https://blog.rockthejvm.com/structuring-services-with-zio-zlayer/
  4. https://medium.com/@wiemzin/zio-with-http4s-and-doobie-952fba51d089
  5. https://blog.knoldus.com/understanding-zlayer-a-powerful-tool-from-zio/
  6. https://github.com/ScalaConsultants/zio-slick-interop

--

--