Synapse

Synapse is a CQRS and event sourcing framework for Ruby 1.9.3 and later.

Code Climate Coverage Status Build Status Gem Version

Synapse is partially an idiomatic port of AxonFramework and Lokad.CQRS

Warning: Synapse is still under heavy development; public API can change at any time.

Getting Started

You know the drill, add it to your Gemfile:

gem 'synapse-core'
gem 'synapse-mongo'

# Or if you're feeling edgy
gem 'synapse-core', :github => 'ianunruh/synapse', :branch => :master
gem 'synapse-mongo', :github => 'ianunruh/synapse-mongo', :branch => :master

You can define your commands and events using plain old Ruby objects.

class CreateAccount
  attr_reader :account_id, :name
  def initialize(id, name)
    @account_id = id
    @name = name
  end
end

class RenameAccount
  # ...
end

class AccountCreated
  # ...
end

class AccountRenamed
  # ...
end

Define the aggregate -- In this case, an event-sourced aggregate.

class Account
  include Synapse::EventSourcing::AggregateRoot

  def initialize(id, name)
    apply AccountCreated.new id, name
  end

  def rename(name)
    apply AccountRenamed.new id, name
  end

  map_event AccountCreated do |event|
    @id = event.id
    @name = event.name
  end

  map_event AccountRenamed do |event|
    @name = event.new_name
  end
end

Define the command handler

class AccountCommandHandler
  include Synapse::Command::MappingCommandHandler
  include Synapse::Configuration::Dependent

  depends_on :account_repository, :as => :repository

  map_command CreateAccount do |command|
     = Account.new command.id, command.name
    @repository.add 
  end

  map_command RenameAccount do |command|
     = @repository.load command.
    .rename command.new_name
  end
end

Setup the necessary services

Synapse.build_with_defaults do
  mongo_event_store do
    use_client Mongo::MongoClient.new
  end

  # The repository gets cool things injected, like locking, an event bus and event store
  es_repository :account_repository do
    use_aggregate_type Account
  end

  # Register your command handler so it can be subscribed to the command bus and get its own
  # dependencies injected upon creation
  factory :account_command_handler, :tag => :command_handler do
    inject_into AccountCommandHandler.new
  end
end

aaaaaand you're done!

class AccountController < ApplicationController
  # oooo shiny
  depends_on :gateway

  def create
    command = CreateAccount.new 123, 'Checking'
    @gateway.send command
  end
end

Features

  • Mixins for aggregate members (root and member entities)
  • Separation of events and commands
  • Event store (backed by MongoDB)
  • Snapshot support
  • Conflict detection support
  • Event upcasting
  • Command validation (using ActiveModel)
  • Simple object serialization
  • DSL for easy mapping of event and command handlers
  • Process manager framework (also known as Saga management)
  • Repository for non-event sourced aggregates (MongoMapper and ActiveRecord)

Compatibility

Synapse is tested and developed on several different runtimes, including:

  • MRI 1.9.3
  • MRI 2.0.0
  • JRuby 1.7.3
  • Rubinius 2.0.0-rc1 (rbx-head)

Coming soon

  • Event store using Sequel
  • Distributed command and event buses (engine partitioning)
  • Event replay and projection framework
  • Event scheduling