Finalize aggregate behavior API
notxcain opened this issue · comments
After all I have a couple of thoughts on how it could be done. There are several decision to be made.
- Decide If there should be a support for traditional snapshot based behavior.
- Should behavior be implemented as a set of functions or using type class instance (is type class appropriate if it is used only in one place)?
There are several options for command handler signature:
Using type class with this signature
/**
* Command[R] - type of command (or entity DSL) that produces result of type R
* State - an entity state
* F[R] - effect on state (not sure about naming) of command handler (see below)
**/
trait CommandHandlerTC[A, Command[_], State, F[_]] {
def handleCommand[R](a: A)(state: State, command: Command[R]): F[R]
}
or this
trait CommandHandlerTC[A, Command[_]] {
type F[X]
type State
def handleCommand[R](a: A)(state: State, command: Command[R]): F[R]
}
without typeclass
type CommandHandlerT[Command[_], State, F[_]] = Command ~> λ[α => State => F[α]]
or
type CommandHandlerT[Command[_], State, F[_]] = λ[α => (Command[α], State)] ~> F
or
trait CommandHandlerT[Command[_], State, F[_]] {
def handleCommand[R](command: Command[R], state: State): F[R]
}
we could go even further, and express effect as some change
type CommandHandler[Command[_], State, Change] = CommandHandlerT[Command, State, (?, Change)]
Here is the very difference between ES and snapshot based entities expressed in types:
type ES[Command[_], State, Event] = CommandHandler[Command, State, Seq[Event]]
type S[Command[_], State] = CommandHandler[Command, State, State]
Event sourcing adds ability to see why state changed compared to just the fact that it happened.
In order to apply change to current state we need one more function of type (State, Change) => State
. The function signature though will depend on actual runtime requirements. e.g. for akka peristence based runtime user would need to provide (State, Event) => State
function, which can be converted to (State, Seq[Event]) => State
so they are kind of identical. For snapshot based runtime it's just a (State, State) => State
with only one obvious implementation.
As for now, in order to drop behavior in Aecor, user should provide:
- Command handler
- Initial state
- Event projection
- Aggregate name (also used to tag all events of same entity).
- Correlation - used to extract entity id from a command.
to be continued...