29

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?

26

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.

  • 2
    can you elaborate on "other bad design decisions" ? – kevin cline Apr 21 '11 at 20:38
  • 2
    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. – kevin cline Apr 26 '11 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. – Wyatt Barnett May 24 '11 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. – kevin cline May 25 '11 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 Jul 7 '15 at 14:38
15

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).

  • 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 Sep 16 '11 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. – kevin cline Sep 16 '11 at 20:22
2

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

protected by gnat Aug 25 '16 at 12:32

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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