Dependency injection in Vert.x with Dagger 2
There is not “The Vert.x way” to design software. But for sure, Vert.x is built for and propagates loosely coupling, lightness and scaling. The most central part of it is the event bus. In the common manner you will communicate over it with Json objects (or other protocols like Google Protobuf eg.) but usually you never transmit complex objects between verticles. This applies to the instantiation of verticles too. When you want to deploy a verticle on multiple event loops you have to deploy it via the full qualified class name (or via class reference).
This is a very nice approach in the scope of loose coupling and scaling since each of this verticle (and it’s handler) will be called within it’s own event loop thread, but this way you will lose the control of verticle instantiation completely. Therefore is not easy get a reference to shared, complex objects like a database connection within verticle instances. Clear, you can solve this with static references for example, but this has other well known drawbacks like bad testability.
Here i will show you a solid approach how you can get back the control of verticle instantiation via the Vert.x standard API and so you can parametrize your verticles and keep the Vert.x core features and paradigms without clashing with them.
Simple, it’s lightweight, enough configurable to solve the issues in that scope. Other dependency injection frameworks like CDI or Spring would solve them as well for sure, but they are not as lightweight enough for my demand (i worked many years with CDI). And last but not least dependency resolution on compile time will save development time, there is near no “magic”, well documented and the learning curve is steep because it’s a real small library.
Short note to Kotlin
Kotlin is the language of my choice for near anything. Specially the coroutines will ends the callback hell in Vert.x, and there are many more nice language features too. So i will use Kotlin for this illustration.
Enough only the words :)
Get back the control
The most central point we have to achieve is to get back the control of verticle instantiation so we can parametrize them afterwards. We will reach that with an own implementation of a verticle factory. This factory will then be responsible for instance creation of our “daggerized” verticles. This factory has to be registered in Vert.x to get on duty, but this will be shown later.
The prefix can easily overlooked in the snipped but ... He tells Vert.x to use this factory when we deploy a verticle with this prefix like …
As we can see the factory on it’s part do not instantiate the verticles itself, but rather get them from the provider (javax.inject.Provider) in the map. But where that map comes from? Here Dagger comes into play.
The verticleName method parameter value is exactly the same as the one used to call deployVerticle (inclusive prefix).
Daggerize the verticle factory
We parametrize the verticle factory instance with the verticle provider map. This map itself is merged from all Dagger modules they provide a map entry for the according types …
The “JvmSuppressWildcards” annotation is the only one Kotlin specific thing that’s needed to work with maps in Dagger. He tells Kotlin to not generate a wildcard type declaration for the map parameter = Map<String, ? extends Provider<Verticle>>.
Now we have a Dagger module that provides our Dagger verticle factory that receives on its part the verticle providers. But … what we still are missing are the verticle instance providers themselves.
A map of String / Verticle eg. can be injected as String / Provider<Verticle> to get instances on demand, so verticles are not instantiated too early or unnecessary. Please visit: https://google.github.io/dagger/multibindings
Provide a verticle for the factory
The DaggerVerticleFactoryModule above requires a map of String / verticle (javax.inject.Provider) pairs via Dagger. So we define them. First we implement the verticle …
Nothing special, expect the constructor parameters / properties. The rest is a common verticle that do register a consumer which get called on received messages and put a count of fruits into the store.
Now we provide this verticle to Dagger via it’s module…
The launch method starts a Kotlin coroutine, so we are not blocking
When the Dagger verticle factory request’s a verticle instance with javax.inject.Provider.get() the FruitVericleModule.provideFruitVerticle method is called for each new requested instance. Now we are missing the complex, shared object, the fruit store.
The @StringKey value corresponds with the suffix when we call deployVerticle, so in fact it’s free choosable. In this example i used the full qualified name for clearness, but it’s a good idea to take more simple ones for real projects. This brings robustness for refactorings, when you move a verticle class from a package to another eg.
The store (shared object)
This is now the “corpus delicti” of this example. A shared, stateful object like a database connection.
And the corresponding Dagger module …
Many times this part could be a third party library. Please notice the @Singleton annotation. For this example the shared, complex object is a singleton, so it would be instantiated just once.
Additional verticle provider
To illustrate that’s that easy to provide additional “daggerized” verticle types to the application, we will add one. Again, first the verticle …
A verticle that just log’s the current state of the fruit store. And now the map of provided verticles becomes an additional entry via Dagger module …
That’s it. Dagger will do any further things for us that this verticle type is available when we declares this module on the Dagger component. From this point you can deploy a store print verticle with:
Vert.x goes Dagger
That we have the Vert.x instance available in Dagger, we go to implement a Vert.x Dagger module as well. You will see, the Dagger verticle factory will be registered too at this point, so we get a ready to use Vert.x instance from here.
You can also instantiate Vert.x outside of Dagger and parametrize then the module with it eg. So you get an easy testing ready Vert.x module.
So we have implemented and defined anything we need. The application now can look’s like …
I personally put all application specific code within same Kotlin file (in Java inner classes / interface eg.). This code deploys all verticles, 3 for fruit storing (one for each fruit) and a logging verticle. Then each 3 second’s an event will be send that a couple of each fruits will add to the store and each 4 second’s an event to log the fruit’s the store contains at the moment.
I think to explain this would blow up this blog too much. So please check out the example: https://gitlab.com/michel.werren/vertx-dagger-verticlefactory-example. This will give much more insights how nice testing with this approach is.
In my opinion Dagger would be proper technology to combine with Vert.x as long as we not use it to bypass any of the fundamental paradigms of Vert.x like no direct associations between verticles, or it’s better to say still use the event bus for any kind of communications.
So we should use DI only and really only to share complex and / or immutable objects, and never inject a verticle in another for example!
I think so we gain a cleaner architecture and avoid OO anti-patterns like static references for some problems and less boiler plate code. Further, with the flexible module combination of Dagger we can configure our Vert.x application in a very simple, comprehensible fashion for any environments, stages and situations, specially as we have inversion of control from now. And last but not least, the Dagger modules are reusable per se, so it’s just a little effort to vary the combination of them for testing eg.
Is that cool, or isn’t it? :)
After now near 3 years i did and do use Vert.x in company and personally project environment i decided to go with this as part of the stack.
This was my first blog and i hope it was informative and interesting. I am glad on responses of constructive criticism and of corse other positive comments as well ;)
To see in a runnable example you can clone: https://gitlab.com/michel.werren/vertx-dagger-verticlefactory-example