Why we follow the Rails repo structure in Rails Event Store

A complete Rails Event Store solution consists of following gems:

  • ruby_event_store — core concepts and and mechanics of an event store
  • rails_event_store — thin wrapper over ruby_event_store with additions possible in Rails framework (like automatically capturing some of the request params into event metadata) and necessary glue to make it work out of the box
  • rails_event_store_active_record — a database adapter based on ActiveRecord
  • aggregate_root — library useful for making event-sourced aggregates

The problem

Until recently, each of the gems lived in a repository of it’s own. That’s how you usually develop a gem and what majority of tools assume. Bundler for example in its release-helping Rake tasks provides a code for tagging git repo with gem version number for a RubyGems release.

Each gem in it own’s repository provides a separation and some gems like aggregate_root or ruby_event_store can be used completely on their own.

This split however has several drawbacks if you’re already a contributor or wishing to become one:

  • the code is harder to navigate if you have to jump between repositories, that makes a difference for rails_event_store which depends on the rest
  • if you introduce a change in one of the gems you have to be sure how it affects the rest, integration tests that span components are easier to write and maintain in a monorepo
  • bigger changes that affect several components are harder to coordinate and become split into smaller commits in each repo
  • separate repositories also mean several places where issues are reported and pull-requests submitted — not necessarily the right ones and that multiple sources make code reviews and discussions painful
  • it might be confusing for newcomers to figure out what is the right place to start the journey with OSS contribution in Rails Event Store

In short it was easier to start that way but it’s painful in the long run.

What Rails does

If you look at Rails repository you immediately notice the code layout:

It is also worth noting that each of the components gets the same version number as Rails release. It might be tempting to keep component versioning separate but it simpler from end-user perspective to refer to only one number (i.e. when reporting issues).

How we approached git migration

Being sold to the idea of monorepo we had to figure out “The How”. For sure we wanted to keep most popular repo alive (and base for others to be merged in).

We could think of 3 possible approaches:

git subtree merge

  • original commit SHA retained (refering to SHA from commit messages and refering to existing tags should work)
  • on a graph it is several roots going into one HEAD
  • breaks file history as the paths after merge are different and it shows only the merge commit

git filter-branch and pull –allow-unrelated-histories

  • commit SHA changed as this is modifying history 😱
  • on a graph it is several roots going into one HEAD as well
  • file history works as paths are rewritten in commits

git mv and pull –allow-unrelated-histories

  • original commit SHA retained (refering to SHA from commit messages and refering to existing tags should work)
  • on a graph it is several roots going into one HEAD (surprise)
  • file history works (with --follow flag that tracks renames) but not necessarily on GitHub UI

For us, from contributor perspective, working file history is more important than preserving original commit identifiers. So we chose approach involving git filter-branch.

The migration involved running following snippet on each repo:

SUB_DIR=ruby_event_store

git filter-branch --index-filter \
  'git ls-files -s | gsed "s-\t\"*-&'"$SUB_DIR"'/-" |
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
  git update-index --index-info &&
  mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD

Finally each of rewritten repos where merged into destination one with following git pull modifier:

git pull --allow-unrelated-histories ../ruby_event_store

Sidenote: gsed stands for GNU Sed. BSD Sed available on MacOS caused me some trouble.

What changed for end users

All these changes were mostly for contributors and maintainers convenience. If you’re a happy Rails Event Store user you might be wondering if that change should be on your radar.

  • we still publish separate gems to RubyGems like we did
  • you can still refer to git sources of a gem in a monorepo (via Gemfile and git: source)
  • you can submit issues on RailsEventStore repo as before, as a plus there are no other repos which could confuse you
  • there was a bump in version number so that all components can have the same but generally there’s no breaking change here either (as a bonus it easier to reason about versions of involved components and there’s a single changelog catching all changes you’d be interested in when upgrading)

Last but not least Rails Event Store got new website. I encourage you to check it out and consider using RES to support your business.