All Projects → notxcain → Aecor

notxcain / Aecor

Licence: mit
Pure functional event sourcing runtime

Programming Languages

scala
5932 projects

Projects that are alternatives of or similar to Aecor

delta
DDD-centric event-sourcing library for the JVM
Stars: ✭ 15 (-94.98%)
Mutual labels:  reactive, cqrs, distributed, eventsourcing
Jdonframework
Domain-Driven-Design Pub/Sub Domain-Events framework
Stars: ✭ 978 (+227.09%)
Mutual labels:  reactive, eventsourcing, cqrs
Akka Persistence Cassandra
A replicated Akka Persistence journal backed by Apache Cassandra
Stars: ✭ 306 (+2.34%)
Mutual labels:  eventsourcing, akka, cqrs
Liiklus
Reactive (RSocket/gRPC) Gateway for the event-based systems
Stars: ✭ 192 (-35.79%)
Mutual labels:  reactive, eventsourcing, cqrs
Akka
Examples and explanations of how Akka toolkit works
Stars: ✭ 20 (-93.31%)
Mutual labels:  reactive, akka
stem
Event sourcing framework based on ZIO and pluggable runtime (currently working with Akka cluster)
Stars: ✭ 22 (-92.64%)
Mutual labels:  akka, cqrs
assembler
Functional, type-safe, stateless reactive Java API for efficient implementation of the API Composition Pattern for querying/merging data from multiple datasources/services, with a specific focus on solving the N + 1 query problem
Stars: ✭ 102 (-65.89%)
Mutual labels:  reactive, cqrs
pdo-snapshot-store
PDO Snapshot Store
Stars: ✭ 24 (-91.97%)
Mutual labels:  cqrs, eventsourcing
les
Go directly from an event storming to a working API: Event Markdown / Markup validation & NodeJS CQRS/ES application builder.
Stars: ✭ 48 (-83.95%)
Mutual labels:  cqrs, eventsourcing
SplitetFramework
Splitet is a Java based Event Sourcing framework which can be benefited by the teams who are planning to make CQRS transitions with minimum learning curve and ease of adaptation.
Stars: ✭ 159 (-46.82%)
Mutual labels:  cqrs, eventsourcing
scala-web-development
Scala Web 开发 - 使用 Akka
Stars: ✭ 42 (-85.95%)
Mutual labels:  reactive, akka
fmodel-ts
Functional Domain Modeling with Typescript
Stars: ✭ 41 (-86.29%)
Mutual labels:  cqrs, eventsourcing
eventuous
Minimalistic Event Sourcing library for .NET
Stars: ✭ 236 (-21.07%)
Mutual labels:  cqrs, eventsourcing
micro
Functional prooph for microservices
Stars: ✭ 53 (-82.27%)
Mutual labels:  cqrs, eventsourcing
akka-persistence-gcp-datastore
akka-persistence-gcp-datastore is a journal and snapshot store plugin for akka-persistence using google cloud firestore in datastore mode.
Stars: ✭ 18 (-93.98%)
Mutual labels:  akka, cqrs
akka-cookbook
提供清晰、实用的Akka应用指导
Stars: ✭ 30 (-89.97%)
Mutual labels:  reactive, akka
sample-axon-kafka
Sample CQRS and event sourced application developed on top of axon framework.(Kafka is used for distributing the events)
Stars: ✭ 31 (-89.63%)
Mutual labels:  cqrs, eventsourcing
factcast
This project is archived. A friendly fork can be found at https://github.com/factcast/factcast/
Stars: ✭ 14 (-95.32%)
Mutual labels:  cqrs, eventsourcing
Aggregates.net
.NET event sourced domain driven design model via NServiceBus and GetEventStore
Stars: ✭ 261 (-12.71%)
Mutual labels:  eventsourcing, cqrs
eventsourcing-go
Event Sourcing + CQRS using Golang Tutorial
Stars: ✭ 75 (-74.92%)
Mutual labels:  cqrs, eventsourcing

Build Status Maven Central Join the chat at https://gitter.im/notxcain/aecor Scala Steward badge

Aecor

A pure functional library for defining and running eventsourced behaviors

Aecor is an opinionated library to help building scalable, distributed eventsourced services written in Scala.

Built-in runtime implementation uses Akka for distribution and fault tolerance.

It heavily relies on Cats and Cats Effect

Aecor works on Scala 2.12 with Java 8.

The name Aecor (lat. ocean) is inspired by a vision of modern distributed applications as an ocean of messages with pure behaviors floating in it.

Installing Aecor

To start using Aecor Akka Persistence Runtime add the following to your build.sbt file:

scalaVersion := "2.12.7"
scalacOptions += "-Ypartial-unification"
addCompilerPlugin("org.scalameta" % "paradise" % "3.0.0-M11" cross CrossVersion.full)
libraryDependencies += "io.aecor" %% "akka-persistence-runtime" % "x.y.z" // See current version on the badge above

Media

Entity Behavior Definition

In this short guide I'll show you how to define and deploy your first event sourced behavior on runtime backed by Akka Persistence and Akka Cluster Sharding.

Each entity needs an identity, so let's start with identifier type:

final case class SubscriptionId(value: java.util.UUID) extends AnyVal

Then define what actions we're able to perform on Subscription

import aecor.macros.boopickleWireProtocol
import cats.tagless.autoFunctorK
import boopickle.Default._

