Value Objects in Rails

You can get a lot of value in rails by using value objects

You can get a lot of value in rails by using value objects to compose your ActiveRecord models and using concepts from value objects to limit said models.

I will strive to explain here:

  • What is a value object

  • How to make a pure value object and use it in rails

  • How to transform ActiveRecord objects into almost value objects

  • Why and when could you use value objects

What is a value object

I deliberately choose not to look up a definition (in part so you, dear reader, can tell me how wrong I am) and provide the definition I have in my head.

A value object is an object that does not need nor have an identity, that can be substituted by another instance of the same object with the same attributes and that is immutable (as changing it would make it by definition another object)

So if we imagine a Money class, that implements a #value and a #currency attributes. Any <Money value=5 currency=€> Can be substituted by another instance of Money that has the same value and currency (after all, when you get a 5€ not, you do not care about the serial number).

This can be opposed to an Account object, that has #balance and #currency methods, in this case, substituting an account for another might mean allowing someone to withdraw money that isn't theirs.

How to make 'pure' value objects and use them in Rails

At its simplest, a value object is just a class with an overridden == method (well and ===). You could just do:

class Money
    attr_reader :amount, currency

    def initialize(amount:, currency:)
        @amount = amount
        @currency = currency
    end

    def ==(other)
        amount == other.amount &&
            currency == other.currency
    end
end

This is a value object in and off itself, but how do you plug it into your ActiveRecord object?

Taking advantage of the composed_of macro:

class Accoun < ActiveRecord 
    [...]    
    composed_of :money, mapping: { balance: :amount, currency: :currency }
end

How to transform ActiveRecord objects into almost value objects

Sometimes, we need our ActiveRecord objects to behave with certain characteristics of value objects, in this case, I will show you how to make an active record (almost) immutable:

class CarModel
    [...]    

    def read_only?    
    !persisted?   
    end
end

You could go further and also overwrite the == method if you wanted.

Why and when could you use value objects

All of this is well and good, but I gather part of the reason you are here, cherished reader, is to know when and why you would use value objects.

When

As stated previously, one of the key reasons to use value objects is when you only care about the characteristics.

You might also find yourself, attentive reader, with 'prefixed attributes'. A classic example of this might be address

For example, a Person had several related attributes like: address_street, address_number, etc

You can group all of these in an address object and enrich them with methods such as #full_addressand store all of those in a jsonb address column.

Finally, it helps you break god objects into more cohesive and reusable units

Why

It is safer as having these objects being immutable means they won't change in the course of your code, which is one less variable to take into account.

You can unit test these objects with more ease than a typical ActiveRecord object.

Finally, it allows you to give them larger behavior than you could if you represented the values 'only' with a primitive.