Going bananas with loose coupling and dependency injection


With the latest additions to our dependency injection framework (annotations in spring), the marginal cost of creating DI-managed components seems to have hit some critical new threshold. While there previously was an overhead associated with spring (tons of XML and additional indirections), dependency injection seems to have started going where lots of patterns go; they go under the hood and "disappear".

The consequence of this is that the conceptual overhead associated with a large number of components becomes acceptable. It's arguable that we could make a system where most classes only expose
one single public method and build the whole system by just aggregating these pieces like crazy. In our case a few things are given; the user interface of your application has some functional requirements that shape the topmost services. And the back-end systems control the lower part. But in between these two, everything is up for grabs.

Our constant discussion is really why are we grouping things in classes and what should the principles be ? A couple of things are certain; the facade pattern is dead and buried. Any service containing multiple unrelated features also tend to get split up. "Unrelated feature" is interpreted in an extremely much stricter sense than I have ever done earlier.

In our team there are two prevailing trains of thought here: Implementation dependencies restrict grouping; any functionality in a single class should preferably be a client of all injected dependencies. We are a DDD project and the other fraction thinks the domain restricts grouping (CustomerService or finer grained CustomerProductService, CustomerOrderService) – normalized usage of injected dependencies is unimportant.

So in the loosely coupled DI universe, why are we grouping logic in classes ?

edit: duffymo point out that this may be moving towards a functional style of programming; which brings up the issue of state. We have quite a few "State" objects that represent (small) pieces of relevant application state. We inject these into any service that has a legitimate need for this state. (The reason we use "State" objects instead of regular domain objects is that spring construct these at an unspecified time. I see this as a slight workaround or alternate solution to letting spring manage the actual creation of domain objects. There may be better solutions here).

So for instance any service that needs OrderSystemAccessControlState can just inject this, and the scope of this data is not readily known to the consumer. Some of the security-relate state is typically used at a lot of different levels but totally invisible on the levels in-between. I really think this violates fundamentally with functional principles. I even had a hard time adjusting to this concept form an OO perspective – but as long as the injected state is precise and strongly type then the need is legit aka the use case is proper.

Best Solution

The overriding principals of good OO Design do not stop at loose coupling, but also high cohesion, which gets ignored in most discussions.

High Cohesion

In computer programming, cohesion is a measure of how strongly-related or focused the responsibilities of a single module are. As applied to object-oriented programming, if the methods that serve the given class tend to be similar in many aspects, then the class is said to have high cohesion. In a highly-cohesive system, code readability and the likelihood of reuse is increased, while complexity is kept manageable.

Cohesion is decreased if:

* The functionality embedded in a class, accessed through its methods,
  have little in common.
* Methods carry out many varied activities, often using coarsely-grained or 
  unrelated sets of data.

Disadvantages of low cohesion (or "weak cohesion") are:

* Increased difficulty in understanding modules.
* Increased difficulty in maintaining a system, because logical changes in 
  the domain affect multiple modules, and because changes in one module 
  require changes in related modules.
* Increased difficulty in reusing a module because most applications
  won’t need the random set of operations provided by a module.

One thing that gets lost when people go crazy with IoC containers is the cohesion is lost and traceability of what and how something does something becomes a nightmare to figure out later own down the road, because all the relationships are obscured by a bunch of XML configuration files ( Spring I am looking at you ) and poorly named implementation classes.