The kind of "architecture" that you are talking about that was often postulated under the banners of "Single Responsibility Principle" (a poor rule of thumb in fact) and "design for testability", that results in one-method classes, or classes that do not have any state and pass everything in via method parameters is in fact contradicting basic tenets of OOP like encapsulation and having a reusable domain model - I find it, like DHH, a horrible abomination, even if tests are faster because of it.
There are lots of other ways of dealing with complexity in Rails that do not involve any of this kind of thing. Some of the people who go around talking about DCI and services have problems with basic OO modelling or even with simply writing good methods (it actually takes a fair amount of skill), with Ruby and Rails knowledge, and drowns in complexity not because of Rails default architecture patterns, but because of a general lack of coding skills, lack of developer-PM communication that increases essential complexity, lack of developer-developer communication, NIH, etc. Here is a whole bunch of thing that you can do without introducing "service objects" and all that:
- Extract non-business-logic-related general components like API wrappers, widgets into a Gem, Rails engines, jQuery plugin etc.
- Use existing gems and techniques that concisely encode high-level patterns, like state machines
- Extract common pieces of behaviour into controller or model mixins (concerns)
- Use finer-grain modelling, e.g. introduce value objects (see documentation of composed_of) or simply split models into smaller ones, e.g. instead of having simply a User model, separate User and Profile. Extract very complicated algorithms into separate classes.
- Promote code reuse by taking extra care of having an ultra clean API for crucial domain logic operations - the methods that correspond to those should be listed in one place with brief descriptions, documented in detail where they are defined, have easy to remember names, flexible parameter lists allowing handling of different use cases, clear error signaling and so forth.
- Group related models in modules.
I would like to see one codebase that perfects all the basic coding practices of this kind and yet still has issues with the explosion of complexity. Somehow people manage to write, for example, complicated games spanning several hundreds of thousands of lines of code without introducing LightRayHitSpherePredicateFactories all over the place, and to a large extent I think they do so by mastering the basics of the kind listed above. I would also like to see one codebase that uses DCI or Service Objects religiously that isn't a 100 line TODO list.
> The kind of "architecture" that you are talking about that was often postulated under the banners of "Single Responsibility Principle" (a poor rule of thumb in fact) and "design for testability", that results in one-method classes, or classes that do not have any state and pass everything in via method parameters is in fact contradicting basic tenets of OOP like encapsulation and having a reusable domain model - I find it, like DHH, a horrible abomination, even if tests are faster because of it.
Both the kinds of design features you describe are generally bad [1] ways of implementing either SRP or design for testability, and don't seem to be the architectural choices the grandparent post was actually suggesting. Things like, however, taking external dependencies as constructor parameters and coding to their required API rather than baking the specific concrete implementation to use into class design and coding the class to that is more what I think the GP is talking about. This doesn't require limiting the internal state of the object, it just pulls decisions that belong to the calling environment out of the object and back to the calling environment, which makes the code more reusable (including being "reusable" in a unit-testing environment where the external requirements are mocks whose behavior is defined by test parameters rather than adaptors to real external resources.)
[1] There are very narrow specific cases where they might be the right thing
There are lots of other ways of dealing with complexity in Rails that do not involve any of this kind of thing. Some of the people who go around talking about DCI and services have problems with basic OO modelling or even with simply writing good methods (it actually takes a fair amount of skill), with Ruby and Rails knowledge, and drowns in complexity not because of Rails default architecture patterns, but because of a general lack of coding skills, lack of developer-PM communication that increases essential complexity, lack of developer-developer communication, NIH, etc. Here is a whole bunch of thing that you can do without introducing "service objects" and all that:
- Extract non-business-logic-related general components like API wrappers, widgets into a Gem, Rails engines, jQuery plugin etc.
- Use existing gems and techniques that concisely encode high-level patterns, like state machines
- Extract common pieces of behaviour into controller or model mixins (concerns)
- Use finer-grain modelling, e.g. introduce value objects (see documentation of composed_of) or simply split models into smaller ones, e.g. instead of having simply a User model, separate User and Profile. Extract very complicated algorithms into separate classes.
- Promote code reuse by taking extra care of having an ultra clean API for crucial domain logic operations - the methods that correspond to those should be listed in one place with brief descriptions, documented in detail where they are defined, have easy to remember names, flexible parameter lists allowing handling of different use cases, clear error signaling and so forth.
- Group related models in modules.
I would like to see one codebase that perfects all the basic coding practices of this kind and yet still has issues with the explosion of complexity. Somehow people manage to write, for example, complicated games spanning several hundreds of thousands of lines of code without introducing LightRayHitSpherePredicateFactories all over the place, and to a large extent I think they do so by mastering the basics of the kind listed above. I would also like to see one codebase that uses DCI or Service Objects religiously that isn't a 100 line TODO list.