I do this and advocate for this in the teams I work in. In my experience, it works well.
Service classes are "configurable functions", as I tell my team. With the advantage that you don't have to monkey-patch an import in another module to do a unitary test of the function, but rather you can inject mocks!
One thing I do insist on is that a service class either does something (calculate some value, retrieve some information, etc.) xor it coordinates service classes (parse value, then call the validator and, finally, persist value). This aims to prevent deep call chains where every function in between does a little bit and calls someone else, which can be hard to reason about.
Service classes are "configurable functions", as I tell my team. With the advantage that you don't have to monkey-patch an import in another module to do a unitary test of the function, but rather you can inject mocks!
One thing I do insist on is that a service class either does something (calculate some value, retrieve some information, etc.) xor it coordinates service classes (parse value, then call the validator and, finally, persist value). This aims to prevent deep call chains where every function in between does a little bit and calls someone else, which can be hard to reason about.