All Projects → andrewwebber → Cqrs

andrewwebber / Cqrs

Licence: apache-2.0
cqrs framework in go

Programming Languages

go
31211 projects - #10 most used programming language
golang
3204 projects

Projects that are alternatives of or similar to Cqrs

Nestjs Cqrs Starter
NestJS CQRS Microservices Starter Project
Stars: ✭ 80 (-55.31%)
Mutual labels:  microservices, event-sourcing, eventstore, cqrs
Ultimate Backend
Multi tenant SaaS starter kit with cqrs graphql microservice architecture, apollo federation, event source and authentication
Stars: ✭ 978 (+446.37%)
Mutual labels:  microservices, event-sourcing, eventstore, cqrs
Cronus
Cronus is a lightweight framework for building event driven systems with DDD/CQRS in mind
Stars: ✭ 139 (-22.35%)
Mutual labels:  microservices, event-sourcing, cqrs
Eventstore
Event store using PostgreSQL for persistence
Stars: ✭ 729 (+307.26%)
Mutual labels:  event-sourcing, eventstore, cqrs
Event Sourcing Castanha
An Event Sourcing service template with DDD, TDD and SOLID. It has High Cohesion and Loose Coupling, it's a good start for your next Microservice application.
Stars: ✭ 68 (-62.01%)
Mutual labels:  event-sourcing, eventstore, cqrs
Event Sourcing Microservices Example
Learn about building microservices with event sourcing using Spring Boot and how to deploy a social network to Kubernetes using Docker Compose or Helm.
Stars: ✭ 167 (-6.7%)
Mutual labels:  microservices, event-sourcing, cqrs
Pitstop
This repo contains a sample application based on a Garage Management System for Pitstop - a fictitious garage. The primary goal of this sample is to demonstrate several software-architecture concepts like: Microservices, CQRS, Event Sourcing, Domain Driven Design (DDD), Eventual Consistency.
Stars: ✭ 708 (+295.53%)
Mutual labels:  microservices, event-sourcing, cqrs
Akkatecture
a cqrs and event sourcing framework for dotnet core using akka.net
Stars: ✭ 414 (+131.28%)
Mutual labels:  microservices, event-sourcing, cqrs
Extreme
Elixir Adapter for EventStore
Stars: ✭ 110 (-38.55%)
Mutual labels:  event-sourcing, eventstore, cqrs
Bifrost
This is the stable release of Dolittle till its out of alpha->beta stages
Stars: ✭ 111 (-37.99%)
Mutual labels:  event-sourcing, eventstore, cqrs
Event Sourcing Jambo
An Hexagonal Architecture with DDD + Aggregates + Event Sourcing using .NET Core, Kafka e MongoDB (Blog Engine)
Stars: ✭ 159 (-11.17%)
Mutual labels:  microservices, event-sourcing, cqrs
Todomvc Ddd Cqrs Eventsourcing
Implementation of basic Todo app via tastejs/todomvc in C#/Typescript with eventsourcing, cqrs, and domain driven design
Stars: ✭ 134 (-25.14%)
Mutual labels:  microservices, eventstore, cqrs
Equinoxproject
Full ASP.NET Core 5 application with DDD, CQRS and Event Sourcing concepts
Stars: ✭ 5,120 (+2760.34%)
Mutual labels:  event-sourcing, eventstore, cqrs
Eventflow.example
DDD+CQRS+Event-sourcing examples using EventFlow following CQRS-ES architecture. It is configured with RabbitMQ, MongoDB(Snapshot store), PostgreSQL(Read store), EventStore(GES). It's targeted to .Net Core 2.2 and include docker compose file.
Stars: ✭ 131 (-26.82%)
Mutual labels:  event-sourcing, eventstore, cqrs
Eventstore
The stream database optimised for event sourcing
Stars: ✭ 4,395 (+2355.31%)
Mutual labels:  event-sourcing, eventstore, cqrs
Messagebus
A MessageBus (CommandBus, EventBus and QueryBus) implementation in PHP7
Stars: ✭ 178 (-0.56%)
Mutual labels:  event-sourcing, event-handlers, cqrs
Go Api Boilerplate
Go Server/API boilerplate using best practices DDD CQRS ES gRPC
Stars: ✭ 373 (+108.38%)
Mutual labels:  microservices, event-sourcing, cqrs
Netcoremicroservicessample
Sample using micro services in .NET Core 3.1 Focusing on clean code
Stars: ✭ 403 (+125.14%)
Mutual labels:  microservices, event-sourcing, cqrs
Commanded
Use Commanded to build Elixir CQRS/ES applications
Stars: ✭ 1,280 (+615.08%)
Mutual labels:  event-sourcing, eventstore, cqrs
Eshoponcontainersddd
Fork of dotnet-architecture/eShopOnContainers in full DDD/CQRS design using my own patterns
Stars: ✭ 126 (-29.61%)
Mutual labels:  microservices, eventstore, cqrs

