This whole thing exists on a normalized/de-normalized spectrum. The problem is that both ends have pros/cons.
On the normalized side, you have the benefit of single-point-of-touch and enforcement of a standard implementation. This can make code maintenance easier if used in the correct places. It can make code maintenance a living nightmare if you try to normalize too many contexts into one method. If you find yourself 10 layers deep in a conditional statement trying to determine specific context, you may be better off with some degree of de-normalization (duplication).
On the de-normalized side, you have the benefit of specific, scoped implementations. Models and logic pertain more specifically to a particular domain or function. This can make reasoning with complex logic much easier as you are able to deal with specific business processes in isolation. You will likely see fewer conditionals in de-normalized codesites. Obvious downsides are that if you need to fix a bug with some piece of logic and 100 different features implement that separately, you can wind up with a nasty code maintenance session.
I find that a careful combination of both of these ideas results in the most ideal application. Stateless common code abstractions which cross-cut stateful, feature-specific code abstractions seems to be the Goldilocks for our most complicated software.
On the normalized side, you have the benefit of single-point-of-touch and enforcement of a standard implementation. This can make code maintenance easier if used in the correct places. It can make code maintenance a living nightmare if you try to normalize too many contexts into one method. If you find yourself 10 layers deep in a conditional statement trying to determine specific context, you may be better off with some degree of de-normalization (duplication).
On the de-normalized side, you have the benefit of specific, scoped implementations. Models and logic pertain more specifically to a particular domain or function. This can make reasoning with complex logic much easier as you are able to deal with specific business processes in isolation. You will likely see fewer conditionals in de-normalized codesites. Obvious downsides are that if you need to fix a bug with some piece of logic and 100 different features implement that separately, you can wind up with a nasty code maintenance session.
I find that a careful combination of both of these ideas results in the most ideal application. Stateless common code abstractions which cross-cut stateful, feature-specific code abstractions seems to be the Goldilocks for our most complicated software.