Domain, API, DTO

Translating between layers with Chimney


Mateusz Kubuszok

About me

Once upon a time

  • simple web backend

  • Circe codecs

  • Doobie (well, Slick actually)

  • Protobuf and Kinesis (well, it was Avro and Postgres)

Let’s look at example

It kinds works, but let add a few modifications:

  • for creationDate let’s use Instant`

  • for id let’s use UUID

We can still improve it!

  • let’s create a separate model for JSONs

  • let’s create a separate model for DB operations

Could we get rid of the silly code for mapping?

YES

What is possible with Chimney?

case class to case class

case class Source(a: Int, b: String, c: Boolean)
case class Target(b: String, a: Int)

import io.scalaland.chimney.dsl.*
Source(777, "value", c = true).transformInto[Target]
// Target("value", 777)
case class Source(a: Int, b: String)
case class Target2(b: String, a: Int, extra: Double)

import io.scalaland.chimney.dsl.*
Source(777, "value").into[Target2]
  .withFieldConst(_.extra, 3.14)
  .transform // Target2("value", 777, 3.14)
Source(777, "value").into[Target2]
  .withFieldComputed(_.extra, src => src.a.toDouble)
  .transform // Target2("value", 777, 777.0)

ADT to ADT

enum Source:
  case Foo, Bar(b: Int)
enum Target:
  case Foo, Bar(b: Int), Baz

import io.scalaland.chimney.dsl.*
(Source.Bar(777): Source).transformInto[Target]
// Target.Bar(777)
enum Source:
  case Foo, Bar(b: Int), Baz
enum Target:
  case Foo, Bar(b: Int)

import io.scalaland.chimney.dsl.*
(Source.Baz: Source).into[Target]
  .withEnumCaseHandled[Source.Baz](_ => Target.Foo)
  .transform // Target.Foo

Collections

case class Source(a: Int, b: String, c: Boolean)
case class Target(b: String, a: Int)

import io.scalaland.chimney.dsl.*
List(Source(777, "value")).transformInto[Vector[Target]]
// Vector(Target("value", 777))
case class Source(a: Int, b: String)
case class Target2(b: String, a: Int, extra: Double)

import io.scalaland.chimney.dsl.*
List(Source(777, "value")).into[Vector[Target]]
  .withFieldConst(_.eachItem.extra, 3.14)
  .transform // Vector(Target("value", 777, 3.14))

More info

Thank you !