6

I'm a student of best practices, architectural patterns, and design principals. I have been studying dependency injection and inversion of control a lot lately, and have been "drinking the koolade" pretty extensively with a lot of my projects over the last half a year or so.

Recently, I got into a discussion about this with my boss. He asked me to expound the virtues of dependency injection particularly as it applies to our specific environment. He isn't a developer himself, but he has a keen ability to comprehend programming languages and related concepts. Specifically, he wanted me to explain to him why using dependency injection was better than having a core library which serves as an API. In other words, this is how we would traditionally architect an application, which has at its core an infrastructure module (data access):

enter image description here

and this is how it might look using DIP/DI

enter image description here

The arrows in each diagram of course represent the direction of dependency.

I told him that loosely-coupled code is always better because it allows you to swap components without affecting other areas of the application and having to re-write a lot of code to target the new components. I explained that coding to an abstraction rather than to a concrete API was preferable because it forces you to think in terms of that abstraction and thus elements specific to any concrete implementation are less-likely to leak out into other modules. I also explained that it's easier to test the components in isolation and use mocks for external systems.

Setting the testing argument aside for the moment, he wanted to know why we couldn't just modify the infrastructure module without affecting its public interface. For instance, if we decided to change from SQL Server to Oracle as a persistence technology, we could just make those changes in the data access module and the modules above it would be none the wiser.

I didn't have a good answer for him (other than the advantages for testing). Since we're unlikely to switch from SQL Server to Oracle in our particular environment, all I could go with was "it's just best practice" and reiterate the reasons I stated before.

What else could I have said to bolster my argument?

  • coding to an abstraction rather than to a concrete API was preferable because... What abstraction might you be referring to? – Robert Harvey Nov 18 '16 at 19:54
  • In the example shown in the diagram, IDataRepository – rory.ap Nov 18 '16 at 19:54
  • 1
    You can do that without a DI container. What additional justification do you have for the DI container? – Robert Harvey Nov 18 '16 at 19:55
  • I didn't say anything about DI/IoC containers... – rory.ap Nov 18 '16 at 19:55
  • 1
    Is it? Your boss may think that you want to stand up a whole new infrastructure (you did mention DI, and most people think of DI containers when you mention DI). If it's just about conforming to an interface instead of an implementation, that's all you need to make a case for, and that seems like a pretty easy sell to me if, in fact, there is some reasonable chance that the implementation may get swapped out later. – Robert Harvey Nov 18 '16 at 19:58
7

Main goal of Dependency Inversion Principle is change direction of dependency.

In your example UI layer depend on Presentation layer and Presentation layer depend on Business logic layer. So if you make a change in Business logic you need to recompile all layers, because they have dependency on lower level layer.

why can't we just modify the implementation in situ given that it won't affect the modules that depend on it because we only change internal aspects of the implementation and leave the public API alone

Because we want isolate detail/implementation changes from higher level layers.
When we make change in lower level layer we want to recompile only this layer.

That's why higher level layer need to provide interface which it expects from lower layers. Lower layer will implement this interface.

So in your application you will end up only with one layer, entry point of application, which will have dependencies on all implementations. The role of this layer to combine them together.
Here Dependency Injection will help. Most of Dependency injection frameworks provide possibility dynamically load all dependencies(implementations). Which give as possibility update our software without reinstalling it, you will provide only updated implementation library to the user.

notice direction of dependency arrows

     Run-time dependencies                Compile-time dependencies
-------------------------------        -------------------------------
|                             |        |                             |
|          UI Layer           |        |          UI Layer           |
|                             |        |                             |
----INeededBehvaiorInterface---        ----INeededBehvaiorInterface---
               |                                     ^
               v                                     |
 ------------------------------         ------------------------------
|                             |        |                             |
|        Business logic       |        |        Business logic       |
|                             |        |                             |
-----IDataServiceInterface-----        -----IDataServiceInterface-----
               |                                     ^
               v                                     |
 ------------------------------         ------------------------------
|                             |        |                             |
|          Data layer         |        |          Data layer         |
|                             |        |                             |
-------------------------------        -------------------------------
  • 3
    Downvoter, please explain. I will be glad to improve my answer. – Fabio Nov 19 '16 at 20:25
  • 3
    Yeah no kidding. Tough crowd. – rory.ap Nov 19 '16 at 22:10
  • 5
    Don't bother with down votes and votes to close. Stack Exchange is full of people who have nothing to do but exercise the power of votign with no rationale – John Kouraklis Nov 19 '16 at 22:53
3

The way you set the example and the question makes the answer really difficult as what you present here is a generalisation; thus, your responses to your boss are general arguments (which, BTW, are perfectly correct). I feel that you can make a strong case if you look at specific examples and show the benefits.

On a general note, here are a few of notes/ideas:

  1. I don't think your second graphs shows the DI concept correctly. Fabio's answer tries to show the boundaries better and how DI may fit

  2. Robert tried to raise another important point when he asked you to clarify which is the abstraction you refer to when you write "coding to an abstraction rather than to a concrete API was preferable". I think the right meaning is to code against abstraction and not against IMPLEMENTATION as APIs are an expression of abstraction by definition

  3. Building on (2), what you also get with the DI approach and, consequently, with the use of interfaces, is expandability (the "Open/Close" principal in SOLID design). Here is an example:

Say YouBusinessLayer accepts IDataRepository and the latter provides you access to your SQL Server.

As you correctly say, if you change to Oracle you can develop a new IDataRepository(OracleDataRepo) descendent and plug it in to YourBusinessLayer(OracleDataRepo) instead of YourBusinessLayer(SQLDataRepo).

So, now you have this hierarchy:

IDataRepository --> OracleDataRepo and IDataRepository --> SQLDataRepo

At this stage, your boss has a point because you can modify the implementation.

But how about if you want to provide additional functionality to existing data layer. I don't know the details of your case so I will hopefully pick a relevant case: say you want to use a logging service from a third party provider which uploaded data to a server.

If you use your current design, you need to use inheritance and polymorphism and if you have the Oracle and SQL classes you need to update both of them. Using DI and interfaces, you look at the IDataRepository and change that only at one point in your code.

Then, how about if you have another team responsible of developing part of your Data access layer (again, don't know tour actual details). Say, the logger is developed by someone else. How will s/he/they work on your class? You need to stop working and give them access to your code.

With interfaces and DI, things play nicely by favouring composition instead of inheritance. This means, you use dependency injection to receive the logger via an interface.

And one last example I can think of is this: how about if your clients (or users of the data access layer) have different needs in terms of features and you don't want to expose all the features to everyone. With DI and interfaces, you inherit IDataRepository, apply your rules accordingly and then the old statement YourBusinessLayer(SQLDataRepo) will consume the new interface nicely.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.