longevity

A Persistence Framework for Scala and NoSQL

View project on GitHub

key values

Key values are used to uniquely identify a persistent object. They can be embedded in persistents in two different ways. The first way to embed a key value is as a unique identifier for the persistent in question. For example, we may want to stipulate that every User has a unique Username. We can model this in longevity like so:

import longevity.model.annotations.keyVal
import longevity.model.annotations.persistent

@keyVal[DomainModel, User]
case class Username(username: String)

@persistent[DomainModel]
case class User(
  username: Username,
  firstName: String,
  lastName: String)

object User {
  implicit val usernameKey = key(props.username)
}

As with persistents and components, you need to declare your key values in the same package or a subpackage of the package where you model is declared.

The implicit val usernameKey defines the key for our key value type Username. As you can see, this makes use of the object props embedded in object User that we saw towards the bottom of the chapter on persistents. Without the @persistent annotation on the case class, this wouldn’t compile at all. But if we recall how this annotation expands, we see if makes sense. The props.username is put in by the @mprops annotation, and the method key comes from the fact that object User has become a PType (scaladoc here).

We’ll talk more about keys in a later section.

The second way we can embed a KeyVal in a persistent object is as a reference to some other persistent object. As an example, let’s suppose that the users in our domain have an optional sponsor. We can specify the sponsor by providing the sponsor’s username, like so:

import longevity.model.annotations.keyVal
import longevity.model.annotations.persistent

@keyVal[DomainModel, User]
case class Username(username: String)

@persistent[DomainModel]
case class User(
  username: Username,
  firstName: String,
  lastName: String,
  sponsor: Option[Username])

object User {
  implicit val usernameKey = key(props.username)
}

This sponsor field represents a relationship between two persistent objects. In UML, we call this kind of relationship an aggregation. With aggregations, the life cycles of the entities in question are independent.

As we can see in the previous example, key values can appear inside collections. They can also have multiple fields, and appear within components. But they currently cannot contain any collections or polymorphic objects.

We can always look up a persistent object by key value using repository method Repo.retrieve, as we will discuss in a later section.

The non-annotation equivalent for the key value looks like this:

import longevity.model.KVType

case class Username(username: String)

object Username extends KVType[DomainModel, User, Username]

The KVType[DomainModel, User, Username] contains an implicit KVEv[DomainModel, User, Username], which gives us compile-time assurance that the key value supplied to Repo.retrieve is a valid key value in the model.

prev: components
up: the domain model
next: limitations on persistents, components, and key values