I am not negating anything you say, Jan. Still, there is something I always take into account — premature optimisation / over-engineering. There is also another thing called Test-Induced Design Damage. I tend not to strictly adhere to principles, when result I achieve does not work for me — and yes, this is subjective approach, but also a proven one over many years of development. It does not mean that your arguments are wrong, it just mean that we are after different things.
I am not after strict isolation and I am not after high granulation that causes even higher indirection. All of these are costs for me. Yes, the code is super testable and super elastic, but if the code is testable enough and just elastic as I need it to be I am fine with that.
This being said — when adhering to rules presented in this article, my services do not grow out of control, and if they do, they are still easily decomposable into smaller ones. And I find leveraging state in them VERY helpful, even though I never instantiate a service directly (nor use its instance for any purpose) — this basically makes (allows) internals of the class very elegant — when there is a need to pass data between methods I just use state for it. And it works great. And it looks and reads great. No other reasons ;)
“even though I never instantiate a service directly” is an important part — this way I never mock object creation on client code and only test static “call” on service itself. And this results in just
expect(CreateUser).to receive(:call).with(name: ‘adam’, surname: ‘nowak’, email: ‘firstname.lastname@example.org’)
There is also no need to mock return value (if you follow other recommendations from this article). Actually in this case I would probably have passed already initialised form object as a dependency of the service instead of passing attributes themselves, but I treat it just as an example.
Still, thanks for interesting discussion — I do always love to see different perspectives! Kudos!