What would you name a class which does only a very small part of functionality, for example taking a recurring payment profile and marking it as ‘failed’.
Is it correct to name it RecurringPaymentProfileMarkAsFailedService? I know it’s just a name, but I would like to adher to standards / convention. Is this what a ‘Service’ class should do, or is this a different design pattern? I am trying to follow up on the line of the SRP principle, and thus if I am correct one would end up with a lot of small classes, each specialised in one task. I would like to define a correct naming standard.
Though I’ve never seen it strictly documented, I typically use the word ‘Service’ to indicate a class that handles some subset of related end-user functionality.
For example, say I have a bank account system, I might have the following service:
So the “high-level” functions that the user can do are encapsulated in my Services. Services will access the database, instantiate whatever helpers, do calculations, etc that it needs. But like I said, this is just what I’ve observed from my time in the industry.
As far as SRP: SRP does not mean that you need a whole new class for each function you want to perform. You don’t need a class to mark a profile as failed and another to mark it as success and another to change the name of the profile and …
SRP means that each class handles “one thing” but that “one thing” can be a set of closely related functions. Another important OO design principle to consider is encapsulation. That is, a class should minimize what it exposes to clients – only exposing what others need. If you are having tons of little classes that manage a PaymentProfile’s internal state, you are exposing all of the details of PaymentProfile. I’ll give you a small example:
Let’s say your PaymentProfile object has a boolean isSuccess. Then your class RecurringPaymentProfileMarkAsFailedService does something like this:
Cool – everything’s working great. Now we get a feature request that not only can a PaymentProfile have statuses ‘Success’ and ‘Failure’, but now we need to support ‘InternalError’ which means some internal system crashed during the update. We can no longer use a boolean, so we change our boolean isSuccess to an enum that can be one of: SUCCESS, FAILURE, ERROR. Now we have to refactor our MarkAsFailedService.
This is a trivial refactor b/c it’s just one class, but for a complex class tree, these refactors could echo all across a system. It’s a sign that I’m revealing too many implementation details about my PaymentProfile. A class should be encapsulated so a change to it’s implementation has little/no impact on it’s clients.