Single Responsibility Principle - what does it mean to you and your Elixir apps?

I think it may have more to do with where you are (in reference to understanding the intent of SRP - as it seems to be a new concept for you). If you go by the Dreyfus model of skill acquisition a “novice” will tend towards “rigid adherence to taught rules or plans” - but in design/architecture things are rarely black and white - everything is about making tradeoffs. So the real “mental overhead” comes from the fact that there are numerous design guidelines that have to be balanced to meet the requirements for a particular situation - some will be met while others will be deliberately violated to gain some highly valued benefit.

For Example, Active Record violates SRP because it mixes the responsibilities of business logic AND persistence management. Yet it is listed as a viable Enterprise Application Pattern:
####Pros:

  • Good choice for domain logic that isn’t too complex, such as creates, reads, updates, and deletes.
  • Simplicity; easy to build, easy to understand.
  • Typically less duplication than Transaction Scripts.

####Cons:

  • Works well only if objects correspond directly to the database tables: an isomorphic schema.
  • Complex domain logic using direct relationships, collections, inheritance doesn’t easily map onto Active Record.
  • Couples the object design to the database design; makes it more difficult to refactor either (independently) as a project goes forward.

i.e. if the “cons” aren’t an issue for you (ever) and you value the “simplicity” then Active Record is a valid design choice (and investing in the higher effort alternatives is a waste).

Collectively all these design principles and guidelines are supposed to give you a sense of all the opposing forces and deliberate tradeoffs that come into play when trying to arrive at a “good design”. Ultimately no good comes from applying these principles and guidelines dogmatically.

Aside: Some people don’t like the SRP

Frankly the examples seemed to be easier to justify with “separation of concerns”:

  • Persistence
  • Domain logic
  • UI (+ reports)

Influenced by DDD “Domain Logic” is the focal point - it dictates the data types and is the dominating influence for the interface designs. Given that the domain logic only deals in domain types, SQL statements belong with persistence (provided you’re even using an RDBMS). Ideally the public API exposed by the domain logic should be able to support a whole range of UIs (web, mobile, desktop, command-line, etc. - though possibly with support BFFs) - but even when keeping it simple with a single frontend, domain logic doesn’t belong in the UI (though validation logic may be duplicated there).

The example with the calculations that differed for the clerical and manager report seemed contrived. It’s valid to say that not everything that “looks the same” is automatically duplication - but the example pre-supposes the existence of distinct domain concepts that somehow had the same calculations up to this point.

I think when Scott Wlaschin jovially states (SRP => functions) he is referring to the typically highly focused nature of short, pure functions.

Modules can be used in the role of namespaces. I think the closest thing to a class is a module that defines a struct. That module should focus on the struct and the data within the struct should be focused.

Elsewhere you stated:

To me this read like you felt that Changeset conflated the notion of being a changeset with the responsibility of validation - which sounds like a violation of SRP or at least a lack of cohesion. Yet lots of people seem to appreciate the convenience afforded to them by this conflation (i.e. they are willing to accept this tradeoff).

4 Likes