A Persistence Framework for Scala and NoSQL

View project on GitHub


Once we get our hands on a persistent state, we can use PState.map to modify the aggregate:

val username = Username("smithy")
val retrieved: Future[PState[User]] = repo.retrieveOne[User](username)
val modified: Future[PState[User]] = retrieved map { userState =>
  userState.modify(_.copy(fullname = "John Smith Jr."))

Like most of the Repo API calls, Repo.update requires an implicit parameter longevity.model.PEv[M, P]. This implicit evidence ensures that the type P is actually a persistent class in the domain model.

We can now persist our changes with Repo.update:

val updated: Future[PState[User]] = modified.map { userState =>

All this looks much nicer using for comprehensions:

val username = Username("smithy")
val updated: Future[PState[User]] = for {
  retrieved <- repo.retrieveOne(username)
  modified  =  retrieved.modify(_.copy(fullname = "John Smith Jr."))
  updated   <- repo.update(modified)
} yield updated

We can continue to manipulate the persistent state returned by Repo.update, and pass it on to further calls to update or delete.

Unless the key value is for a primary key, there is nothing at present that prevents you from modifying the contents of a key value for a persistent object. Consequently, it is possible for update to fail by attempting to put in a duplicate key value. See the note at the bottom of the section on repo.create for more information on duplicate keys.

The resulting PState result may well be different from the PState taken as input. For example, the aggregate’s revision counter may have been updated. In this case, re-using (or continuing to use) the input PState could result in an optimistic locking failure. In general, you should consider a PState passed to Repo.update as no longer valid.

prev: repo.retrieve
up: the repository
next: repo.delete