trait CommandProtocol[Alg[_[_]]] {
  def server[F[_]]: Decoder[IncomingCommand[F, Alg]]
  def client: Alg[OutgoingCommand[*]]

CommandProtocol is to be implemented for each entity algebra. It provides a client interpretation wrapping each function into a OutgoingCommand context and a server decoder which can deserialize an incoming command into IncomingCommand.

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, client materializes algebra invocations into concrete serializable outgoing commands and server acts as a switchboard for incoming commands. See BookingCommandProtocol for a concrete example.

Explicit or implicit representations

CommandProtocol is the entry point for implementations to map algebra entries to concrete commands and replies. We tend to prefer explicit materialization for migration safety but nothing prevents protocol implementers to opt for automatic serialization via macros. 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.


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.