Last active
November 14, 2021 19:51
-
-
Save przemek-pokrywka/d86787767a1ccc9201f5855d9ebeb42f to your computer and use it in GitHub Desktop.
Can programming be liberated from the ZIO layer style?
This file contains 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
/// Powered by TSK - The Scripting Kit https://github.com/tsk-tsk/tsk-tsk 2> /dev/null \\\ | |
/* | |
export v=0.1.5 | |
. $(b=boot-tsk-$v u=git.io/$b; (cat ~/.tsk/$b || curl -sfL $u || wget -qO - $u) | sh) | |
scala_version=2.12.13 | |
bloop_version=1.4.11 | |
dependencies=' | |
com.h2database:h2:1.4.199 | |
io.getquill::quill-jdbc-zio:3.8.0 | |
' | |
run | |
exit | |
*/ | |
import com.typesafe.config.ConfigFactory | |
import io.getquill._ | |
import io.getquill.util.LoadConfig | |
import io.getquill.context.ZioJdbc._ | |
import io.getquill.context.qzio.ImplicitSyntax._ | |
import zio._ | |
import zio.console._ | |
import java.io.Closeable | |
import javax.sql.DataSource | |
import org.h2.tools.RunScript | |
import java.io.StringReader | |
import java.sql.SQLException | |
// Application domain starts here, note lack of ZIO Layer references! | |
// Also no dependencies are present in the R parameter. | |
case class Person(name: String, age: Int) | |
trait PersonRepo { | |
def select(): IO[SQLException, List[Person]] | |
} | |
class PersonRepoH2(ds: DataSource with Closeable) extends PersonRepo { | |
implicit private val env = Implicit(Has(ds)) | |
private val ctx = new H2ZioJdbcContext(SnakeCase) | |
import ctx._ | |
def select(): IO[SQLException, List[Person]] = | |
ctx.run(query[Person].filter(p => p.name == "John")).implicitDS | |
} | |
trait PersonService { | |
def showAllPeople: UIO[Unit] | |
} | |
class PersonServiceImpl(repo: PersonRepo, _console: Console) extends PersonService { | |
def showAllPeople: UIO[Unit] = (for { | |
people <- repo.select() | |
_ <- ZIO.foreach(people) { case Person(name, age) => | |
putStrLn(s"${name} ${age}").provide(_console) | |
} | |
} yield ()).orDie | |
} | |
/** | |
* Wires the whole application together. Look, no layers! | |
*/ | |
class Application(dataSource: DataSource with Closeable, _console: Console) { | |
private val personRepo: PersonRepo = new PersonRepoH2(dataSource) | |
val service: PersonService = new PersonServiceImpl(personRepo, _console) | |
} | |
// The first ZIO Layer occurrence, because some libraries ship with their ZLayers (Quill in this case) | |
object persistence { | |
private val config = | |
ConfigFactory.parseString(""" | |
ctx.dataSourceClassName="org.h2.jdbcx.JdbcDataSource" | |
ctx.dataSource.url="jdbc:h2:mem:yourdbname" | |
ctx.dataSource.user="sa" | |
""").getConfig("ctx") | |
private def createSchema(ds: DataSource with Closeable) = | |
Task { | |
RunScript.execute(ds.getConnection, new StringReader(""" | |
CREATE TABLE IF NOT EXISTS Person( | |
name VARCHAR(255), | |
age int | |
); | |
INSERT INTO Person(name, age) VALUES ('John', 2); | |
""")) | |
ds | |
} | |
val layer = | |
(DataSourceLayer.fromConfig(config) >>> ZLayer.fromServiceM(createSchema)).orDie | |
} | |
object example extends App { | |
/** | |
* Services managed by ZLayers are passed to an object which does the wiring | |
* in the traditional OOP way (composition root pattern). | |
*/ | |
val applicationLayer = | |
ZLayer.fromService[DataSource with Closeable, Application](new Application(_, Has(Console.Service.live))) | |
def program(app: Application): UIO[ExitCode] = | |
app.service.showAllPeople.as(ExitCode.success) | |
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] = | |
(ZIO.access[Has[Application]](_.get) >>= program) | |
.provideLayer(persistence.layer >>> applicationLayer) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment