Skip to content

Instantly share code, notes, and snippets.

@szoio
Last active September 17, 2018 04:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save szoio/4bd99d24316a58b0fef2185c88c22879 to your computer and use it in GitHub Desktop.
Save szoio/4bd99d24316a58b0fef2185c88c22879 to your computer and use it in GitHub Desktop.
Event Sourcing Interpreter
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