Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Single responsibility principle on complex process

I always had a question on how to assure the single responsibility principle when the process I have to assure is quite complex.

I work with a 3 layers architecture Backend: Controller (my API endpoints) | Service (single responsibility functions) | Data (access to the DB)

Let's say I have a process ProcessA that is composed by 4 tasks TasksA1, TasksA2, TasksA3, TasksA4.

If I have an endpoint exposed on my controller layer such as: POSTMethodProcessA

How should be composed my code in order to respect the single responsibility principle on my service layer?

The options I see:

Option 1 (the controller must know the process):

class MyController {
  exports.processA = functions.https.onRequest(req, res) => {
    myservice.doTaskA1(); // single responsability on task1
    myservice.doTaskA2(); // single responsability on task1
    myservice.doTaskA3(); // single responsability on task1
    myservice.doTaskA4(); // single responsability on task1
  });
}

Option 2 (the service know the process and loose the Single responsibility)

class MyController {
  exports.processA = functions.https.onRequest(req, res) => {
    myservice.doProcessA();
  });
}

//inside the service (the doProcessA method must be in charge of multiples tasks and loose the single responsability principle :
class MyService {
  function doProcessA() {
    this.doTasksA1();
    this.doTasksA2();
    this.doTasksA3();
    this.doTasksA4();
  }
}

This question is even more complicated to me if the tasks are composed themselves by multiple jobs: FirstJobA1, SecondJobA1, ThirdJobA1 ...

How those complexities layer should be handled on the code structure to respect the single responsibility principle is something that always blocked me.

like image 211
Aion Avatar asked Sep 12 '25 21:09

Aion


1 Answers

A common misconception is that Single Responsibility Principle means that a class (or service or a system, etc.) should do only one thing. Instead, SRP means that the subject must have a single reason to change.

Thus it's fine to have separate "service" implementations for each task, and then an "aggregate" service that orchestrates the whole process:

service TaskA;
service TaskB;
service TaskN;

service Process{
    TaskA::run();
    TaskB::run();
    ...
    TaskN::run();
}

A change in a task should not affect unrelated tasks. Also, a change in the process should not affect the subtasks. This is related to the Common Closure Principle (CCP) which states:

The classes in a component should be closed together against the same kind of changes. A change that affects a component affects all the classes in that component and no other components.

or more informally:

gather into components those classes that change for the same reasons and at the same times

This means that if a change in TaskA will inevitably lead to a change in TaskB, then maybe those two should be a single task, instead of two.

Apply that recursively to your sub-tasks: FirstJobA1...FirstJobA-N

like image 140
Svetlin Zarev Avatar answered Sep 15 '25 13:09

Svetlin Zarev