45

I'm curious what are the drawbacks to using the ActiveRecord pattern for data access/business objects. The only one I can think of off the top of my head is that it violates the Single Responsibility Principle, but the AR pattern is common enough that this reason alone doesn't seem "good enough" to justify not using it (of course my view may be skewed since often none of the code I work with follows any of the SOLID principles).

Personally I am not a fan of ActiveRecord (with the exception of writing a Ruby on Rails application, where AR feels "natural") because it feels like the class is doing too much, and data access shouldn't be up to the class itself to handle. I prefer to use Repositories that return business objects. Most of the code I work with tends to use a variation of ActiveRecord, in the form of (I do not know why the method is a boolean):

public class Foo
{
    // properties...

    public Foo(int fooID)
    {
        this.fooID = fooID;
    }

    public bool Load()
    {
        // DB stuff here...
        // map DataReader to properties...

        bool returnCode = false;
        if (dr.HasRows)
            returnCode = true;

        return returnCode;
    }
}

or sometimes the more "traditional" way of having a public static Foo FindFooByID(int fooID) method for the finders and something along the lines of public void Save() for saving/updating.

I get that ActiveRecord is typically much simpler to implement and use, but it seems a little too simple for complex applications and you could have a more robust architecture by encapsulating your data access logic in a Repository (not to mention having it easier to swap out data access strategies e.g. maybe you use Stored Procs + DataSets and want to switch to LINQ or something)

So what are other drawbacks to this pattern that should be considered when deciding if ActiveRecord is the best candidate for the job?

3 Answers 3

37

The main drawback is your "entities" are aware of their own persistence which leads to a lot of other bad design decisions.

The other issues is that most active record toolkits basically map 1 to 1 to table fields with zero layers of indirection. This works on small scales but falls apart when you have trickier problems to solve.


Well, having your objects know about their persistence means you need to do things like:

  • easily have database connections available everywhere. This typically leads to nasty hardcoding or some sort of static connection that gets hit from everywhere.
  • your objects tend to look more like SQL than objects.
  • hard to do anything in the app disconnected because database is so ingrained.

There ends up being a whole slew of other bad decisions on top of this.

7
  • 3
    can you elaborate on "other bad design decisions" ? Commented Apr 21, 2011 at 20:38
  • 3
    Thanks. I have not found these issues to be a problem in Ruby on Rails development. It is still possible to test behavior and persistence separately. IMO separating persistence from behavior has little practical value. Commented Apr 26, 2011 at 13:18
  • @kevin: these things are less of a drawback with ruby features like mixins and duck typing. With static languages -- like C# which is what the OP used in his question -- it is a bit more difficult to separate the two. Commented May 24, 2011 at 13:18
  • @Wayne: to me, classes are just boxes to put methods in -- I can separate business logic from persistence by putting them in separate classes, or I can just separate them conceptually by making sure that the business methods don't do I/O. In languages with poor delegation support (e.g. Java), this saves a huge amount of code. OTOH, I'm just getting into Hibernate so I could be completely wrong. Commented May 25, 2011 at 6:02
  • 4
    I would add two things; 1. The coupling with the persistence mechanism makes the code difficult, if not impossible, to properly unit test. 2. Active Record glues your code to the relationships in a centralized persistence mechanism, making it really difficult to split your monolith if you ever decide to.
    – istepaniuk
    Commented Jul 7, 2015 at 14:38
17

The biggest drawback to active record is that your domain usually becomes tightly coupled to a particular persistence mechanism. Should that mechanism require a global change, perhaps from file-based to DB-based persistence, or between data access frameworks, EVERY class that implements this pattern may change. Depending on the language, framework, and design, even something so simple as changing where the DB is located or who "owns" it might require going through every object to update the data access methods (this is uncommon in most languages that provide easy access to config files with connection strings).

It also generally requires you to repeat yourself. Most persistence mechanisms have a lot of common code, to connect to a DB and start a transaction. DRY (Don't Repeat Yourself) would tell you as a coder to centralize such logic.

It also makes atomic operations tricky. If a group of objects must be saved in an all-or-nothing fashion (such as an Invoice and its InvoiceLines and/or Customer and/or GL entries), either one object must know about all these other objects and control their persistence (which stretches the scope of the controlling object; large interconnected records can easily become "god objects" that know everything about their dependencies), or the control over the entire transaction must be handled from outside the domain (and in that case why are you using AR?)

It's also "wrong" from an Object-Oriented perspective. In the real world, an invoice doesn't know how to file itself, so why would an Invoice code object know how to save itself to the DB? Of course, overly religious adherence to "objects should only model what their real-world counterparts can do" would lead to anemic domain models (an invoice also doesn't know how to calculate its own total, but splitting the calculation of that total to another object is generally considered a bad idea).

2
  • In Ruby, where persistence may be defined or abstracted behind a very simple keyword or command, it's probably less of a problem. In .NET, which requires more LoC to set up various persistence mechanisms, it is usually redundant to have a SQL connection for each object, especially if multiple objects must be saved in one atomic transaction. Connecting to the DB looks the same whether you're saving an Invoice or a Customer. You would either abstract the common stuff into a base class common to all ActiveRecord objects in your codebase, or extract it to its own class (a Repository)
    – KeithS
    Commented Sep 16, 2011 at 20:14
  • can you provide examples of Ruby ActiveRecord usage that illustrate your points? I did not experience these problems, but my application was fairly small. I could write migrations in Ruby, and deploy them to differing DB implementations. I found that Ruby's ActiveRecord was very useful in eliminating repetition, so I don't see why you would assert that using ActiveRecord would have the opposite effect. I did not have problems with saving: I just modified the object model, and let AR update the DB to reflect the object model. Commented Sep 16, 2011 at 20:22
3

The basic drawback is,It makes your domain model complex because It not only holds business logic but also persistence information.

So the solution is to make use of Data Mapper implementation of ORM. That separates out the persistence layer and we are now more centric on entity business logic. Doctrine is Data Mapper ORM.

But this approach also has some complexity, For query now you are too much depend upon Data Mapper, makes query oriented environment. To simplify it another layer is introduced between Domain Model and Data Mapper is called Repository.

Repository abstract out the persistence layer. It makes feel of object oriented programming in the sense, it is collection of all same type objects (as all entity stored in database table) and you can perform operation on them as collection operation, add, remove. contains etc.

For example for User Entity , there will be UserRepository that represent collection of same type of user objects(that stored in users table) that you can perform operation over. In order to query to users table it makes use of User Data Mapper but it abstracted out to domain model User.

Repository pattern is type of Data Access Layer, another is Data Access Object only differences Repository has Aggregate Root feature

1
  • Repository and DAO patterns differ, DAO is general data access, Repository is for persistence of collection of all same type objects. I.e. all Repositories should have same interface (like array or list). Other king of methods does not belong to Repository. DAO is lower level, Repository may use DAO. Often, programmers (especially PHP) use Repository as DAO, but it's not correct.
    – xmedeko
    Commented Nov 18, 2019 at 8:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.