CQRS framework in go

Join the chat at https://gitter.im/andrewwebber/cqrs GoDoc Build Status

Project Summary

The package provides a framework for quickly implementing a CQRS style application. The framework attempts to provides helpful functions to facilitate:

  • Event Sourcing
  • Command issuing and processing
  • Event publishing
  • Read model generation from published events

Example code

Example test scenario (inmemory)

Example test scenario (couchbase, rabbitmq)

Example CQRS scaleout/concurrent test

Test Scenario

The example test scenario is of a simple bank account that seeks to track, using event sourcing, a customers balance and login password

The are two main areas of concern at the application level, the Write model and Read model. The read model is aimed to facilitate fast reads (read model projections) The write model is where the business logic get executed and asynchronously notifies the read models

Write model - Using Event Sourcing

Account

type Account struct {
  cqrs.EventSourceBased

  FirstName    string
  LastName     string
  EmailAddress string
  PasswordHash []byte
  Balance      float64
}

To compensate for golang's lack of inheritance, a combination of type embedding and a call convention pattern are utilized.

func NewAccount(firstName string, lastName string, emailAddress string, passwordHash []byte, initialBalance float64) *Account {
  account := new(Account)
  account.EventSourceBased = cqrs.NewEventSourceBased(account)

  event := AccountCreatedEvent{firstName, lastName, emailAddress, passwordHash, initialBalance}
  account.Update(event)
  return account
}

The 'attached' Update function being called above will now provide the infrastructure for routing events to event handlers. A function prefixed with 'Handle' and named with the name of the event expected with be called by the infrastructure.

func (account *Account) HandleAccountCreatedEvent(event AccountCreatedEvent) {
  account.EmailAddress = event.EmailAddress
  account.FirstName = event.FirstName
  account.LastName = event.LastName
  account.PasswordHash = event.PasswordHash
}

The above code results in an account object being created with one single pending event namely AccountCreatedEvent. Events will then be persisted once saved to an event sourcing repository. If a repository is created with an event publisher then events saved for the purposes of event sourcing will also be published

persistance := cqrs.NewInMemoryEventStreamRepository()
bus := cqrs.NewInMemoryEventBus()
repository := cqrs.NewRepositoryWithPublisher(persistance, bus)
...
repository.Save(account)

Account Events

type AccountCreatedEvent struct {
  FirstName      string
  LastName       string
  EmailAddress   string
  PasswordHash   []byte
  InitialBalance float64
}

type EmailAddressChangedEvent struct {
  PreviousEmailAddress string
  NewEmailAddress      string
}

type PasswordChangedEvent struct {
  NewPasswordHash []byte
}

type AccountCreditedEvent struct {
  Amount float64
}

type AccountDebitedEvent struct {
  Amount float64
}

Events souring events are raised using the embedded Update function. These events will eventually be published to the read models indirectly via an event bus

func (account *Account) ChangePassword(newPassword string) error {
  if len(newPassword) < 1 {
    return errors.New("Invalid newPassword length")
  }

  hashedPassword, err := GetHashForPassword(newPassword)
  if err != nil {
    panic(err)
  }

  account.Update(PasswordChangedEvent{hashedPassword})

  return nil
}

func (account *Account) HandlePasswordChangedEvent(event PasswordChangedEvent) {
  account.PasswordHash = event.NewPasswordHash
}

