Active Record pattern (or anti-pattern) – overview

Active Record is one of the most controversial architectural patterns with many supporters and opponents. Some people even call it the anti-pattern. Despite this, it is very common in many ORMs (object-relational mappers) in various languages.

Basics

In the Active Record pattern, every record in the database is represented by an in-memory object called a model. Models provide a set of methods for CRUD operations (create, read, update, delete) and access to related objects. Let’s take a look at a simplified diagram of a model.

Below is a short excerpt from a model class from the Laravel framework. That class originally has over 1500 lines of code and over 100 methods so I pasted only the declarations of the most important ones. The actual Model class is available here.

namespace Illuminate\Database\Eloquent;

abstract class Model
{
    public function fill(array $attributes) {}
    public function forceFill(array $attributes) {}
    public function update(array $attributes = [], array $options = []) {}
    public function push() {}
    public function saveQuietly(array $options = []) {}
    public function save(array $options = []) {}
    public function saveOrFail(array $options = []) {}
    public function delete() {}
    public function forceDelete() {}
    public function toArray() {}
    public function toJson($options = 0) {}
    public function jsonSerialize() {}
    public function replicate(array $except = null) {}
    public function is($model) {}
    public function isNot($model) {}
}
Examples of Active Record implementation:
  • Eloquent (part of Laravel framework) – PHP
  • RoR ActiveRecord (part of Ruby on Rails framework) – Ruby
  • Django ORM (part of Django framework) – Python

Drawbacks of the pattern

Active Record pattern has many disadvantages. I consider these 3 to be the most important:

  • against the rules and best practices of OOP (Object Oriented Programming) – models represent table rows instead of domain objects. This results in breaking rules of OOP e.g. encapsulation (fields of the model are accessible from the outside of the object). Model objects tend to be quite large objects and have a lot of responsibilities and many reasons to change. It’s a violation of SRP (Single Responsibility Principle).
  • mixing layers – models have responsibilities of at least 2 application layers. They have the ability to persist themselves (persistence layer) and contain business logic (domain layer).
  • difficult for unit testing – high coupling between the domain layer and persistence layer makes writing decent unit tests almost impossible. You are not able to mock the persistence layer (e.g. using doubles) and test only your business logic. Lack of dependency injection causes additional issues like no possibility of injecting mocks or stubs instead of production services. The only way to thoroughly test a model object is integration testing.

When to use

In the paragraph above, I explained why using Active Record is usually a bad idea. However, there are some cases where it can be really useful. Active Record based ORMs, due to their specificity, allows us to bootstrap application very quickly. For this reason, they are a great solution for projects that have to be cheap, delivered fast, and are not critical. Examples of such projects are prototypes, POCs (Proof Of Concept), and short-life applications.

You May Also Like

Leave a Reply

Your email address will not be published. Required fields are marked *