diff --git a/core/src/main/scala/endless/core/entity/Deployer.scala b/core/src/main/scala/endless/core/entity/Deployer.scala new file mode 100644 index 00000000..f8d52011 --- /dev/null +++ b/core/src/main/scala/endless/core/entity/Deployer.scala @@ -0,0 +1,86 @@ +package endless.core.entity + +import cats.effect.kernel.Resource +import endless.core.entity.Deployer.{InterpretedEffector, InterpretedEntity, InterpretedRepository} +import endless.core.event.EventApplier +import endless.core.interpret.EffectorT.EffectorT +import endless.core.interpret.EntityT +import endless.core.protocol.CommandProtocol + +/** `Deployer` deploys entity repositories by assembling the required interpreters and components. + */ +trait Deployer { + + /** Deploys an entity repository in context `F`, returning an instance of its algebra wrapped in a + * resource (since deployments typically require finalization). + * + * Repository operation uses the three provided interpreters in combination, following a strictly + * defined sequence: + * - the interpreted repository is used to create a handle on the entity with the specified ID, + * which implements the entity algebra. This allows further interaction with the entity by + * the caller of the repository algebra. + * - the interpreted entity runs invoked function on the entity algebra: this possibly involves + * reading the entity state (e.g. for validation), and writing events (which affect the + * state) + * - after events were written, a possible side-effect is triggered: this can be asynchronous + * (i.e. the function doesn't wait for completion) + * - the function finally returns to the caller of entity algebra + * + * This interaction pattern occurs with "actor-like" semantics: all calls on the entity are + * processed in sequence. + * + * @param repository + * provides access to to an entity with a specific ID via its algebra + * @param entity + * runs calls on entity algebra + * @param effector + * handles side-effects after entity interaction + * @param nameProvider + * provides a name for the entity (in other words, the "type of entity", e.g. "booking") + * @param commandProtocol + * protocol-centric definition of entity algebra: defines a wire encoding for interactions with + * remote entities + * @param eventApplier + * defines how events are applied to the entity state + * @tparam ID + * entity ID + * @tparam S + * entity state + * @tparam Alg + * entity algebra + * @tparam RepositoryAlg + * repository algebra + * @tparam EffectorAlg + * effector algebra + * @return + * a resource encapsulating access to the repository algebra + */ + def deployRepository[F[_], ID, S, E, Alg[_[_]], RepositoryAlg[_], EffectorAlg[_]]( + repository: InterpretedRepository[F, ID, Alg, RepositoryAlg], + entity: InterpretedEntity[F, S, E, Alg], + effector: InterpretedEffector[F, S, Alg, RepositoryAlg, EffectorAlg] + )(implicit + nameProvider: EntityNameProvider[ID], + commandProtocol: CommandProtocol[Alg], + eventApplier: EventApplier[S, E] + ): Resource[F, RepositoryAlg[F]] + +} + +object Deployer { + trait InterpretedEntity[F[_], S, E, Alg[_[_]]] { + def apply(entity: Entity[EntityT[F, S, E, *], S, E]): F[Alg[EntityT[F, S, E, *]]] + } + + trait InterpretedRepository[F[_], ID, Alg[_[_]], RepositoryAlg[_]] { + def apply(repository: Repository[F, ID, Alg]): F[RepositoryAlg[F]] + } + + trait InterpretedEffector[F[_], S, Alg[_[_]], RepositoryAlg[_], EffectorAlg[_]] { + def apply( + effector: Effector[EffectorT[F, S, Alg, *], S, Alg], + repositoryAlg: RepositoryAlg[F], + entityAlg: Alg[F] + ): F[EffectorAlg[F]] + } +}