I have a question regarding the design of my persistent actor system. Let’s say I try to model a set of different but closely related types of objects:
SimpleTestRun (just examples) that all are types of a “TestRun” in the sense that
- the concept “TestRun” is part of the user’s mental model and the sub-types are only instantiations of those,
- different kinds of TestRuns can be used in the same use cases,
- different types of TestRuns can be associated with the same kinds of other entities, e.g. a “TestReport” may refer to a collection of TestRuns of different types.
These different kinds of TestRuns are polymorphic and “deeply modeled” in the sense that they have a common, narrow interface, e.g.
StartTestRun() but may have completely different internal behavior. They are implemented using state machines that may vary wildly in complexity.
In a non-actor world, I would model TestRuns either with inheritance, with e.g.
ManualTestRun being a sub-class of
TestRun, or via delegation, where a TestRun uses some subtype specific strategy (which, in turn, may use the State pattern internally).
With actors and in particular with persistent actors, I am struggling how to model this relationship. In an ideal world, all my sub-TestRuns have very specific Domain Events of their own and related to that different States and state machines and only share a basic set of basic events such as
Without event sourcing I would just implement a
TestRunManager actor that creates concrete sub-TestRuns and forwards commands to those actors. The sub-type actors themselves are completely autonomous. The Open-Closed principle could be respected as much as possible and the world would be good.
With Akka event sourcing, things are a bit different. I could either make
TestRun a PersistentEntity. Then, I would only have access to a very coarse-grained state model (
Running, etc.) and very generic events (
TestRunStarted etc.) or I would lose type safety and protected-variation by deriving all possible event and state subtypes from
Alternatively, I could have a PersistentEntity for every sub-type. In this case, I “only” see the problem that my
TestRunManager would have to know what sub type a command is intended for to choose the correct EntityRef, which may (or will) lead to a leaky abstraction (e.g. when every command has a
testRunType field or the test run ids bear type information).
What do you think? Are there any best practices for situations like this?