3

"Transactional outbox" is a pattern for distributed systems which makes use of a single db transaction to do both, persist changes to the domain model and send a domain event message to the outbox.

In my understanding, being responsible for updating the domain model and sending out domain events are two quite separate responsibilities, it's not just like "having several tables per database".

Does this count as violating the Single Responsibility Principle?

2
  • 5
    In fact, the very purpose of a database transaction is to insure that more than one operation succeeds together, or none of them do. Note that SRP doesn't mean "do only one thing." Commented Apr 1, 2021 at 19:37
  • 1
    @RobertHarvey - is "SRP" the single most misunderstood of the software design "principles"? I think yes.
    – davidbak
    Commented Apr 2, 2021 at 14:54

3 Answers 3

7

I think like most so-called principles (at least those originating broadly from proponents of OO and Agile), they are essentially empty tautologies which exist primarily to make it look like the author is communicating something useful.

It's like saying "God is good", scarce any of the faithful can disagree, but it begs the very question of what is God - and what is good!

The "single responsibility principle" essentially introduces the concept of "responsibilities" which do not necessarily correspond to any visible element in a normal taxonomy of code - methods, classes, or modules. So it assumes we already know what a "responsibility" is - something abstract in the head of the designer, I would dare say.

But it does tell us that each of these ordinary elements should correspond to one such "responsibility".

What confuses me firstly is that if a method has a single responsibility, then must the class, module, and application contain only the one method, lest the higher levels fall foul of having further, multiple responsibilities? Or is the real principle that of "one responsibility or less", where the whole application has one responsibility, and the lower elements contribute to fulfilling that responsibility in part, divide and conquer style? This principle leaves me to do all this guesswork.

And what of our common sense notions, that life does consist of having multiple responsibilities, and sometimes having to coordinate them, or find solutions that elegantly meet multiple responsibilities (i.e. "killing two birds with one stone")? Such concepts of combining or coordinating responsibilities must ultimately be represented in code, and thus those elements must become concerned with multiple responsibilities.

I suspect at this point that mental gymnastics are applied, and proponents decide that the element which combines or coordinates multiple responsibilities has only one responsibility itself, which is the sole management of multiple responsibilities that actually "belong" individually to other elements (but do not belong to itself, since that would violate SRP). But now we not only have the additional notion of responsibilities being "owned", but also of responsibilities being "owned" by elements different from those which actually control the fulfilment of those responsibilities. This now sounds more like a Dilbert sketch involving Catbert, than a sound principle of software design.

In my view the "single responsibility principle" is about as profound as simply saying "don't get yourself in a muddle", but robbed of a comprehensive set of examples which show exactly what muddles look like and what alternatives exist, it communicates nothing at all, and is liable to mislead far more than it is liable to guide.

4
  • 1
    SRP would probably be better restated as "In general, it is better that a module have fewer responsibilities". Or perhaps "A module should have as few responsibilities as practical". Not quite as catchy though.
    – MattHH
    Commented Apr 13, 2021 at 0:45
  • 1
    +1 This answer very much summarizes my views on SRP as well.
    – wasatz
    Commented Apr 13, 2021 at 6:05
  • @MattHH, another way of stating "as few as practical" is to say "as many as necessary", which seems to me like the ideological opposite of saying "one responsibility only". It's possible to cause a muddle by having too many responsibilities when fewer would do, but how many responsibilities per part are the fewest necessary to solve a given problem, and how many parts are necessary overall to cope with the minimum number of responsibilities in a clear enough fashion, is probably a product of trial and error in most cases. (1/2)
    – Steve
    Commented Apr 13, 2021 at 6:51
  • 1
    There is also the complication that many perceived responsibilities arise as a consequence of choosing to design a solution in a particular way - such as how in being responsible overall for transport of people, a car must have something subsidiary which is responsible for combustion, whereas a pedal bike need not - so it's actually rather difficult to talk of the responsibilities of code in isolation from its actual implementation. In other words, it's difficult to choose an alternative implementation of responsibilities, without tacitly reconfiguring the responsibilities themselves. (2/2)
    – Steve
    Commented Apr 13, 2021 at 7:04
5

SRP is a code design principle which helps keep code easy to understand, decoupled, and reusable. This is mostly for code organization in object oriented programming.

Outbox pattern is a technique for solving the problem: what if this transaction succeeds, then the event we have to send for that transaction fails? This is an architectural technique for ensuring an eventing system does not lose events and can continue to process incoming events even if there is a failure to send outgoing events.

Using the same transaction to persist the outbox message is not just allowed, but is required for the technique to work in the event of failures.

In my opinion SRP does not apply to Outbox Pattern. Outbox pattern does not make code harder to understand, more coupled, or less reusable. And SRP does not really apply to an architectural pattern. It is sort of like saying: if the database has more than one table, is that a violation of SRP? Of course it is okay to use multiple tables in a single database.

2
  • 5
    Darn. I've been creating a new database for each table. I knew something felt off. :)
    – Eric King
    Commented Apr 1, 2021 at 23:10
  • 2
    @EricKing, you have entire tables in your database? I followed SRP and normalisation and I model a database for every field. Is that not how you're supposed to do it?
    – Andy
    Commented Apr 2, 2021 at 8:32
5

It depends on how you structure your code:

  • if you mix up the code for "updating the domain model" and inserting a record into the "outbox" table (together with the enclosing transactional code) in a way it cannot be used, reused, and tested separately, then this code would violate the SRP

  • if, however, you put the code for "updating the domain model" into a class (for example, a repository class) RepoXY which does not contain any transactional code, put the code for inserting a record into the outbox table in another class RepoOutbox, and have a third class which coordinates the calls to RepoXY and RepoOutbox as well as the transaction, then this code does not violate the SRP. Note this third class does not violate the SRP as well, since it has only one responsibility: coordination of the steps for this DB transaction. (See also this former SE.SE Q&A)

The SRP is not about forbidding an application to do multiple things, or to do things which require a certain order, it is about how classes are structured inside the code base, to achieve better testability, maintainability and reusability.

Note for lots of real-world cases it is not necessary to apply this separation really on the class level, but only at the function level, or not at all. The SRP is not an end in itself, it is a means to an end, and different systems have different requirements for testability, maintainability and reusability.

1
  • @MattHH: if one uses repositories or not is pretty independent from my answer to this question. Somewhere in the system there is code for updating the domain model, and somewhere code for updating the outbox table. One can mix this up in a single function, or one can separate it cleanly, that is what makes the difference between vioalting the SRP or not.
    – Doc Brown
    Commented Apr 13, 2021 at 1:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.