Adding decrel to your build
decrel is published for Scala 2.13 and 3, and for JVM and JS platforms.
Release versions
sbt
"com.yoohaemin" %% "decrel-core" % "0.1.1" // Defines Relation and derivations
"com.yoohaemin" %% "decrel-zquery" % "0.1.1" // Integration with ZQuery
"com.yoohaemin" %% "decrel-fetch" % "0.1.1" // Integration with Fetch
"com.yoohaemin" %% "decrel-scalacheck" % "0.1.1" // Integration with ScalaCheck
"com.yoohaemin" %% "decrel-ziotest" % "0.1.1" // Integration with ZIO-Test Gen
"com.yoohaemin" %% "decrel-cats" % "0.1.1" // Integration with F[_]: Monad
mill
ivy"com.yoohaemin::decrel-core:0.1.1" // Defines Relation and derivations
ivy"com.yoohaemin::decrel-zquery:0.1.1" // Integration with ZQuery
ivy"com.yoohaemin::decrel-fetch:0.1.1" // Integration with Fetch
ivy"com.yoohaemin::decrel-scalacheck:0.1.1" // Integration with ScalaCheck
ivy"com.yoohaemin::decrel-ziotest:0.1.1" // Integration with ZIO-Test Gen
ivy"com.yoohaemin::decrel-cats:0.1.1" // Integration with F[_]: Monad
Snapshot versions
sbt
"com.yoohaemin" %% "decrel-core" % "0.1.1+15-73201156-SNAPSHOT" // Defines Relation and derivations
"com.yoohaemin" %% "decrel-zquery" % "0.1.1+15-73201156-SNAPSHOT" // Integration with ZQuery
"com.yoohaemin" %% "decrel-fetch" % "0.1.1+15-73201156-SNAPSHOT" // Integration with Fetch
"com.yoohaemin" %% "decrel-scalacheck" % "0.1.1+15-73201156-SNAPSHOT" // Integration with ScalaCheck
"com.yoohaemin" %% "decrel-ziotest" % "0.1.1+15-73201156-SNAPSHOT" // Integration with ZIO-Test Gen
"com.yoohaemin" %% "decrel-cats" % "0.1.1+15-73201156-SNAPSHOT" // Integration with F[_]: Monad
mill
ivy"com.yoohaemin::decrel-core:0.1.1+15-73201156-SNAPSHOT" // Defines Relation and derivations
ivy"com.yoohaemin::decrel-zquery:0.1.1+15-73201156-SNAPSHOT" // Integration with ZQuery
ivy"com.yoohaemin::decrel-fetch:0.1.1+15-73201156-SNAPSHOT" // Integration with Fetch
ivy"com.yoohaemin::decrel-scalacheck:0.1.1+15-73201156-SNAPSHOT" // Integration with ScalaCheck
ivy"com.yoohaemin::decrel-ziotest:0.1.1+15-73201156-SNAPSHOT" // Integration with ZIO-Test Gen
ivy"com.yoohaemin::decrel-cats:0.1.1+15-73201156-SNAPSHOT" // Integration with F[_]: Monad
Snapshot resolver (sbt)
If you are using an older (< 1.7.0) version of sbt, you might also need to add a resolver.
resolvers +=
"Sonatype S01 OSS Snapshots" at "https://s01.oss.sonatype.org/content/repositories/snapshots"
What module to pick?
It depends on what you need from decrel.
decrel-core
You would normally not need to specify decrel-core
as a dependency, but it would be enough to specify one of the others and get this in as a transitive dependency.
Declare this dependency if:
- You have a module that only contains purely the domain model without actual business logic.
- You can't rely on dependencies that are pulled in transitively. (I've heard bazel is like that)
decrel-zquery
Declare this dependency if you want to access datasources with relations, and your application is based on ZIO.
decrel-fetch
Declare this dependency if you want to access datasources with relations, and your application is based on cats-effect (whether it is tagless-final style or directly on cats.IO
).
decrel-scalacheck
Declare this dependency if you want to generate random data with relations, and your tests use scalacheck
generators.
decrel-ziotest
Declare this dependency if you want to generate random data with relations, and your tests use zio-test
generators.
decrel-cats
Declare this dependency if you have some datatype outside the above four that you want to use with decrel, and if that datatype has cats.Monad
implemented.
Quick Start with ZIO
Here's a minimal example to get you started with decrel and ZIO:
import decrel.Relation
import decrel.reify.zquery
import decrel.syntax.relation._
import zio._
// 1. Define your domain models
case class User(id: String, name: String)
case class Post(id: String, userId: String, title: String, content: String)
// 2. Define your relations
object User {
object posts extends Relation.Many[User, List, Post]
}
object Post {
object user extends Relation.Single[Post, User]
}
// 3. Create your implementation
object DataAccess extends zquery[Any] {
// Simulated data
val users = Map(
"u1" -> User("u1", "Alice"),
"u2" -> User("u2", "Bob")
)
val posts = List(
Post("p1", "u1", "Alice's Post", "Hello!"),
Post("p2", "u1", "Another Post", "More content"),
Post("p3", "u2", "Bob's Post", "Hi there")
)
// Implement the post.user relation
implicit val postUserProof: Proof.Single[Post.user.type, Post, Nothing, User] =
implementSingleDatasource(Post.user) { posts =>
ZIO.succeed(
posts.map(post => post -> users(post.userId))
)
}
// Implement the user.posts relation
implicit val userPostsProof: Proof.Many[User.posts.type, User, Nothing, List, Post] =
implementManyDatasource(User.posts) { users =>
ZIO.succeed(
users.map(user =>
user -> posts.filter(_.userId == user.id)
)
)
}
}
// 4. Use in your application
object MyApp extends ZIOAppDefault {
def run = {
val program = for {
post <- ZIO.succeed(DataAccess.posts.head)
user <- Post.user.toZIO(post)
_ <- Console.printLine(s"Post '${post.title}' was written by ${user.name}")
// Compose relations - get a user's posts and then get the user of each post (should be the same user)
user2 <- ZIO.succeed(DataAccess.users("u1"))
postsWithUsers <- (User.posts <>: Post.user).toZIO(user2)
_ <- Console.printLine(s"${user2.name} wrote ${postsWithUsers.size} posts")
} yield ()
program
}
}
Quick Start with cats-effect
Here's a minimal example using decrel with cats-effect and Fetch:
import cats.effect._
import decrel.Relation
import decrel.reify.fetch
import decrel.syntax.relation._
// 1. Define your domain models
case class Product(id: String, name: String, price: Double)
case class Order(id: String, customerId: String)
case class OrderItem(orderId: String, productId: String, quantity: Int)
// 2. Define your relations
object Order {
object items extends Relation.Many[Order, List, OrderItem]
}
object OrderItem {
object product extends Relation.Single[OrderItem, Product]
}
// 3. Create your implementation
object DataAccess extends fetch[IO] {
override protected implicit val CF = IO.asyncForIO
// Simulated data
val products = Map(
"p1" -> Product("p1", "Laptop", 1200.0),
"p2" -> Product("p2", "Phone", 800.0),
"p3" -> Product("p3", "Headphones", 200.0)
)
val orders = List(
Order("o1", "c1")
)
val orderItems = List(
OrderItem("o1", "p1", 1),
OrderItem("o1", "p3", 2)
)
// Implement the orderItem.product relation
implicit val orderItemProductProof: Proof.Single[OrderItem.product.type, OrderItem, Product] =
implementSingleDatasource(OrderItem.product) { items =>
IO.pure(
items.map(item => item -> products(item.productId))
)
}
// Implement the order.items relation
implicit val orderItemsProof: Proof.Many[Order.items.type, Order, List, OrderItem] =
implementManyDatasource(Order.items) { orders =>
IO.pure(
orders.map(order =>
order -> orderItems.filter(_.orderId == order.id)
)
)
}
}
// 4. Use in your application
object MyApp extends IOApp.Simple {
def run: IO[Unit] = {
val program = for {
order <- IO.pure(DataAccess.orders.head)
items <- Order.items.toF(order)(DataAccess.orderItemsProof, IO.asyncForIO.clock)
_ <- IO.println(s"Order ${order.id} has ${items.size} items")
// Compose relations - get all products in an order
productsInOrder <- (Order.items <>: OrderItem.product).toF(order)(
// Implicits are derived automatically
implicitly, IO.asyncForIO.clock
)
_ <- IO.println(s"Products in order: ${productsInOrder.map(_.name).mkString(", ")}")
} yield ()
program
}
}
Next Steps
Now that you've set up decrel and seen basic examples, you can:
- Read Defining Relations to learn how to create a comprehensive domain model
- Check the Showcase for more complex examples
- Explore advanced topics like caching and optimizing batch fetching