Code Monkey home page Code Monkey logo

scala-cqrs's Introduction

Proof of concept - CQRS in Scala

API

  type Identifier = String
  def newIdentifier: Identifier = java.util.UUID.randomUUID().toString

  type Revision = Long


  case class AggregateRoot[A](id: Identifier, revision: Long, value: A) {
    def nextRevision(value: A): AggregateRoot[A] = copy(value = value, revision = revision + 1)
  }

  object AggregateRoot {
    def apply[A](value: A): AggregateRoot[A] = apply(newIdentifier, value)
    def apply[A](id: Identifier, value: A): AggregateRoot[A] = AggregateRoot(id, -1, value)
  }

  case class Command[C](aggregateId: Identifier, value: C)
  case class Event[E](aggregateId: Identifier, value: E)

  trait Repository[A] {
    def save(ar: AggregateRoot[A])
    def get(aggregateId: Identifier): Option[AggregateRoot[A]]
  }

  trait EventStore {
    def add(event: Event[_])
    def stream[E](aggregateId: Identifier)(implicit tag: ClassTag[E]): Stream[Event[E]]
    def stream: Stream[Event[_]]
  }

  trait Context[A, C, E] {
    def execute(command: C): E
    def applyEvent(value: A, event: E): A
    def newInstance: A
    def repository: Repository[A]
  }

  trait CommandHandler {
    def eventStore: EventStore

    def apply[A, C, E](command: Command[C])(implicit context: Context[A, C, E]): AggregateRoot[A] = {

      val aggregateId = command.aggregateId

      val ar = context.repository.get(aggregateId) getOrElse AggregateRoot(context.newInstance)

      val event = context.execute(command.value)

      eventStore.add(Event(aggregateId, event))

      val result = ar.nextRevision(context.applyEvent(ar.value, event))

      context.repository.save(result)

      result
    }
  }

Example

object InventoryItemExample {
  val eventStore: EventStore = EventStoreInMemory

  case class InventoryItem(name: String, activated: Boolean)
  sealed  trait InventoryItemEvent
  sealed trait InventoryItemCommand

  case object DeactivateInventoryItem extends InventoryItemCommand
  case class CreateInventoryItem(name: String) extends InventoryItemCommand
  case class RenameInventoryItem(newName: String) extends InventoryItemCommand
  case class CheckInItemsToInventory(count: Int) extends InventoryItemCommand
  case class RemoveItemsFromInventory(count: Int) extends InventoryItemCommand


  case object InventoryItemDeactivated extends InventoryItemEvent
  case class InventoryItemCreated(name: String) extends InventoryItemEvent
  case class InventoryItemRenamed(name: String) extends InventoryItemEvent
  case class ItemsCheckedInToInventory(count: Int) extends InventoryItemEvent
  case class ItemsRemovedFromInventory(count: Int) extends InventoryItemEvent

  object InventoryItemContext extends Context[InventoryItem, InventoryItemCommand, InventoryItemEvent] {
    def execute(command: InventoryItemCommand) = command match{
      case DeactivateInventoryItem => InventoryItemDeactivated

      case CreateInventoryItem(name) => InventoryItemCreated(name)

      case RenameInventoryItem(newName) =>
        require(newName != null)
        InventoryItemRenamed(newName)

      case CheckInItemsToInventory(count) =>
        require(count > 0)
        ItemsCheckedInToInventory(count)

      case RemoveItemsFromInventory(count) =>
        require(count > 0)
        ItemsRemovedFromInventory(count)
    }

    def applyEvent(value: InventoryItem, event: InventoryItemEvent) = event match {
      case InventoryItemDeactivated => value.copy(activated = false)
      case InventoryItemCreated(name) => value.copy(name = name)
      case InventoryItemRenamed(newName) => value.copy(name = newName)
      case ItemsCheckedInToInventory(count) => value
      case ItemsRemovedFromInventory(count) => value
    }

    def newInstance = InventoryItem("", activated = false)

    val repository = new RepositoryInMemory(eventStore, this)
  }


  val itemId = newIdentifier
  on(InventoryItemContext) handle Command(itemId, CreateInventoryItem("my item"))
  on(InventoryItemContext) handle Command(itemId, RenameInventoryItem("my renamed item"))
  on(InventoryItemContext) handle Command(itemId, CheckInItemsToInventory(2))
  on(InventoryItemContext) handle Command(itemId, RemoveItemsFromInventory(1))
  on(InventoryItemContext) handle Command(itemId, DeactivateInventoryItem)


  val handler = new CommandHandler {
    def eventStore = InventoryItemExample.eventStore
  }
  def on[A, C, E](context: Context[A, C, E]) = new {
    def handle(command: Command[C]): AggregateRoot[A] = handler(command)(context)
  }
}

scala-cqrs's People

Contributors

t3hnar avatar

Stargazers

Mark van Buskirk avatar Alexander Mikuta avatar Alexander Sidorenko avatar Kamil Zajac avatar Mateusz Maciaszek avatar

Watchers

 avatar Maksym Fedorov avatar Olger Warnier avatar James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.