A Ruby implementation of Command-Query Responsibility Segregation (CQRS) with Event Sourcing, based upon the ideas of Greg Young.
Dependencies are managed using Bundler.
$ sudo gem install bundler
Install all of the required gems for this application
$ sudo bundle install
Run the RSpec specifications as follows.
$ rspec spec/
###UI
- display query results using read-only reporting datastore
- create commands - must be 'task focused'
- basic validation of command (e.g. required fields, uniqueness using queries against the reporting data store)
###Commands
such as RegisterCompanyCommand
- capture users’ intent
- named in the imperative (e.g. create account, upgrade customer, complete checkout)
- can fail or be declined
###Command Bus
- validates command
- routes command to registered handler (there can be only one handler per command)
###Command Handler
such as RegisterCompanyHandler
- loads corresponding aggregate root (using domain repository)
- executes action on aggregate root
###Aggregate Roots
such as Company
- guard clause (raises exceptions when invalid commands applied)
- calculations (but no state changes)
- create & raise corresponding domain events
- subscribes to domain events to update internal state
###Domain Events
such as CompanyRegisteredEvent
- inform something that has already happened
- must be in the past tense and cannot fail
- used to update internal state of the corresponding aggregate root
###Event Bus
- subscribes to domain events
- persist domain events to event store
- routes events to registered handler(s) (can have more than one handler per event)
###Event Handler
such as CompanyRegisteredHandler
- update de-normalised reporting data store(s)
- email sending
- execute long running processes (e.g. 3rd party APIs, file upload)
Right now if You want to use Resque for handling events You need to inherit Your EventHandlers by Events::Handlers::AsyncHandler for example:
class UserCreatedhandler < Events::Handlers::AsyncHandler def execute(event) # ... end end ###Event Store
- persists all domain events applied to each aggregate root (stored as JSON)
- reconstitutes aggregate roots from events (from serialised JSON)
- currently two adapters: ActiveRecord and in memory (for testing)
- adapter interface is 2 methods:
find(guid)
andsave(aggregate_root)
- could be extended to use a NoSQL store
There is possibility to configure rcqrs-rails. Right now there is just 2 options: orm and file path for example:
Rcqrs::Setting.set do |setting|
setting.default_orm = :in_memory
setting.default_database_file_path = "config/database_event_store.yml"
end
Rcqrs::Gateway.instance
You can put it for example in config/initializer/rcqrs.rb
If You will use dm-rails You need to put all config into config/database.yml to make sure that all rakes will work.
You must remmber that You CAN NOT give the same name for event handler as for command handler, first because it's not properly, second app will crash :)