When I initially started working on longevity, I was definitely in a traditional Domain Driven Design mindset, where persistence is mainly about storing entity aggregates. It is quite natural to make the aggregate root as the basic unit of persistence, and so the longevity API was initially built up with the Root playing the primary role. For example, all the Repo methods took roots as arguments, and returned roots as values.
At some point I realized that this was a bit of a mistake. We don't only persist aggregates. We need to persist events when building an event-driven system, or when we are doing event sourcing. And we need to persist views to store data to support queries in a CQRS based system. Such views are typically derivative of aggregates or events, reorganizing the data for fast query response.
These are some obvious examples of things we persist that are not aggregates, but there could be other things as well. For example, maybe you want to persist all the commands that come into your system, with an indication of what happened when a command was rejected. The bottom line is, I don't want to limit your thinking, or force you to use terminology that doesn't make sense, (such as calling an event a root so that you can actually persist it), to do what you are trying to do.
With all this in mind, I've replaced the Root and RootType in the longevity API with Persistent and PType, respectively. And I've built a couple of parallel hierarchies for these, so you can use the terminology that makes sense to you:
- Persistent / PType
- Root / RootType
- Event / EventType
- ViewItem / View
At the moment, all these types are equivalent from longevity's point of view. There is nothing you can do with a Root that you cannot do with an Event, ViewItem, or Persistent. In the future, we would like to add support for event-driven systems and CQRS systems. We are also very interested in looking into integration with other like-minded Scala frameworks, such as Akka persistence. But for the time being, we are really focused on getting the persistence story right. And one key aspect of the persistence story is that we find ourselves storing lots of different kinds of things, depending on the circumstances.
To read more about these changes, please check out the persistent entities chapter of the longevity user manual.
Before wrapping up, I'd like to shout out to Douglas Alan, who pointed out a flaw in the longevity API that hadn't occurred to me. In prior releases, the repository methods used the global execution context to construct futures. As of 0.6, users provide their own implicit execution context when calling the repository methods. While this gives you full control over the execution context, if you don't really care, you only need to import the global execution context wherever you make use of the repositories.
I'm planning to put another post out next week describing the other major API change in this release: we've totally revamped the way you specify the keys and indexes in your persistent type. The new API is a lot less kludgy, and is very easy for you to use. All in all, I feel like this release solidifies the longevity API. There is still plenty of work to do there - such as queries returning reactive streams instead of Future[Seq[_]] - but the core of the API is solid. I'm really looking forward to turning my attention back to missing features for the next release.