Design – Am I making the classes too granular? How should Single Responsibility Principle be applied

clean codedesignsingle-responsibility

I write lots of code that involves three basic steps.

  1. Get data from somewhere.
  2. Transform that data.
  3. Put that data somewhere.

I typically end up using three types of classes – inspired by their respective design patterns.

  1. Factories – to build an object from some resource.
  2. Mediators – to use the factory, perform the transformation, then use the commander.
  3. Commanders – to put that data somewhere else.

My classes tend to me be quite small, often a single (public) method, e.g. get data, transform data, do work, save data. This leads to a proliferation of classes, but in general works well.

Where I struggle is when I come to testing, I end up will tightly coupled tests. For example;

  • Factory – reads files from disk.
  • Commander – writes files to disk.

I can't test one without the other. I could write additional 'test' code to do disk read/write also, but then I'm repeating myself.

Looking at .Net, the File class takes a different approach, it combines the responsibilities (of my) factory and commander together. It has functions for Create, Delete, Exists, and Read all in one place.

Should I look to to follow the example of .Net and combine – particularly when dealing with external resources – my classes together? The code it still coupled, but it's more intentional – it happens at the original implementation, rather than in the tests.

Is my issue here that I have applied Single Responsibility Principle somewhat overzealously? I have separate classes responsible for read and write. When I could have a combined class which is responsible for dealing with a particular resource, e.g. system disk.

Best Answer

Following the Single Responsibility principle may have been what guided you here but where you are has a different name.

Command Query Responsibility Segregation

Go study that and I think you'll find it following a familiar pattern and that you're not alone in wondering how far to take this. The acid test is if following this is getting you real benefits or if it's just a blind mantra you follow so you don't have to think.

You've expressed concern about testing. I don't think following CQRS precludes writing testable code. You might simply be following CQRS in a way that makes your code not be testable.

It helps to know how to use polymorphism to invert source code dependencies without needing to change the flow of control. I'm not really sure where your skill set is on writing tests.

A word of caution, following the habits you find in libraries is not optimal. Libraries have their own needs and are frankly old. So even the best example is only the best example from back then.

This isn't to say there aren't perfectly valid examples that don't follow CQRS. Following it will always be a bit of a pain. It's not always one worth paying. But if you need it you'll be glad you used it.

If you do use it heed this word of warning:

In particular CQRS should only be used on specific portions of a system (a BoundedContext in DDD lingo) and not the system as a whole. In this way of thinking, each Bounded Context needs its own decisions on how it should be modeled.

Martin Flowler: CQRS

Related Topic