|
|
|
|
|
by tomblomfield
4479 days ago
|
|
I disagree - I think we're talking about two competing philosophies. DHH / standard-Rails seems to encourage you to fit things into Models, Views or Controllers. Models got too fat, and so Concerns were introduced. This is problematic, because you still have massive god-models, but now their code is split between a dozen different files, called "Concerns". The main problem is that by making ActiveRecord models the core of your application, you generally give each model too much responsibility & too much knowledge of its associated objects. This means it's very hard to break down & recompose the different functionality into new objects. A model only functions correctly if all of its associated objects are present. This is tight coupling. The alternative view is to introduce several new single-responsibility objects like Interactors/Service-Objects (and Policies, as here) which should contain the core of your application's business logic. These Service Objects are the opposite of concerns - they do not extend the API of models. They exist to control the interactions between the models. The models become dumb interfaces that deal with data validation and persistence. Because they know hardly anything about the other objects in the system, and have very limited APIs, they can be passed around easily, and re-composed into more complex interactions. They are also incredibly simple to test. The same applies to Interactors - they should be broken down into small units of behaviour with very limited APIs, which can then be passed around easily and composed into more complex interactions. This is loose coupling. |
|
Rails encourages you to use the "primitives" it ships with, because they've all been more or less designed to work together, and I think this is what DHH is getting at - you get a lot of free behaviour, and in his mind you need a rather strong reason to be throwing them away.
>This is problematic, because you still have massive god-models, but now their code is split between a dozen different files, called "Concerns".
When you put it that way, that still sounds identical to your policies and interactors - you've just chosen a different nomenclature.
Both your approach and concerns are reactions to the way we've been shoving "responsibilities" into our domain objects. When we talk about coupling, we're talking about our ability to reason about our business logic in isolation; generally, the fewer atomic items we have to think about, the easier it is to maintain.
Concerns are just a DSL for writing Ruby mixins; I recently upgraded an older app and discovered I could port it all over by renaming my app/modules folder to app/concerns and I was basically done. So, the differentiating aspect rests in your preferred mental model for breaking up the business logic into these "atomic units".
When you talk about disliking AR models, to my ears it sounds like what you're really saying is "I hate the overhead of hitting the database when I'm writing tests", which is a different idea altogether from "these semantic units ought to only be responsible for serialization".
To me, it's not necessarily relevant if this one object can be persisted; what matters is that it's semantically relevant in my model of the domain/business/etc.
So, at the end of the day there is a ton of logic that is more meaningful when associated with my "users"; but it's an enormous pain in the ass to have huge swaths of my User model governing totally different bits of unrelated functionality. In this circumstance, splitting off the different unrelated bits of functionality is a net win, from both testing and maintenance.
Meanwhile, if we find that there is no good place for stuffing this bit of functionality… then we're probably missing a whole new domain model and we didn't even know it, and imho we're better off encapsulating those behaviours into an independent model.
This is to say: policies as presented in your post strike me as a more convoluted way to achieve this separation of concerns already afford to us by regular ruby mixins/classes.