Hi,
I’m pretty new to Scala (although not to programming in general) so am looking for someone wiser/cleverer than myself to give some advice on the best way to achieve merging case classes together. I’m using this in the context of Lagom but I think this is more of a general Scala question rather than Lagom-specific. Please forgive me for any glaring mistakes I’ve made - the learning curve is quite steep here - but please do point them out! Also, please note that using the code provided I am able to achieve what I wanted to achieve - just not in a style that I am comfortable with.
With Lagom it seems to be encouraged to use case classes to pass data around to each part of the eco-system of microservices that one is assembling.
Basically, I have two microservices: survey and question - each one handles persisting their own data separately - questions can be updated independently of surveys (and vice-versa). When a survey is saved, it is saved with only references to questions (via the ref field in PageQuestion which will be used to populate the mutable question field later on).
I have the following case classes in my Survey microservice. Note that in the PageQuestion class I am currently using a var field for questions because I need to be able to amalgamate the PageQuestion’s question field with data from the question service’s Question object.
case class Survey(uuid: String, name: String, pages: Option[Array[Page]])
object Survey {
implicit val format: Format[Survey] = Json.format
}
case class Page(uuid: String, name: String, pos: Int, questions: Option[Array[PageQuestion]])
object Page {
implicit val format: Format[Page] = Json.format
}
case class PageQuestion(ref: String, pos: Int, var question: Option[Question])
object PageQuestion {
implicit val format: Format[PageQuestion] = Json.format
}
And the following in my Question microservice.
case class Question(uuid: String, title: String, description: String)
object Question {
implicit val format: Format[Question] = Json.format
}
When I come to re-assemble a Survey object I am using the following function that iterates through the survey and gets the Question data from the question service and then appends it onto the question field of the PageQuestion nested object:
override def getSurvey(id: String) = ServiceCall { request =>
Console.println("getting survey")
refFor(id).ask(GetSurvey).map {
case Some(survey) =>
//below is the function I need help with
amalgamateSurveyWithQuestions(Survey(survey.uuid, survey.name, survey.pages))
case None =>
throw NotFound(s"Survey with id $id")
}
}
//this function below just gets the Question from the questionService and returns a Future[Question]
def getSurveyQuestion(id: String) = questionService.getQuestion(id: String).invoke(NotUsed)
//this function is what I have so far, taking advantage of the var question field in PageQuestion
def amalgamateSurveyWithQuestions(survey: Survey): Survey = {
survey.pages.foreach(pages => {
pages.foreach(page => {
page.questions.foreach(pagequestions => {
pagequestions.foreach(pagequestion => {
getSurveyQuestion(pagequestion.ref).map { question =>
pagequestion.question = Some(question).orElse(None)
}
})
})
})
})
survey
}
Based on the snippets provided, can anyone sggest any better way for achieving what I want to do? From what I understand about case classes, using vars / mutable fields is not encouraged and, somehow, I feel that the way that I have written the amalgamateSurveyWithQuestions()
function is clunky and rather un-scala-like. Would it be better to iterate through the survey.pages array and rebuild a new Survey object using the copy function that gets added to case classes? The reason why I have done it the way above is purely for convenience as it seemed going through all the nested Pages and PageQuestions and copying them into new arrays was a bit of a hassle!
Can anyone with more experience guide me here? If anything is unclear, please let me know. Any help greatly appreciated!
Tom