The MVC architecture, today's standard for at least web development, if abused, often turns into a nightmare of objects sharing a lot of responsibilities, and spanning across different areas, knowing about different objects, essentially defeating the holy grail purpose of separation of concerns.
We shouldn't be polluting MVC, we should be embracing it. There's a reason that monolithic have become a problem, and while I certainly favor micro-services and SOA for complex applications, I'm aware that usually the problem with them it's not having everything under the same directory structure, it's not the team, and it's not isolating components, it's usually the code inside that app that has become a burden to maintain and refactor.
I'm a big fan of isolated presentation logic, logical models, and decorators/presenters and here is a quick post with my opinionated views and resources that will address some topics (mostly in Ruby/Rails development) that will help you keep your code DRY and maintainable.
Data Context Interaction (DCI) is a big topic, but I'm going to address it first. It's not only a big topic, it's a big change to the way you program, and the design of your software, I'm in ❤ with it.
If you have the time and the interest in order to explore it, I'll recommend you to do your research, starting at the above resource, and maybe even pick Jim Gay's "Clean Ruby" book on the subject, it's great reading.
act_as_api, etc. I think is a big
no-no to have your controller provide your API endpoints. It gets so convoluted
if you're serving multiple formats, but specially I believe its purpose gets
blurred along the times, versioning and namespacing becomes an issue (once
you're migrating and deprecating versions), parameter declarations and security,
and in general, it's something that I wouldn't advise you to try to maintain.
Your API logic should be separate from everything else, (and so everything else from your API), and a way of doing that would be with the Grape API framework. But just don't use Grape, respect the lines, the architectural lines, use only Grape methods in your endpoints, everything else should be on your models, or contexts preferably.
Try to have logic-less views, prefer them, even enforce them. We're being a bit hypocritical for mocking PHP and JSP when we have the tools available and yet we fall back (to a minor extent, sure) to this PHP-itis in larger projects. There's a reason some client-side templating systems avoid all logic.
And before you go "what 'if'?, I mean I need my
if", remember all
conditionals could be replaced with polymorphism, just saying.
I know we need to do "things" after our models get saved or created, I
completely agree. However
ActiveSupport::Callbacks behavior is coupled by
nature. This has been discussed a lot in the past so I won't rehash it. You can
use publish/subscribe instead, and this great
takes a look at it, including my preferred tool of choice
wisper (BTW if you're interested in
contributing at some point to my MongoDB adapter for
it, let me know).
Remember that the benefits of decoupling are "coupled" with the ones of introducing indirection 👌, and that's a good thing. In most cases it allows code and environmental(s) to change without having to change the core. Today it's a simple callback, tomorrow could be something that gets pushed to a background job or to a distributed system, and only tomorrow you'll thank yourself for having introduced this separation allowing you to, in most cases, just switch an Adapter.
Notice that while I do prefer
wisper you can do similar things with
ActiveSupport's instrumentation API.
EDIT: An interesting question was asked on
which I translated as what would happen when you need to halt the operation
based on some condition, that's an interesting concern, I took a quick look at
the code in particular, and except for asynchronous scenarios, it looks like
raising an exception would have the same effect that it does on
ActiveSupport::Callbacks, but since this a very narrow implementation detail
and answer, you might want to research your implementation of choice, however I
still believe that generally-speaking the benefits of pub/sub outgrow the costs.
For an API you should be presenting logical models instead of physical ones. Here the tool support is not that broad, but you should certainly not be using the model's JSON representation or a variant of it, you need to be explicit, for security, for correctness.
If you're interested in hypermedia at all, although can be used without it, oat is a very interesting presenter project. I will be expanding on hypermedia API development in Ruby in a future post.
That concludes what it will most likely be a multi-part series, let me know if you have any comments below; you can also join the Reddit thread.