Models
Facade Pattern with POROs
As projects grow in size the models tend to grow in complexity. Let’s look at a few strategies for managing the situation.
Background
The core issue is that ActiveRecord
classes mix two roles: persistence and business logic. This is convenient, especially when first starting an application, but it violates the "Single Responsibility Principle."
From Wikipedia:
In object-oriented programming, the single responsibility principle states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility. A class or module should have one, and only one, reason to change.
As a project matures there becomes a clearer division between these roles, and breaking them up into distinct domain objects is often a good idea.
Creating a Facade Object
A facade is concerned only with manipulating the data from other objects, it has no persistence itself. See the Facade Pattern on Wikipedia.
Writing a facade is very easy:
1 2 |
|
It’s just a "PORO" or "Plain Old Ruby Object".
Where Does It Live?
You can store your facade objects into app/models
. Some developers prefer more separation and store them in app/lib
. Any folder added under app/
will be added to the automatic load path when the server starts, so create folders whenever they make sense for the organization of your project.
Practical Techniques
A facade object will primarily use the same Ruby techniques you’re accustomed to, but here are a few methods that will make life easier:
attr_reader
attr_reader
, short for "Attribute Reader", creates an instance variable and accessor method for you:
1 2 3 4 5 6 7 8 9 10 11 |
|
You’re creating an attribute that, from outside the instance, can only be read. If you wanted to allow write access you’d use attr_accessor
, though that’s probably violating the encapsulation of the child objects.
If you are creating multiple attributes you can combine them in one call to attr_reader
like this:
1
|
|
delegate
& Law of Demeter
The delegate
method helps us deal with Law of Demeter violations.
The Law of Demeter says, generally speaking, that we can talk to an object but shouldn’t talk directly to the object’s children.
For instance, imagine we have a Plane
instance in @plane
. We want the engines started. The temptation is to write something like this:
1
|
|
But that assumes knowledge of how @plane
relates to its engines. What if there’s only a single engine? Will there still be an engines
method that returns a collection, or will there only be engine
? We’re breaking the encapsulation of the plane class.
Instead, proper object oriented design would be to tell the plane what to do:
1
|
|
That leaves it up to the @plane
to decide what it means to start the engines.
Demeter in your Facade
How does this relate to facade objects? When you create a facade, you’ll often want to act on attributes and methods of the child objects. Don’t do this:
1
|
|
Instead:
1
|
|
That’s why it’s called a facade, we present a unified face to the outside world, hiding the components underneath.
Using delegate
How do you make that work? Here’s the simplistic approach:
1 2 3 4 5 6 7 |
|
If you have multiple child objects with many methods, writing and maintaining these proxy methods will be a pain. Instead, use delegate
like this:
1 2 3 4 |
|
This has the exact same effect as the wrapper above. You can delegate many methods at once:
1 2 3 4 |
|
But sometimes the child method names don’t make sense when called on the parent. Or they could conflict with identically named methods in the parent. In those cases, you can use the :prefix
option:
1 2 3 4 |
|
Then method calls would look like my_obj_instance.child_second_method
as a proxy to my_obj_instance.child.second_method
. If you want to prefix with the name of the receiver, you can use :prefix => true
instead of repeating the receiver name.
Now you can preserve encapsulation but have easily maintained facade.
Trying it Out
Get the Blogger project from GitHub and run setup procedures:
1 2 3 4 5 |
|
All existing tests should pass. Optionally, run the tests continuously while developing by running guard
Start the server, and visit the root page. This is the DashboardController#index
which we’ll use to illustrate a facade.
Why Use a Facade
The dashboard is going to mix instances of Article
and Comment
. Check out the show
action in DashboardsController
:
1 2 3 4 5 6 7 8 9 10 |
|
Even though we’re using scopes and model methods, there’s a lot going on here. There are seven different instance variables that we have to juggle in the view template.
The intent of the dashboard is to display three things:
- Recent articles
- Recent comments
- Metrics about the total articles and comments
Three concepts should be three objects, maybe even one, but definitely not seven. Let’s create a domain concept which supports the third intent.
Starting the Facade
Create a file app/models/dashboard.rb
and add this:
1 2 3 |
|
Then let’s start picking off functionality from the existing implementation.
Total Word Counts
Add a method to the facade to count the words in all the articles:
1 2 3 |
|
Then do the same for comments.
Use the Facade
In the controller action, instead of this:
1 2 3 4 5 6 7 8 9 10 |
|
We can now do this:
1 2 3 4 5 6 7 8 9 10 |
|
No improvement yet. Condense those into one object creation instead:
1 2 3 4 5 6 7 8 |
|
Then modify the view template to use @dashboard.total_article_word_count
and @dashboard.total_comment_word_count
instead of the former instance variables.
And Your Point Is?
One surface win is that we have one fewer instance variable.
The real win is encapsulating the concept of word counts. Now our controller doesn’t care where the Dashboard
gets the counts from. Our current implementation is to calculate them on the fly, but there’s no reason it couldn’t fetch that from the database, Redis, or elsewhere. We can make that decision later when the performance really matters.
And by getting the responsibility out of the controller we can, more cleanly, test the functionality in our unit tests.
Continue the Process
Write wrapper methods in the dashboard.rb
and refactor your controller/view until your controller is just this:
1 2 3 4 5 |
|
Calculation in the Facade
After that refactoring, I still have this in the view template:
1 2 3 4 5 |
|
That’s logic in the view template, which is never good. Instead, take care of the calculation down in the facade so the view template can look like this:
1
|
|
By pushing that down into the model layer, it’s easier to refactor and test. Without the facade, where would the method have lived? It wouldn’t have a logical home, since it belongs to neither Article
nor Comment
. The Dashboard
works as a domain concept.
Pulling Up From the Children
The Article
class has this method:
1 2 3 |
|
What does that method have to do with Article
? Little. It is conceptually a part of the Dashboard
. Move it there, then do the same for Comment
.
Even though the page works, some specs are now breaking due to the refactoring. Create a dashboard_spec.rb
, move the problem specs over there, and rework them to match the new structures.
Going Further
Now that you have a facade encapsulating all the logic, some things you might try:
- Create a decorator for the facade and reduce complexity in your view template
- Cache the calculated data into a key-value store like Redis