I'm building the following api for Users in play scala:
case class User(_id: BSONObjectId, username: String)
The issue is that when the client sends a request in order to store/create a new user the _id is not present.
One viable solution seemed to be:
case class User(_id: Option[BSONObjectId], username: String)
However, it seemed a bit annoying to check for the option field not only for inserting the user, but for the other CRUD operations.
One suggestions that I got was to have two types:
case class User(name: String) ; type UserWithId = (Id, User)
or
case class User[A](id: A, name: String); type UserWithId = User[BSONObjectId]
type UserWithoutId = User[Unit]
I chose the latter implementation as it seemed appropiate. I created the proper Json Formats to deal with both cases insertion and the other operations:
object User {
lazy val userWithIdFormat: OFormat[UserWithId] = Json.format[UserWithId]
lazy val userWithoutIdFormat: OFormat[UserWithoutId] = Json.format[UserWithoutId]
}
However, now I faced an issue when Inserting, how to convert elegantly from UserWithoutId => UserwithId after the Id has been generated
So the next step was to create this method:
case class User[A](_id: A, username: String)
{
def map[B](f: A => B): User[B] = ???
}
Is this some type of Monad that I need to implement? (Just learning category theory now)
I guess the easiest way is to just add an "initialized/empty" state where the case Class is required to always have an Id, and only when it's persisted it will have username.
Something like val initalizedUser = User(BSONObjectId.generate(), "")
What is the best approach here? Are using Types would improve performance wise and make the domain more rich, or just adding a state will make it more approachable for future colleagues
What would be the implementation for map, is this something I should extract and have my own Monad implementation to be applied for all future collections?
Is it better to reach for a functional library to solve this issue or not yet?