Last active
September 17, 2018 04:18
-
-
Save szoio/4bd99d24316a58b0fef2185c88c22879 to your computer and use it in GitHub Desktop.
Event Sourcing Interpreter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package stephenzoio.kafka_tests.shared.eventsrc | |
import java.util.UUID | |
import cats._ | |
import cats.implicits._ | |
import cats.data.{EitherK, WriterT} | |
import cats.free.Free | |
import stephenzoio.kafka_tests.shared.free.FreeOp | |
trait EventSourcable { | |
// abstract definitions | |
type C[_] // command | |
type E[_] // update | |
type Q[_] // query | |
type Key // event key | |
type Event // event value | |
final case class EventSpec(key: Key, event: Event, commandId: UUID) | |
final case class CommandSpec(commandId: UUID) | |
def asEventSpec[A]: E[A] => EventSpec | |
def asCommandSpec[A]: C[A] => CommandSpec | |
def c2Id: C ~> Id | |
// the logging algebra | |
sealed trait L[A] extends FreeOp[L, A] with Product with Serializable | |
final case class Append(eventSpec: List[EventSpec]) extends L[Unit] | |
final case class Exists(commandId: UUID) extends L[Boolean] | |
// free of the Coproduct of Q and E (the language commands are interpreted into) | |
type EQ[A] = Free[EitherK[Q, E, ?], A] | |
def c2MLogged[M[_]](c2U: C ~> EQ, u2M: E ~> M, q2M: Q ~> M, e2M: L ~> M)(implicit M: Monad[M]): C ~> M = { | |
type WM[A] = WriterT[M, List[EventSpec], A] | |
val u2W: E ~> WM = | |
new (E ~> WM) { | |
override def apply[A](fa: E[A]): WM[A] = | |
WriterT[M, List[EventSpec], A](u2M(fa).map(x => (List(asEventSpec(fa)), x))) | |
} | |
val q2W: Q ~> WM = new (Q ~> WM) { | |
override def apply[A](fa: Q[A]): WM[A] = | |
WriterT[M, List[EventSpec], A](q2M(fa).map(x => (List.empty, x))) | |
} | |
def c2W: C ~> WM = new (C ~> WM) { | |
override def apply[A](fa: C[A]) = c2U(fa).foldMap(q2W or u2W) | |
} | |
new (C ~> M) { | |
override def apply[A](fa: C[A]): M[A] = for { | |
exists <- Exists(asCommandSpec(fa).commandId).liftF.foldMap(e2M) | |
a <- if (exists) c2Id(fa).pure[M] | |
else | |
c2W.apply(fa).run.flatMap { | |
case (updateList, a) => Append(updateList).liftF.foldMap(e2M) >> a.pure[M] | |
} | |
} yield a | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment