Spekka Stateful
To use this library add the following dependencies:
- sbt
val AkkaVersion = "2.6.16" libraryDependencies ++= Seq( "io.github.spekka" %% "spekka-stateful" % "0.1.1", "com.typesafe.akka" %% "akka-stream" % AkkaVersion, "com.typesafe.akka" %% "akka-stream-typed" % AkkaVersion, "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion )
- Maven
<properties> <akka.version>2.6.16</akka.version> <scala.binary.version>2.13</scala.binary.version> </properties> <dependencies> <dependency> <groupId>io.github.spekka</groupId> <artifactId>spekka-stateful_${scala.binary.version}</artifactId> <version>0.1.1</version> </dependency> <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-stream_${scala.binary.version}</artifactId> <version>${akka.version}</version> </dependency> <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-stream-typed_${scala.binary.version}</artifactId> <version>${akka.version}</version> </dependency> <dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-actor-typed_${scala.binary.version}</artifactId> <version>${akka.version}</version> </dependency> </dependencies>
- Gradle
def versions = [ AkkaVersion: "2.6.16", ScalaBinary: "2.13" ] dependencies { implementation "io.github.spekka:spekka-stateful_${versions.ScalaBinary}:0.1.1" implementation "com.typesafe.akka:akka-stream_${versions.ScalaBinary}:${versions.AkkaVersion}" implementation "com.typesafe.akka:akka-stream-typed_${versions.ScalaBinary}:${versions.AkkaVersion}" implementation "com.typesafe.akka:akka-actor-typed_${versions.ScalaBinary}:${versions.AkkaVersion}" }
Motivation
Working with stateful stream is at the same time a quite common and complex task.
While Akka Streams does offer all the required building blocks to work with stateful flows, the overall experience is hampered by the low level nature of the library: it’s often necessary to drop down to the GraphStage
API level to implement common tasks and in general the user its left without guidance in the design of the solution. Furthermore there is no stream native encoding of persistent stateful flows, requiring users to create their own custom solution.
The goal of Spekka Stateful is to provide an opinionated, stream native way to define stateful flow with the following properties:
- clean distinction between business-logic and state-management
- structured way to interact with stateful flows from outside the stream
- state persistence support
Overview
The idea of the library is that a stateful flow is defined in terms of:
- The business logic:
StatefulFlowLogic
- The way to manage the state:
StatefulFlowBackend
A StatefulFlowLogic[State, In, Out, Command]
is responsible of defining how the state is altered in response to a stream element / command: it encodes the business logic of the stream. The logic doesn’t need to concern itself on the details of how the state is managed as this will be handled by an instance of StatefulFlowBackend
.
Combining a logic and a backend instance we obtain a StatefulFlowProps
which is a complete description of how the flow will work. Since the same stateful flow could be used for multiple purposes we need a way to tie the flow description to a specific semantic instance.
To achieve this we register the flow description on a StatefulFlowRegistry
specifying the entityKind
of the flow (i.e. which kind of entities will be computed). The StatefulFlowBuilder
which we get as a result, encodes both how the flow works (what it computes and how it manages the state) as well as what the flow purpose is.
The StatefulFlowBuilder
can now be used to obtain flow for particular entityId
via the method StatefulFlowBuilder.flow
. The result is a standard Akka Flow
with a materialized value containing a StatefulFlowControl
object which can be used to interact with the flow from outside the stream by sending commands.
The library supports 2 kinds of stateful flows:
- Event based: stream elements and commands do not affect the state directly, but they cause the generation of events which are then used to modify the state. This approach is well suited to implement event sourced systems.
- Durable state: stream elements and commands do affect the state directly: a new state is returned (and if required persisted) after each message/command handling.