In the model-view-controller pattern that Rails is built on, it’s pretty clear what views and controllers are, but models are a little less clear. In my years with Rails I’ve had the opportunity to come across a number of different conceptions regarding what models are. These conceptions often overlap but often don’t share the same exact contours.
Our conception of what a model is is super important because it has a huge influence on how we structure our Rails applications, which in turn determines how easy and economical it’s going to be to maintain our applications.
Here’s how I think about what Rails models are.
How I conceive of Rails models
First of all, forget about Rails
I think one of the most helpful mindset shifts a Rails developer can make with regard to models is to think about models outside the concept of Rails, at least for a moment. Instead, let’s consider: What is a model in general? Why do we use models?
And in fact, not to get too crazy, but we can even think about models even outside the context of programming. Scientists and mathematicians use models as well. Why do people in these professions use models? If you asked a scientist what a model is, what would they say?
Non-programming models
According to Wikipedia, “Scientific modeling is a scientific activity, the aim of which is to make a particular part or feature of the world easier to understand, define, quantify, visualize, or simulate by referencing it to existing and usually commonly accepted knowledge.”
Let’s see if we can draw inspiration from this to describe models in programming.
My definition of a model
I might adapt the above definition of a scientific model to programming as follows: “Modeling is a programming activity, the aim of which is to make a particular part of a program easier to understand and work with by representing parts of reality in a simplified way that can be expressed in code.”
A model is not a simulation
It’s common for object-oriented programming tutorials to use examples like Dog
and Cat
each inheriting from Animal
, and with methods like bark
and meow
. I find these kinds of examples misleading. These entities aren’t models, they’re simulations, and we programmers almost never build simulations.
Instead we create models with names more like User
, PatientPayment
or HashWithIndifferentAccess
. These models are useful precisely because they break off a tiny part of the infinite hugeness of reality and provide us with a simplified abstraction we can work with, rather than having to deal with all of reality at once.
In programming we would never try to model an entire dog or cat. Rather, we try to model the tiny slices of reality related to dogs and cats that we care about. For example, for a pet store application, we might keep track of a dog’s name, the dog’s owner’s contact information, and other things like that, but never e.g. its skeletal structure or biological taxonomy. And we certainly wouldn’t create a method that invokes a bark (whatever that could even mean).
A model doesn’t need to have anything to do with Active Record
I used to think that Rails applications were supposed to have exactly one model per database table and that every model file was supposed to inherit from ActiveRecord::Base
.
Now, with my broader conception of models, not only do I not think models have to inherit from Active Record, but most of my models don’t. Most of my models are just plain old Ruby objects (POROs). In fact, I don’t even see why models should have to take the form of objects at all, but since I’m personally a big OOP fan and so I almost always write my models in the form of objects.
A model doesn’t have to have anything to do with business/domain logic
Some developers define models as the part of the application that has to do with domain logic. I don’t see it this way.
To me, a model can model anything. A model can model domain logic (i.e. stuff that has to do with the peculiarities of the organization/industry the application serves) or it can model application logic (i.e. stuff that’s purely technical, and doesn’t particularly have anything to do with your specific domain).
A Rails app can be made almost entirely of models
I view Rails controllers as just very thin entry points that deal mostly with HTTP requests and responses. The real “meat” of the application is in its models. And I find that models, in the form of POROs, can account for 95%+ of my needs.
I don’t use Interactors or service objects or any other pseudo-patterns like that. In fact, it could be argued that Interactors and service objects are models, they’re just models that have been adulterated with some confused ideas and therefore are less effective at the job models are intended to do: to simplify and abstract reality to make reality easier to work with in code.
Takeaways
- Modeling is an activity that aims to make a particular part of a program easier to understand and work with by representing parts of reality in a simplified way that can be expressed in code.
- A model is not a simulation. Rather, it’s an abstraction.
- A model doesn’t have to inherit from Active Record.
- A model doesn’t have to specifically deal with domain/business logic. A model can deal with any aspect of reality, including imagined realities that only exist in the context of your program.
- Rails apps can be made almost entirely of models. No Interactors or service objects are necessary.