In today’s post-OO world, is dependency injection still relevant?
It’s 2015. Most of the new popular languages are more or less functional. The old ones, like Java, gain functional programming elements. In Scala, people are increasingly leaning towards the pure side, using more FP and less OO. So – Dependency Injection? Really?
You could say that DI is just using (constructor) parameters. I’m saying that as well in my talks on no-framework DI in Scala using MacWire. Passing parameters is the basic building block of FP. So why give it another name? Is DI a separate concept?
Different kinds of parameters
Recently I had a short discussion with Alois Cochard on his blog, where he describes an approach to DI similar to mine. He also once wrote a DI library (Sindi), so it’s good to know we came to similar conclusions. But that got me thinking – maybe there are two kinds of parameters?
One kind of parameters would be ordinary “data” parameters. That’s what we pass around functions, manipulate, modify, persist, etc. These are entities, aggregates, collections, case classes. The other kind would be “service” parameters. These are classes which encapsulate business logic, allow communicating with external systems, provide data access.
While you could of course use only FP to create an entire system end-to-end, I think a number of people (me included) find it convenient and readable to use FP “in the small” and objects “in the large”, as described e.g. in the Programming Scala book, or in this Quora question. So you have some possibly pure FP code manipulating your data, implementing the core business logic fragments, and that is encapsulated (organized into?) by objects (you could call them “services”, “modules”, or some name better reflecting the overall responsibility).
That’s where DI comes into the picture. It can be very useful when wiring the “service”/”module” object graph. The means to create the object graph may be the same as when dealing with any other kinds of objects – either by doing DI manually, or with the help of MacWire, or with a container – but the way we are using parameters feels different.
Environments
Another way to think about the “data” and “service” parameter split is that the services form an execution environment for our (pure-FP or not) code. Such an environment may contain services for accessing the database, communicating with the user, etc. Currently we use the same mechanism – parameters – to define the environment (e.g. via constructor parameters), as we use to pass data around functions. But maybe in some future programming language, these concepts will be separate?
Or instead of creating a separate mechanism, which could end up as poorer, “shadow” functions, we could have some more convenient syntax for creating and using environments?
A good example of such an “environmental” parameter is the quite commonly used ExecutionContext
in Scala. Currently it is very often passed as an implicit parameter (and it’s contagious, the implicit is needed in every place we want to manipulate futures, from method to method …). At least for me it is a different “kind” of parameter then, let’s say, a Set[Person]
. Wouldn’t it be great if we could just assume that our code is executed in an environment, where “some” execution context is provided? Other examples might include “some” way to access a database; or “some” way to talk to a webservice.
Such “environmental” parameters are often what we want to swap out for testing, or change depending on deployment configuration – “canonical” use cases for dependency injection.
Finally, Alois Cochard also pointed me to a talk by Tomas Petricek on a calculus for environmental parameters (it’s just 20 minutes!). Time to study coeffects? :)
Reference: | In today’s post-OO world, is dependency injection still relevant? from our JCG partner Adam Warski at the Blog of Adam Warski blog. |