View profile

VIII: The Presenter Anti-Pattern in Ruby

Jared Norman
Jared Norman
The “presenter” pattern is a relatively common pattern to see used in Rails apps. I’ll be up front that it’s not really an anti-pattern. I only said that so you’d click. It’s a totally fine pattern.
For many use cases however, there’s a better pattern.
The Presenter Pattern
The presenter pattern seeks to provide an abstraction in between your data layer (often ActiveRecord) and your views that allows you to hide certain implementation details from the view.
Googling “presenter pattern rails” produces various resources on the subject. This article, for example, show how you can create a PostPresenter which takes a post (an ActiveRecord object) to its constructor. The presenter exposes methods that provide the view with data it needs, while hiding the logic for how the data is computed.
This pattern isn’t just useful for HTML views. I’ve seen it used when generated PDFs, CSV reports, JSON that’s fed to external systems, and more. It’s a good pattern for when you need to create new representations of data in your system. It allows you to avoid putting any formatting logic into your models without simply forcing that complexity into your view layer.
Better Presenters
One of the downsides of these objects is that they are coupled to the data source. The PostPresenter in the article I mentioned cannot be used independently of a post record, or at least something that quacks like one.
Additionally, in order to write isolated tests for your views you’ll need to either use mocks of your presenter class or create post records to construct real ones. Now I don’t actually normally write view specs, but as we see later, it might not strictly be a view that’s consuming these objects.
My preference, rather than wrapping the models in presenter objects, is to create structs with factory methods. Here’s an example of how you might turn the PostPresenter into something that follow the struct with factory methods pattern.
I’ve changed a few things from the original. Now, rather than constructing an object that maintains a reference to the original post, we use a struct that only contains the data the view cares about. We compute this struct by using a factory method (PostPresenter.from_post) that encodes all the logic for formatting our post’s data for the frontend. If we wanted to test our view in isolation, we’d simply have to construct our own PostPresenter, without ever having touched a database record.
This design affords the ability to construct these objects from different sources. If you needed to also display posts fetched over the network, or from a different model, you’re in luck. You can just add an additional factory method and everything else in your system stays the same.
Not Just For Views
While this design works well for views, it doesn’t end there. This pattern works whenever you want to use data from one or more sources and pass it to any object that needs a different, perhaps domain-specific representation of it.
I work in digital commerce, so let’s use orders as an example. It’s frequent that we need to build connections to send order data to external systems like analytics services, accounting software, or fulfillment services.
Each and every such service has a slightly different representation of an order. Some understand split shipments, some do not. Some see shipping as separate from the items in the order, some as just another line item.
Inevitably, we need code somewhere in our system that reconciles these differences and translates our order representation to theirs. This pattern works wonderfully here.
I write a factory method responsible for taking our order representation and constructing one or more structs that contain the order data in a structure that matches what the external system needs. I then pass this struct to the code that needs to do something with that order data.
I usually end up with a few very focused objects when constructing integrations like this. The struct or structs are just data representations, containing no logic themselves. They typically get passed to an object responsible for transforming them into XML, JSON or whatever the other system needs. Finally, I have an object responsible for sending that data to the third party.
The benefit of this kind of architecture is that you can test everything in isolation. You can construct orders and test that your factory methods produce the correct representations. You only need structs to test that a given order struct results in the correct XML, resulting in very fast tests that don’t touch the database. In most cases, the code responsible for transmitting the data to the external system doesn’t care about the payload itself, so the connection can be tested independently of everything above.
Conclusion
Using factory methods to create domain-specific structs is a great way to make your system easier to understand and test.
It creates a single place where your system transforms data from one domain to another, so that you can test that transformation independent of what the system then does with the data. Because it involves objects that are simple to create, it also results in simpler tests that are less dependent on mocks or the database.
You still end up with all the same capabilities in your system, without significantly more or less code. It’s just easier to test because the responsibilities are more clearly separated.
Bonus tip: you can use Factory Bot to instantiate your structs.
Side Note: I may republish this as a blog post. I didn’t realize I couldn’t do code blocks in Revue when I started writing this.
Did you enjoy this issue? Yes No
Jared Norman
Jared Norman @jardonamron

computers bend to my will • founder of super good software • solidus core team • Rubyist • time abolitionist • not actually grumpy • he/him

In order to unsubscribe, click here.
If you were forwarded this newsletter and you like it, you can subscribe here.
Created with Revue by Twitter.