Cut Out The Middleman

Jun 27, 2017
-
6
minute read

Too much decoupling in software development is arguably worse than too little. Excessive decoupling leads to software that is more complex, more verbose, harder to understand and much less maintainable. Don’t let your critical use cases get split up into little tiny bits and strewn about your application!

There are several causes of excessive decoupling. The one I want to address in this post is the dysfunctional approach to getting everything through an intermediary. Why does everything need to be acquired through a Repository, a Supplier, a Provider, a Service, a Lookup, a Builder and so forth? Do we need so much separation?

Now we have a bunch of different objects strewn about the system. In order to track down an Invoice problem, developers have to trudge through several areas of the system. Is the right data in the database? Is the InvoiceBuilder correctly assembling the various segments of the Pdf? Is InvoiceService getting the right things from the Repositories? Are the repositories getting the right things out of the database?

Furthermore, any changes to the structure of the invoice are going to require changes to a large number of classes. To add a new segment to the Invoice, you’ve got to change your database tables, update your repositories, update your data transfer objects, update your InvoiceService and update your InvoiceBuilder. That sounds like a lot of work. That’s because it IS a lot of work.

How much value are we getting by organizing the application functionality this way? Is there a better way to arrange this code?

Yes! Cut out the middlemen! Code is poorly arranged if you need to change several source code files at the same level of abstraction to implement a new business use case. It’s far better to have a larger object that is cohesive than smaller objects that hide and disconnect critical pieces of functionality.

Evolving the Code

Let’s evolve the code example above into something simpler and more direct. First, we must step back and consider WHAT we want done. This means throwing away all preconceptions about HOW it will get done.

What we want is an Invoice, presented as a PDF. The invoice consists of some information about the Customer, and some information about the Order.

This is far better than the earlier code with all the Repositories, Builders, and Services. It is more expressive, more cohesive, and has a simpler API. Let me point out a few specific benefits:

1. Clear Business Concepts

In the original code, there was no object modelling an Invoice. Now, we have an Object that represents a business thing and consists of precise information: a Customer, Items and a Total. Those are business terms, not computer-science language.

2. Less Data Trucking

Our PDF is easier to create. Previously we had to independently find and pass in very particular data arguments in order to build the Invoice Pdf. Now we simply create an InvoicePdf using an Invoice. If tomorrow your project owner decides that the Invoice needs four new pieces of information, no public methods or constructors for the InvoicePdf will change.

3. No DI Container Needed

With objects composed in this manner, you don’t need a DI Container to inject all the right pieces of functionality into your application. There are fewer moving parts in the system, and each of the parts is simpler.

4. True Dependency Inversion

Previously the InvoiceService depended on the InvoiceBuilder. The service was coupled to the presentation mechanism. Now, InvoicePdf depends on Invoice, but Invoice knows nothing about presentation concerns.

Those are just a few concrete benefits that we gain from cutting out the middleman and focusing on the actual business functionality of the software.

Our recent solution is good. We can make things just a little better by moving away from the database-oriented models and raising the abstraction level one more time. Let’s make Order responsible for his Customer, rather than requiring all calling code to assemble disparate pieces of information.

Summary

Sometimes when you’re in a codebase that has a lot of needless indirection, it becomes easy to think that the indirection is necessary or even a “best practice”. It’s time to take a step back. Do I actually want a CustomerBuilder, or do I want a Customer? Do I want a ProductsLookupService or do I want a ProductList? Do I want a CarProvider, or could I simply use a Car?

Move towards concepts and language that express the problem itself. Try to minimize the number of different classes, functions and objects that data passes through. Always go directly to the source instead of through a middleman. Avoid unnecessary indirection.

As you work towards eliminating the indirections, your code will become simple, concise, and easy to use.