user2066049
user2066049

Reputation: 1371

Enrich case class via implicit extension

there is Person model case class which is exposed to the client via REST endpoint.

scala> case class Person(firstname: String, lastname: String)
defined class Person

Now one of the internal service wants to "enrich" this Person case class and add social security number to it. BUT that enrichment is only to populate social security number from one service and pass it along to another one.

Immediate thought is to add ssn property to Person as follow - but that's not desired given Person is exposed to endpoint consumer and SSN MUST NOT be visible to any user calling REST endpoint which exposes Person data.

case class Person(firstname: String, lastname: String, ssn: String)

So another thought is to enrich Person case class as follow

scala> implicit class ExtendedPerson(person: Person){
 | val SSN : String = ""
 | }
defined class ExtendedPerson

then Service1.findPerson API will have implicit in the scope and will want to prepare Person as follow

val person = Person(firstName="Joe",lastname="Doe", ssn ="123-456-0000")

and then

Service2.moreWorkWithPerson(person,other arguments)

then Service2.moreWorkWithPerson will want to get ssn=123-456-0000 from person argument.

what's the right way to achieve this in Scala without modifying definition of Person case class? Can shapeless help? any pointers would be appreciated?

Upvotes: 0

Views: 268

Answers (3)

Jasper-M
Jasper-M

Reputation: 15086

Maybe you should just expose a different class to the client.

case class Person(firstname: String, lastname: String, ssn: String) {
  def toClient = ClientPerson(firstname, lastname)
}

case class ClientPerson(firstname: String, lastname: String)

object RESTService {
  def getPerson(id: String): ClientPerson = {
    val p: Person = DB.getPerson(id)
    p.toClient
  }
}

Upvotes: 0

shim_
shim_

Reputation: 357

SSN as val in ExtendedPerson makes not much sense, instead it should be a function which queries your DB using the persons first and lastname. Given the fact that your DB is slow and you might call Person.SSN more than once I would suggest not to use this pattern as it will query your DB everytime you call .SSN. Instead I would write an implicit converter for Person to convert more explicitly.

case class ExtendedPerson(basicInfo: Person,ssn: String)

implicit class PersonHelper(person: Person) extends AnyVal{
 def extended = {
   val ssn = DB.querySSN(...)
   ExtendedPerson(person,ssn)
 }
}


val person: ExtendedPerson = Person("John","Doe").extended

//person = ExtendedPerson(Person("John","Doe"),"123-456-0000"")

Note: The code is written out of mind but should be logically correct

Upvotes: 1

Dima
Dima

Reputation: 40500

It seems that you are overthinking this. What's wrong with this?

case class Person(firstname: String, lastname: String, ssn: Option[String] = None);

Now, in REST handler you do return Person("John", "Doe"), and in the other service: return Person("John", "Doe", Some("111-11-1111"))

Upvotes: 0

Related Questions