Problem Decomposition
Splitting a problem by responsibility rather than by folder name — and the single-responsibility idea stated as "a coherent reason to change" rather than "one thing".
Split a problem into responsibilities and give each one an explicit interface. Splitting it into folders named after technical types is not decomposition — it is filing.
Ask of every unit: what is this responsible for? If the answer needs the word "and" more than once, you have found the design problem before it found you.
Not "a class does one thing" — that phrasing has no content. "A module should have one coherent reason to change" has some, and even then the hard cases are genuinely ambiguous.
Transport, business logic, persistence, formatting and infrastructure change for different reasons, so mixing them is expensive. Adding a layer for each of them anyway is a different and equally expensive mistake.
The anti-lesson. Splitting by technical type puts every file of a kind together and every file of a feature apart, producing boundaries that no requirement respects.
Three probes locate where a boundary should go: follow the change, follow the invariant, follow the rate of change. All three are questions about evidence, not taste.
Ten files that must all be read together are worse than one file that need not be. Splitting has a cost, it is paid by every future reader, and nothing about it is free.