CommandProtocol
trait CommandProtocol[Alg[_[_]]] {
def server[F[_]]: Decoder[IncomingCommand[F, Alg]]
def clientFor[F[_]](id: ID)(implicit sender: CommandSender[F, ID]): Alg[F]
}
CommandProtocol
is to be implemented for each entity algebra. It provides an RPC-like client
in charge of transforming calls into instances of OutgoingCommand
, delivering such commands to the targeted entity using a CommandSender
and decoding the reply. It also defines the corresponding server
, which can decode commands into IncomingCommand
instances, run the corresponding entity logic and encode the reply.
OutgoingCommand
is able to encode the command into a binary representation ready to be sent over the wire and also decode the expected subsequent reply. IncomingCommand
is able to decode the incoming command, invoke the corresponding handler and encode the reply.
In other words, clientFor
materializes algebra invocations into outgoing commands delivered to server
, which acts as a switchboard for incoming commands. See BookingCommandProtocol for a concrete example.
CommandProtocol
is the entry point for implementations to map algebra entries to concrete commands and replies. Having these lower-level aspects described separately makes it easier to have precise control of versions and to deal with migration challenges.
We provide helpers for definition of binary protocols in endless-protobuf-helpers
as well as endless-scodec-helpers
and JSON protocols in endless-circe-helpers
.
Definition of protobuf protocols is very convenient using endless-protobuf-helpers
because scalaPB-generated types can be referenced directly, there is no need for a separate representation of commands (and associated data-mapping headaches).
Command protocols can be tested in isolation via synchronous round-trip exercise of the journey client invocation -> command materialization -> command encoding -> command decoding -> behavior invocation -> reply materialization -> reply encoding -> reply decoding. See BookingCommandProtocolSuite for an example.