@boopickleWireProtocol
@autoFunctorK(false)
trait Subscription[F[_]] {
  def createSubscription(userId: String, productId: String, planId: String): F[Unit]
  def pauseSubscription: F[Unit]
  def resumeSubscription: F[Unit]
  def cancelSubscription: F[Unit]
}

You may notice that there is no SubscriptionId involved, and it's okay because this interface describes actions of a concrete Subscription and entity behavior should not know about its identity, it's behavior should be defined solely by its state.

There is an abstract type F[_] which stays for an effect (see Rob Norris, Functional Programming with Effects) that would be performed during each action invocation.

Also being polymorphic in effect improves the reuse of this interface, you'll see it later.

@boopickleWireProtocol - is a macro annotation that automates derivation of a WireProtocol, which is used by Akka Runtime to encode and decode actions and corresponding responses.

We are event sourced, so let's define our events:

import aecor.runtime.akkapersistence.serialization._

sealed abstract class SubscriptionEvent extends Product with Serializable
object SubscriptionEvent {
  final case class SubscriptionCreated(userId: String, productId: String, planId: String) extends SubscriptionEvent
  final case object SubscriptionPaused extends SubscriptionEvent
  final case object SubscriptionResumed extends SubscriptionEvent
  final case object SubscriptionCancelled extends SubscriptionEvent

  implicit val persistentEncoder: PersistentEncoder[SubscriptionEvent] = ???
  implicit val persistentDecoder: PersistentDecoder[SubscriptionEvent] = ???
}

I've intentionally omitted implementation of PersistentEncoder and PersistentDecoder, because providing generic JSON encoding would be careless as persistent event schema requires your attention and I would recommend to use Protobuf or other formats that support schema evolution.

Let's define a state on which Subscription operates.

import aecor.data.Folded.syntax._
import SubscriptionState._

final case class SubscriptionState(status: Status) {
  def update(e: SubscriptionEvent): Folded[Subscription] = e match {
    case SubscriptionCreated(_, _, _) =>
      impossible
    case SubscriptionPaused =>
      subscription.copy(status = Paused).next
    case SubscriptionResumed =>
      subscription.copy(status = Active).next
    case SubscriptionCancelled =>
      subscription.copy(status = Cancelled).next
  }
}

object SubscriptionState {
  def create(e: SubscriptionEvent): Folded[SubscriptionState] = e match {
    case SubscriptionCreated(userId, productId, planId) =>
      Subscription(Active).next
    case _ => impossible
  }
  sealed abstract class Status extends Product with Serializable
    object Status {
      final case object Active extends Status
      final case object Paused extends Status
      final case object Cancelled extends Status
    }
}

Pay attention to Folded datatype, it has to constructor:

  • Impossible is used to express impossible folds of events, so that you don't throw exceptions.
  • Next(a: A) is used to express successful event application.

Now, the final part before we launch.'

As I said earlier Subscription[F[_]] is polymorphic in its effect type.

Our effect would be any F[_] with instance of MonadAction[F, Option[SubscriptionState], SubscriptionEvent] which provides essential operations for eventsources command handler

  • read: F[Option[SubscriptionState]] - reads current state
  • append(event: SubscriptionEvent, other: SubscriptionEvent*): F[Unit] - append one or more events Other stuff like state recovery and event persistence is held by Akka Persistence Runtime.

So lets define SubscriptionActions

import cats.implicits._

final class SubscriptionActions[F[_]](
  implicit F: MonadAction[F, Option[SubscriptionState], SubscriptionEvent]
  ) extends Subscription[F] {

  import F._ // import algebra functions

  def createSubscription(userId: String, productId: String, planId: String): F[Unit] =
    read.flatMap {
      case Some(subscription) =>
        ignore
      case None =>
        // Produce event
        append(SubscriptionCreated(userId, productId, planId))
    }

  def pauseSubscription: F[Unit] =
    read.flatMap {
      case Some(subscription) if subscription.status == Active =>
        append(SubscriptionPaused)
      case _ =>
        ignore
    }

  def resumeSubscription: F[Unit] =
    read.flatMap {
      case Some(subscription) if subscription.status == Paused =>
        append(SubscriptionResumed)
      case _ =>
        ignore
    }
   
  def cancelSubscription: F[Unit] =
    read.flatMap {
      case Some(subscription) if subscription.canCancel =>
        append(SubscriptionCancelled)
      case _ =>
        ignore
    }
}

Now when actions are defined we're ready to deploy

import cats.effect.IO
import aecor.runtime.akkapersistence._

val system = ActorSystem("system")

val journalAdapter = CassandraJournalAdapter(system)

val runtime = AkkaPersistenceRuntime(system, journalAdapter)

val behavior: EventsourcedBehavior[Subscription, IO, Option[SubscriptionState], SubscriptionEvent]  
  EventsourcedBehavior.optional(
    new SubscriptionActions,
    Fold.optional(SubscriptionState.create)(_.update(_))
  )

val deploySubscriptions: IO[SubscriptionId => Subscription[IO]] =
  runtime.deploy(
    "Subscription",
    behavior,
    Tagging.const[SubscriptionId](EventTag("Subscription"))
  )

Projections

val journalQuery = runtime.journal

Adopters

Using Aecor in your organization? Send us a PR to list your company here:

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].