From 81f93f53fa722a8f935367c6f705c198c3e4ee65 Mon Sep 17 00:00:00 2001 From: Jonas Chapuis Date: Sun, 12 Dec 2021 21:42:40 +0100 Subject: [PATCH] Avoid triggering side-effects for read-only commands --- documentation/src/main/paradox/effector.md | 6 +++++- .../src/main/scala/endless/runtime/akka/Deployer.scala | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/paradox/effector.md b/documentation/src/main/paradox/effector.md index 9df0249e..39d0cd46 100644 --- a/documentation/src/main/paradox/effector.md +++ b/documentation/src/main/paradox/effector.md @@ -14,10 +14,14 @@ trait Self[F[_], Alg[_[_]]] { trait Effector[F[_], S] extends StateReader[F, S] with Passivator[F] with Self[F] ``` -@scaladoc[Effector](endless.core.entity.Effector) is a typeclass used to describe side effects occurring **after** event persistence and entity recovery. +@scaladoc[Effector](endless.core.entity.Effector) is a typeclass used to describe side effects occurring **after** event persistence and entity recovery. Side-effects are typically asynchronous operations such as kafka writes, outgoing REST requests, and [entity passivation](https://doc.akka.io/docs/akka/current/typed/cluster-sharding.html#passivation) (flushing out of memory). `Effector` is used in a `Effector => F[Unit]` function provided upon entity deployment (e.g. @github[BookingEffector](/example/src/main/scala/endless/example/logic/BookingEffector.scala)). In the provided Akka runtime, the resulting `F[Unit]` is executed in *run & forget* mode so that command reply is not delayed by any lengthy side-effect (`Self` can be used to notify success or failure of asynchronous operations back to the entity). +@@@ warning +In the provided Akka runtime, read-only commands (commands that do not generate events) do not trigger side-effects, which corresponds to sound practice. +@@@ + @@@ note Defining an effector is entirely optional with the Akka runtime, pass-in `(_, _) => EffectorT.unit` in @scaladoc[deployEntity](endless.runtime.akka.Deployer) to disable effector. @@@ diff --git a/runtime/src/main/scala/endless/runtime/akka/Deployer.scala b/runtime/src/main/scala/endless/runtime/akka/Deployer.scala index f4667742..b5bb7a10 100644 --- a/runtime/src/main/scala/endless/runtime/akka/Deployer.scala +++ b/runtime/src/main/scala/endless/runtime/akka/Deployer.scala @@ -231,7 +231,7 @@ trait Deployer { .flatMap { case Left(error) => Logger[F].warn(error) >> Effect.unhandled[E, Option[S]].thenNoReply().pure - case Right((events, reply)) => + case Right((events, reply)) if events.nonEmpty => Effect .persist(events.toList) .thenRun((state: Option[S]) => @@ -250,6 +250,12 @@ trait Deployer { Reply(incomingCommand.replyEncoder.encode(reply)) } .pure + case Right((_, reply)) => + Effect + .reply[Reply, E, Option[S]](command.replyTo)( + Reply(incomingCommand.replyEncoder.encode(reply)) + ) + .pure } dispatcher.unsafeRunSync(effect) }