Synapse is a CQRS and event sourcing framework for Ruby 1.9.3 and later.
Warning: Synapse is still under heavy development; public API can change at any time.
You know the drill, add it to your
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 = Account.new command.id, command.name @repository.add account end map_command RenameAccount do |command| account = @repository.load command.account_id account.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
- 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)
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)
- Event store using Sequel
- Distributed command and event buses (engine partitioning)
- Event replay and projection framework
- Event scheduling