Manage the marbles with Dagger Producers

David Rawson
4 min readAug 8, 2017

TL;DR in composing pipelines of asynchronous tasks we encounter the same kinds of problems as in composing graphs of dependencies.

The dependency injection framework Dagger 2 can manage both types of composition, leading to increased modularity.

Assume we have an app where the user enters in a player name and the app displays the last 20 games played by that player.

A simple app

The business logic for this involves two requests to our API:

  • a web request to obtain the id of the player from the name
  • a second web request to obtain the games played by that id
Two requests in the business logic

If we were using Dagger 2 to manage the dependencies in our app, the object graph might look something like this:

Fair enough. Assume we are using MVP. The presenter for the app might contain some code that looked like this if we were using RxJava:

We’ll translate that to Google Guava just for now:

The transformAsync here performs the same function as the flatMap in the RxJava example — it means “when you’ve finished retrieving the player, retrieve the games using the id of that player”.

Assume we are using MVP and our presenter will show the results of the request to our view. Our presenter has a dependency on both PlayerRepository and GamesRepository.

The initial naive presenter

This is no longer ideal since to test the class we would need to mock both PlayerRepository and GamesRepository.

If we wanted to tidy up the presenter we could extract a MainRequestHelper which exposes the final result for both requests, the ListenableFuture<Games>:

Naive extracted helper class

Now we’ve reduced the number of the dependencies for our presenter at the cost of maintenance of a helper class:

The resultant refactored MainPresenter

It feels good to consolidate all the requests into one service for the presenter to call. We’ve also learned that the “graph” of async tasks (retrievePlayer -> retrieveGames) is inextricably related to the object graph; it’s the DI framework that allows us to easily move dependencies from the presenter to the helper class.

At the same time, I’m ambivalent about the helper class — it’s taking up space. Taking up space, perhaps, in the same way a handwritten Factory or ServiceLocator would were we not using a DI framework.

This naturally leads us to ponder whether we could manage the graph of async tasks (production graph?) in the same way we manage the object graph for dependency injection.

In RxJava marble diagrams, the marble represents a single Observable. Often this is simply the single result of an asynchronous task (like a web request) and we are composing these in a very simple manner using flatMap or map. What if there was a way to manage the marbles in the same way we manage the dependencies for our app?

The marble diagram for flatMap (courtesy http://reactivex.io/documentation/operators/flatmap.html)

Enter Dagger Producers.

Just as we can model an object graph with the @Bindsand @Providesmethods in a Dagger 2 module, with Dagger Producers we can model a production graph with a @ProducerModule.

In the below example we model the pipeline: a futurePlayer is produced from a request to the and a future Games is produced from, having already obtained a Player, requesting their Games from the GamesRespository with their id.

We can now write a Component (injector) to expose the future Games we want:

and call the Component inside our presenter:

While this may seem like a lot of boilerplate for achieving very little, imagine a wild requirement change appears and a processed Statistics must be shown instead of the list of Games.

If we were updating the old HelperClass, we’d need to change the dependencies and the signature:

If instead were using Dagger Producers, we could simply add a new @Produces method to our module:

And we can expose the Future representing the processed Statistics in the Component:

Finally, the presenter can access the future Statistics through the Component:

If you’re excited by this kind of solution, bear in mind that bear in mind that Google Guava is a heavyweight library and if you have method counts or .jar sizes to worry about, you may have to use ProGuard aggressively.

For those working on an existing project using Guava and Dagger 2, the new FluentFuture API and Dagger Producers represent an exciting new frontier for code generation and DI.

--

--