Immutable ActiveRecord Models

I've just implemented something that I think is pretty neat in DeskBeers - an immutable ActiveRecord model.

Why?

Why would you want an immutable ActiveRecord model? In our case, the model in question was Subscription. Subscription, in the DeskBeers sense, holds on to the products a customer wants, and the frequency with which a customer wants them. A customer can update the products in their Subscription and the frequency with which they receive them any time, for example this week you might not want your usual order of wine, or you might have a bit of a backlog so might choose to dial deliveries down to being fortnightly for a while. That's totally cool, we got you.

From the information we hold in a customer's Subscription, we then create Order records at the appropriate time. Because a Subscription can change but an Order (once processed, at least) shouldn't, we have to take a copy of the products requested by the Subscription and duplicate that onto the Order. This way, when we look back at past orders, they retain the information of what was sent to the customer and when, regardless of changes in the parent Subscription.

By making Subscription immutable, we get rid of the requirement for the order to know what the customer requested. When the customer makes a change to their Subscription, we create a new Subscription for them, based off the old Subscription, with the required changes. The Order records that pertain to the old Subscription still hang onto the old Subscription, and new ones take their information from the new Subscription.

This allows us to massively simplify Order and make it way more flexible - anything can be added to an Order (anything skuable, that is) and we can add new LineItems to Order way more easily. I'm going off-track a bit here and, not wanting to get into the weeds, let's just say this is a huge win for the flexibility of the DeskBeers platform, honest.

A nice side effect of this change is that, if a customer so wanted to, they can roll back a change made to Subscription as we now maintain a history of the model, which is cool. Or they could treat these different versions as templates, e.g. have one with wine and one without, and alternate between these presets as they saw fit. If they wanted to. Which I'm not sure is a requirement anyone has actually asked for. But it's basically a free feature, perhaps it'll come in useful.

How?

Largely thanks to this post on the Strikingly blog, I created a module like this:

module ActiveRecord
  module Immutable
    class UpdateImmutableException < Exception
      def initialize
        super("Immutable model can't be updated")
      end
    end

    extend ActiveSupport::Concern

    included do
      before_update :prevent_update
    end

    private

    def prevent_update
      raise UpdateImmutableException.new
    end
  end
end        

and included it into Subscription like this:

class Subscription < ApplicationRecord
  include ActiveRecord::Immutable
  # ... code from what is definitely a God model omitted
end

This caused hundreds of test failures. Working through them has proved to be super-interesting, and I've learned a lot about our codebase in fixing them. Making Subscription immutable really highlighted where Subscription and Order were tightly coupled, and fixing the tests has lead me to what I believe will be a way more flexible and ultimately easier to understand codebase.

Do you have any models in your Rails app that could potentially benefit from being immutable?

推荐文章

Lucene中TokenStream,Tokenizer,TokenFilter,TokenStreamComponents与Analyzer

Lucene中TokenStream,Tokenizer,TokenFilter,TokenStreamComponents与Analyzer

推荐文章

网站开发人员应该知道的62件事

网站开发人员应该知道的62件事

推荐文章

Lingpipe中的spell模块-拼写纠错

Lingpipe中的spell模块-拼写纠错

推荐文章

lucene简单应用--多字段内容检索

lucene简单应用--多字段内容检索

推荐文章

尝试java开发搜索引擎爬虫

尝试java开发搜索引擎爬虫

推荐文章

hello1 hello2 服务器安装加部署全过程

hello1 hello2 服务器安装加部署全过程

推荐文章

摘自河畔的零碎资料——可以日后参考

摘自河畔的零碎资料——可以日后参考

推荐文章

Lucene 3.6.2入门 :HelloWord

Lucene 3.6.2入门 :HelloWord

推荐文章

作为软件平台的浏览器与文件管理器

作为软件平台的浏览器与文件管理器

推荐文章

Lucene 3.6.2入门:针对索引文件的CRUD

Lucene 3.6.2入门:针对索引文件的CRUD

推荐文章

Lucene 3.6.2入门简述Lucene中常见的搜索功能

Lucene 3.6.2入门简述Lucene中常见的搜索功能

推荐文章

浅析网站优化以及用户体验

浅析网站优化以及用户体验

推荐文章

Lucene 3.6.2入门:自定义停用词分词器和同义词分词器

Lucene 3.6.2入门:自定义停用词分词器和同义词分词器

推荐文章

一种让谷歌搜索引擎拒绝搜索的字符串

一种让谷歌搜索引擎拒绝搜索的字符串

推荐文章

Lucene入门教程(一)

Lucene入门教程(一)

推荐文章

定制自己的火狐搜索插件 searchplugins

定制自己的火狐搜索插件 searchplugins