Search code examples
scalainheritancecase-class

How to share behavior over case classes in scala


Implementing my domain model in scala using case classes I got

abstract class Entity {
    val _id: Option[BSONObjectID]
    val version: Option[BSONLong]
}

and several case classes defining the different entities like

case class Person (
   _id: Option[BSONObjectID],
   name: String, 
   version: Option[BSONLong]
) extends Entity

What I need is a way to set the _id and version later on from a generic method which operates on an Entity because I have to share this behavior over all Entities and want to avoid writing it down hundreds of times ;-). I would love to be able to

def createID(entity: Entity): Entity = {
  entity.copy(_id = ..., version = ...)
}

...but of course this does not compile since an Entity has no copy-method. It is generated for each single case class by the compiler...

What is the best way to achieve this in scala?

To prevent somebody asking: I have to use case classes since this is what the third-party-library is extracting for me from the requests I get and the case class instances are what is serialized back to BSON / MongoDB later on...


Solution

  • Indeed one can find a way to implement something like this at

    Create common trait for all case classes supporting copy(id=newId) method

    but since it is quite complicated for my use case I would prefer just to create two new classes

    class MongoId(var id : BSONObjectID = null) {
        def generate = {
            id = BSONObjectID.generate
        }
    }
    
    class MongoVersion(var version: Long = 0) {
        def update = {
            version = System.currentTimeMillis
        }
    }
    

    and implemented the shared behavior regarding these fields there. Of course you have to change the definition of your base class accordingly:

    abstract class Entity {
        def _id: MongoId
        def version: MongoVersion
    }
    

    To make it clear: This works only if the behavior you want to share over several case classes does only affect (in my case changes) one attribute ...