# Ruby Event Store - use without Rails

[Ruby Event Store v0.27](https://github.com/RailsEventStore/rails_event_store/releases/tag/v0.27) is here with some nice improvements. Let's have a quick look.

<!-- more -->

## Using RES without Rails

We've always built our [ecosystem of gems](https://github.com/RailsEventStore/rails_event_store/) with the intention of not being coupled to Rails. So the majority of features are implemented in `ruby_event_store` gem and a few other features such as async handlers integrated with ActiveJob are in `rails_event_store`. Every dependency is taken in a constructor and can be swapped to something different.

Until now we had one coupling which prevented you from using `ruby_event_store` easily. The `rails_event_store_active_record` gem (which provides an implementation for a repository to save events) depended on `rails` because it provided a migration generator to create necessary tables for storing events.

`rails_event_store_active_record` now integrates with `rails` optionally and can work without it. So you can use `ruby_event_store` together with `rails_event_store_active_record` without `rails`. Here is how:


Add `ruby_event_store` too your `Gemfile`:

```ruby
source 'https://rubygems.org'

gem 'activerecord'
gem 'ruby_event_store'
gem 'rails_event_store_active_record'

# And one of:
gem 'sqlite3'
gem 'pg'
gem 'mysql2'
```

As you are not using rails and its generators, please create required database tables which are equivalent to [what our migration would do](https://github.com/RailsEventStore/rails_event_store/blob/master/rails_event_store_active_record/lib/rails_event_store_active_record/generators/templates/migration_template.rb) in whatever way you manage DB schema in your project.

You can now use RES in your app.

```ruby
require 'active_record'
require 'rails_event_store_active_record'
require 'ruby_event_store'

ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.establish_connection(ENV['DATABASE_URL'])

class OrderPlaced < RubyEventStore::Event
end

event_store = RubyEventStore::Client.new(
  repository: RailsEventStoreActiveRecord::EventRepository.new
)

event_store.publish_event(OrderPlaced.new(data: {
    order_id: 1,
    customer_id: 47271,
    amount: BigDecimal.new("20.00"),
  }),
  stream_name: "Order-1",
)
```

## Protobuf & Custom event mappers

Since the beginning events had to inherit from `RubyEventStore::Event`. That is no longer the case, however. Now, any object can be used as events, provided you tell us how to map it to columns that we store in the database. To do that you can implement a custom mapper.

This new `ruby_event_store` version comes with `RubyEventStore::Mappers::Protobuf` mapper which you can use to store messages generated with `google-protobuf` gem.

Add RES and protobuf to your app's Gemfile:

```ruby
gem 'google-protobuf'
gem 'rails_event_store' # or `ruby_event_store` 
```

Configure protobuf mapper:

```ruby
Rails.application.configure do
  config.to_prepare do
    Rails.configuration.event_store = RailsEventStore::Client.new(
      repository: RailsEventStoreActiveRecord::EventRepository.new(
        mapper: RubyEventStore::Mappers::Protobuf.new
      )
    )
  end
end
```

Define your events in protobuf file format i.e.: events.proto3

```
syntax = "proto3";
package my_app;

message OrderPlaced {
  string event_id = 1;
  string order_id = 2;
  int32 customer_id = 3;
}
```

and generate the Ruby classes:

```ruby
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: events.proto3

require 'google/protobuf'

Google::Protobuf::DescriptorPool.generated_pool.build do
  add_message "my_app.OrderPlaced" do
    optional :event_id, :string, 1
    optional :order_id, :string, 2
    optional :customer_id, :int32, 3
  end
end

module MyApp
  OrderPlaced = Google::Protobuf::DescriptorPool.
                  generated_pool.
                  lookup("my_app.OrderPlaced").
                  msgclass
end
```

You can now use those structures when publishing events with Rails/Ruby Event Store 

```ruby
event_store = Rails.configuration.event_store

event = MyApp::OrderPlaced.new(
  event_id: "f90b8848-e478-47fe-9b4a-9f2a1d53622b",
  customer_id: 123,
  order_id: "K3THNX9",
)
event_store.publish_event(event, stream_name: "Order-K3THNX9")
```

The work is not yet finished. We are still working on [enabling this feature for async handlers](https://github.com/RailsEventStore/rails_event_store/issues/228) but we think this a good start.

We believe this will make much easier to use RES and exchange events between multiple applications and/or micro-services.

You can now **serialize your events however you want: protbuf, messagepack, Apache Avro, BSON, Thrift, Cap’n Proto.** It's up to you. You just need to implement a custom mapper with 3 methods.

Here is an example of how you could serialize your events with messagepack. 

Implement the mapper:

```ruby
require 'msgpack'

class MyHashToMessagePackMapper
  def event_to_serialized_record(domain_event)
    # Use data (and metadata if applicable) fields
    # to store serialized representation
    # of your domain event 
    SerializedRecord.new(
      event_id:   domain_event.fetch('event_id'),
      metadata:   "",
      data:       domain_event.to_msg_pack,
      event_type: domain_event.fetch('event_type')
    )
  end

  # Deserialize proper object based on
  # event_type and data+metadata fields
  def serialized_record_to_event(record)
    MessagePack.unpack(record.data)
  end

  def add_metadata(event, key, value)
    event[key.to_s] = value
  end
end
```

Pass it as a dependency:

```ruby
Rails.application.configure do
  config.to_prepare do
    Rails.configuration.event_store = RailsEventStore::Client.new(
      repository: RailsEventStoreActiveRecord::EventRepository.new(
        mapper: MyHashToMessagePackMapper.new
      )
    )
  end
end
```

and now you can publish an event:

```ruby
Rails.configuration.event_store.publish_event({
  'event_id'     => SecureRandom.uuid,
  'order_id'     => 'K3THNX9',
  'event_type'   => 'OrderPlaced',
  'order_amount' => BigDecimal.new('120.55'),
}, stream_name: 'Order$K3THNX9')
```

## Documentation

* http://railseventstore.org/docs/protobuf/
* http://railseventstore.org/docs/mapping_serialization/
* http://railseventstore.org/docs/without_rails/
* https://github.com/RailsEventStore/rails_event_store/releases/tag/v0.27.0

## Read more

If you enjoyed that story, [subscribe to our newsletter](http://arkency.com/newsletter). We share our everyday struggles and solutions for building maintainable Rails apps which don't surprise you.

Also worth reading:

* [Rails Event Store - better APIs coming](/rails-event-store-better-apis-coming/) - How we delivered nice APIs in Ruby/Rails EventStore in version 0.26 recently.
* [When DDD clicked for me](/when-ddd-clicked-for-me/) - It took me quite a time to grasp the concepts from DDD community and apply them in our Rails projects. This is a story of one of such “aha” moments.
* [Application Services - 10 common doubts answered](/application-service-ruby-rails-ddd/) - You might have heard about the Domain-Driven Design approach to building applications. In this approach, there is this horizontal layer called Application Service. But what does it do?
