Event Sourcing and Polymorphism

Hi all,

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: ManualTestRun, ComplexAutomatedTestRun, 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 TestRunStarted.

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 TestRunEvent and TestRunState.

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?

1 Like