Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala idiom for partial models?

Tags:

scala

I am writing a HTTP REST API and I want strongly typed model classes in Scala e.g. if I have a car model Car, I want to create the following RESTful /car API:

1) For POSTs (create a new car):

case class Car(manufacturer: String, 
               name: String, 
               year: Int)

2) For PUTs (edit existing car) and GETs, I want tag along an id too:

case class Car(id: Long, 
               manufacturer: String, 
               name: String, 
               year: Int)

3) For PATCHes (partial edit existing car), I want this partial object:

case class Car(id: Long, 
               manufacturer: Option[String],
               name: Option[String], 
               year: Option[Int])

But keeping 3 models for essentially the same thing is redundant and error prone (e.g. if I edit one model, I have to remember to edit the other models).

Is there a typesafe way to maintain all 3 models? I am okay with answers that use macros too.

I did manage to combine the first two ones as following

trait Id {
  val id: Long
}

type PersistedCar = Car with Id 
like image 206
pathikrit Avatar asked Oct 20 '14 03:10

pathikrit


2 Answers

I would go with something like that

  trait Update[T] {
    def patch(obj: T): T
  }

  case class Car(manufacturer: String, name: String, year: Int)

  case class CarUpdate(manufacturer: Option[String], 
                       name: Option[String], 
                       year: Option[Int]) extends Update[Car] {
    override def patch(car: Car): Car = Car(
      manufacturer.getOrElse(car.manufacturer),
      name.getOrElse(car.name),
      year.getOrElse(car.year)
    )
  }


  sealed trait Request
  case class Post[T](obj: T) extends Request
  case class Put[T](id: Long, obj: T) extends Request
  case class Patch[T, U <: Update[T]](patch: U) extends Request

With Post & Put everything is straightforward. With Patch a bit more complicated. I'm pretty sure CarUpdate class can be replaced with auto generated with macros.

If you'll update you Car model, you'll definitely will not forget about patch, because it will fail at compile time. However this two models looks too "copy-paste-like".

like image 102
Eugene Zhulenev Avatar answered Nov 08 '22 17:11

Eugene Zhulenev


You could represent your models as Shapeless records, then the id is simply one more field on the front, and the mapping to/from options can be done generically using ordinary Shapeless type-level programming techniques. It should also be possible to generically serialize/deserialize such things to JSON (I have done this in the past, but the relevant code belongs to a previous employer). But you would definitely be pushing the boundaries and doing complex type-level programming; I don't think mature library solutions with this approach exist yet.

like image 3
lmm Avatar answered Nov 08 '22 18:11

lmm