home

Introduction to Mutations

written by on · Comments

About a year ago we found ourselves with an overly complicated codebase. The amount of business logic is constantly growing. We followed the ‘fat model’ approach, meaning most of this business logic is contained in our models. Actions on our models can have many obscure side effects caused by ActiveRecord callbacks.

The list of business requirements grows continuously, so the number of actions on models is growing at a constant rate too. It is hard to keep a good overview of all callbacks that are executed when performing an action, this makes the codebase error prone. We needed a way to structure our code to ensure its long-term maintainability.

The solution came in the form of a Ruby gem named Mutations. Mutations offers a way to organize our business logic into separate “commands”. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require "mutations"

module Contacts
  class CreateContact < Mutations::Command
    required do
      string :first_name
      string :last_name
    end

    optional do
      boolean :receive_newsletter
    end

    def execute
      instance = Contact.new(first_name: first_name, last_name: last_name)

      if instance.save and receive_newsletter
        NewsLetters::SendWelcome.run!(contact: instance.id)
      end

      instance
    end
  end
end

The example above allows us to create a contact using the following call:

1
Contacts::CreateContact.run!(first_name: "Money", last_name: "Bird")

As you can see in the first example, we’ve specified “required” and “optional” blocks. Mutations uses these blocks to automatically validate the input and to throw away anything that isn’t listed.

In doing so we’re effectively programming by contract. We’ve exposed a contract to which the caller must comply. If the input requirements are met the logic will be executed. If they aren’t the execution will halt, usually resulting in an error. In doing so errors will surface with a clear message just after their inception. Without Mutations this wouldn’t be the case.

Conclusion

Mutations helped restructure our codebase, it neatly organized our business logic into commands. It ridded our models of unwanted hooks and made our controllers a lot leaner.

comments powered by Disqus