Ironically, for different reasons, solutions that are over-engineered and under-engineered share a similar problem: they tend to be inflexible.
Even well-designed object hierarchies - what we'll call the over-engineered solution - are weak here because you've tried to map out ahead of time all of these possible scenarios.
Clemens does a wonderful job explaining this here.
The idea of “self-contained” domain objects, while thought to be an ideal modularization model, indeed most often fails to provide modularity. . . . Although the idea of object-centric storage, object self-responsibility and universal object-identity is fantastically attractive, a single object implementation that attempts to accommodate all these requirements simply results in a monolithic application block that is anything but modular.
While encapsulation is a desired effect, we're encapsulating in the wrong thing. The possible operations to be performed on an object should not be bound to the object (the data) itself. This isn't how things are encapsulated in the real world, so why should we try to apply this principle when designing software?
A better model is that of one of the simplest yet most flexible constructs around: the sentence.
The reason that sentences are so flexible is because the language recognizes cohesion even when there might not appear to be any similarity on the surface. There might be no reason to think that a box and an apple share any similar “functionality“ or are even related in any way - but the fact of the matter is you can plug in either interchangeably in a sentence (well, many sentences at least).
Programming in this model becomes that old game where you build some obscure insult by picking one word from columns A, B and C.
The object is the noun - the it we're dealing with. The service is the verb - the general action to perform on that it. The provider is the adverb - the modifier, telling us specifically how to go about performing that action.
To achieve this ultimate modular and flexible state, an architecture should allow you to build simple operations and a means to combine these modular entities into increasingly more complex sentences.
