I believe the point of adopting Rails is to maximize the development productivity, namely to build an application very quick with small amount of code. But if you try to do everything within the original MVC, you will easily end up messing up with fat controllers and/or fat models, and lose the maintainability and readability of your codes. This is what I think is a pragmatic pattern enough to be both agile and maintainable.
What is maintainable code?
This style went well in my previous project (edit: updated a bit). Our code became beautifully loose-coupled. “Went well” here means, you can keep models single-responsible, well testable and fully re-usable and controllers well-readable even to non-techies. There are lots of items here, but keep YAGNI in your mind and choose only what you need.
This is where domain logics reside. For most cases, logics that interact with non-ruby world. Persistence(ActiveRecord), File system, Outside service and so on. ActiveRecord model is not the only models. Models cannot have more than one domain, for example, cannot have DB and File System functionality together in one model. Add logics which only relates to the model domain. All methods need to be reusable, free from any procedures and of course single responsible. Models are reusable parts of your applications. When you enhance a gem for our own domain, have a model that extends the gem’s behavior (I often use delgation for this cases).
Sub-domains for Models (Optional)
First of all, we must make sure models have only one domain. For example, if the model is related to two infras (DB, File system, Outside service etc, the same as non-ruby world), we have to separate the model. But even after that, one model can still be huge. Often there are cases where you can find sub-domain(s) inside a domain model in such situations. You can have a sub-directory with the model name and put those sub-domain(s) into it. Examples are, object builders, factories, logics for composited models (such as associations) or other meaningful groups of behaviors. Some fit well as classes, others fit well as modules to be included inside the main model, but they are both namespaced by the main model name. (Remember rails has conventions between directory structures and class/module names.)
Responsible for input processing, sessions, controlling flows and passing the results to view for rendering. They say controllers’ main responsibility is to control the flow. What exactly does that mean? I think it means that we should be able to tell what-to-dos (not how-to-dos) just by looking at the controller. Just write what-to-dos here so that your people can tell what happens in each action. How-to-do should reside in lower layers.
Responsible for serializing Ruby objects into HTML, JSON or something else with templates. When your application is beautifully Restful, you can use a model serializer instead of Jbuilder/Rabl.
Helper methods consumed by views reside here as Rails originally intended. Think if decorator/presentor(below) is more suitable when you add a method here. No side effect methods exist here.
This is what manages complicated business logics (BL). For most cases, BL itself is procedural. If BL is complicated enough such case as when it’s related to multiple models or if BL has a lot of procedures or conditions that our controller cannot handle, we should use it. That’s what is so-called “application layer service”, whereas we can have “domain layer services”, too. Check Service Object gem.
I have seen a naming confusions though. Some people try to put third party web service domain logics here which I think should go to models.
The name “concerns” is confusing I don’t get why they adopted this word.
The shared logics between models reside here. If the logic belongs to only one model, probably you should use “sub-domains” above. One example I used this is to save shared Identity Cache modules such as
Value Objects (Optional)
If you need some computations for immutable objects, we can have value objects. But if that belongs to only one model, we should handle it as “sub-domain(s)” above.No side effect methods exist here.
Decorators / Presentors (Optional)
Logics that belong to a certain model but are consumed by views will come here. Aka presentors. For rails, decorators usually mean presentors whereas decorators has wider meaning originally. Use Active Decorator / Draper gem. No side effect methods exist here.
For HTML widgets that show up across our web application. Use Cells gem. No side effect methods exist here.
When you want to serialize an object in a customized way, you should have this. ActiveModelSerializers might fit your needs.
Extract callback methods from model. You can use the same callback file for multiple models, too, but be careful, callbacks across different domains are a no-no.
Extract validators that inherits ActiveModel::Validator from models. You can checkRails Guides for details.
Socials Aka Policies (Optional)
Not sure who called it policy first, but social modules are to get access token from third party and also to get necessary social graph information. This could be a part of model by nature, but it makes sense to separate this to avoid too many number of models.
-But more importantly-
You should not start with all of the above (YAGNI). You don’t need to separate validations at first. But more importantly, you should always keep things loose-coupled as much as possible (which means, methods should be small and single-responsible) so that you can easily separete things later into reasonable modules whenever you feel the necessity.