Again the calling convention routes our PasswordChangedEvent to the corresponding HandlePasswordChangedEvent instance function

Read Model

Accounts projection

type ReadModelAccounts struct {
  Accounts map[string]*AccountReadModel
}

type AccountReadModel struct {
  ID           string
  FirstName    string
  LastName     string
  EmailAddress string
  Balance      float64
}

Users projection

type UsersModel struct {
  Users    map[string]*User
}

type User struct {
  ID           string
  FirstName    string
  LastName     string
  EmailAddress string
  PasswordHash []byte
}

Infrastructure

There are a number of key elements to the CQRS infrastructure.

  • Event sourcing repository (a repository for event sourcing based business objects)
  • Event publisher (publishes new events to an event bus)
  • Event handler (dispatches received events to call handlers)
  • Command publisher (publishes new commands to a command bus)
  • Command handler (dispatches received commands to call handlers)

Event sourcing and integration events

Nested packages within this repository show example implementations using Couchbase Server and RabbitMQ. The core library includes in-memory implementations for testing and quick prototyping

persistance := cqrs.NewInMemoryEventStreamRepository()
bus := cqrs.NewInMemoryEventBus()
repository := cqrs.NewRepositoryWithPublisher(persistance, bus)

With the infrastructure implementations instantiated a stock event dispatcher is provided to route received events to call handlers

readModel := NewReadModelAccounts()
usersModel := NewUsersModel()

eventDispatcher := cqrs.NewVersionedEventDispatchManager(bus)
eventDispatcher.RegisterEventHandler(AccountCreatedEvent{}, func(event cqrs.VersionedEvent) error {
  readModel.UpdateViewModel([]cqrs.VersionedEvent{event})
  usersModel.UpdateViewModel([]cqrs.VersionedEvent{event})
  return nil
})

We can also register a global handler to be called for all events. This becomes useful when logging system wide events and when our read models are smart enough to filter out irrelevant events

integrationEventsLog := cqrs.NewInMemoryEventStreamRepository()
eventDispatcher.RegisterGlobalHandler(func(event cqrs.VersionedEvent) error {
  integrationEventsLog.SaveIntegrationEvent(event)
  readModel.UpdateViewModel([]cqrs.VersionedEvent{event})
  usersModel.UpdateViewModel([]cqrs.VersionedEvent{event})
  return nil
})

Within your read models the idea is that you implement the updating of your pre-pared read model based upon the incoming event notifications

Commands

Commands are processed by command handlers similar to event handlers. We can make direct changes to our write model and indirect changes to our read models by correctly processing commands and then raising integration events upon command completion.

commandBus := cqrs.NewInMemoryCommandBus()
commandDispatcher := cqrs.NewCommandDispatchManager(commandBus)
RegisterCommandHandlers(commandDispatcher, repository)

Commands can be issued using a command bus. Typically a command is a simple struct. The application layer command struct is then wrapped within a cqrs.Command using the cqrs.CreateCommand helper function

changePasswordCommand := cqrs.CreateCommand(
  ChangePasswordCommand{accountID, "$ThisIsANOTHERPassword"})
commandBus.PublishCommands([]cqrs.Command{changePasswordCommand})

The corresponding command handler for the ChangePassword command plays the role of a DDD aggregate root; responsible for the consistency and lifetime of aggregates and entities within the system)

commandDispatcher.RegisterCommandHandler(ChangePasswordCommand{}, func(command cqrs.Command) error {
  changePasswordCommand := command.Body.(ChangePasswordCommand)
  // Load account from storage
  account, err := NewAccountFromHistory(changePasswordCommand.AccountID, repository)
  if err != nil {
    return err
  }

  account.ChangePassword(changePasswordCommand.NewPassword)

  // Persist new events
  repository.Save(account)  
  return nil
})

As the read models become consistant, within the tests, we check at the end of the test if everything is in sync

if account.EmailAddress != lastEmailAddress {
  t.Fatal("Expected emailaddress to be ", lastEmailAddress)
}

if account.Balance != readModel.Accounts[accountID].Balance {
  t.Fatal("Expected readmodel to be synced with write model")
}
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].