Ruby’s ActiveRecord is a great ORM framework for Ruby and is very easy and simple to use. It is normally used as the Model of the MVC pattern in most of the Rails applications.
The convention over configuration approach is incredibly well implemented on ActiveRecord and it fits very well in cases you want to query the database and show its results on a web page, without having to write dozens of lines to configure it. It’s very good for testing a hypothesis really fast without having to code too much.
The problem of using the Ruby’s ActiveRecord on that way is when you have to deal with big applications that needs to have some layers and responsibilities well defined and not coupled, in this case, we’ll need to have a well distinguished layer between business objects and persistence rules (or even the serialization rules – e.g.: versioning of RESTful resources).
In those scenarios, if we use only one class (aka model), that represents your business object, data mapping and serialization (and view models, services, async process, and so on), we’ll end up with a big and fat model. Sometimes, even the instantiation of that class becomes too difficult.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
You can try to separate those responsibilities into modules, although you’ll still have a single class that will include all of them, in other words, you’ll still have a class with too many responsibilities (and maybe it becomes harder to test and to predict all its behaviors).
1 2 3 4 5 6 7 |
|
So, to avoid it, you can start the refactoring by separating the data persistence from the business object. A good pattern for that is the Repository Pattern.
A Repository mediates between the domain and data mapping layers, acting like
an in-memory domain object collection. Client objects construct query
specifications declaratively and submit them to Repository for satisfaction.
Objects can be added to and removed from the Repository, as they can from a
simple collection of objects, and the mapping code encapsulated by the
Repository will carry out the appropriate operations behind the scenes.
Conceptually, a Repository encapsulates the set of objects persisted in a
data store and the operations performed over them, providing a more
object-oriented view of the persistence layer. Repository also supports the
objective of achieving a clean separation and one-way dependency between the
domain and data mapping layers.
So, here is a simple approach of implenting the repository pattern:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Like I said, it is just a simple example and it can be better written, but the point here is to show you how the layers of persistence and business are well defined and decoupled. If you implement anything under the persistence layer it will not affect the implementation of your business object and you can keep using the ActiveRecord to query and to save into the database, but it won’t represent the abstraction of the business model in your application anymore. The entity, the new representation of the business model, has the responsibility of carrying some business rules and its relevant data now, becoming a simple PORO (Plain Old Ruby Object).
The biggest problem of this approach is that you may need to rewrite part of your code, since the majority of the current gems, that you might be using, were implemented to be used with an ActiveRecord class.
You can find this implementation at Ruby repository pattern example