Visualizing Dependency Tree from DI Container
So you are a C# developer. And you need to read the code and understand its structure. Maybe you’ve just joined the project, or it’s your own code you wrote 1 year ago. In any case, reading code is hard.
Luckily, some good thought was applied to this particular piece of code. It’s all broken down into small classes (they might even be SOLID!), and all the dependencies are injected via constructors. It looks like it’s your code indeed.
So, you figured out that the entry point for your current use case is the
class called ThingService
. It’s probably doing something with Thing
’s
and that’s what you need. The signature of the class constructor looks
like this:
public ThingService(
IGetThings readRepository,
ISaveThing saveRepository,
IParseAndValidateExcel<Thing, string> fileParser,
IThingChangeDetector thingChangeDetector,
IMap<Thing, ThingDTO> thingToDtoMapper,
IMap<int, ThingDTO, Thing> dtoToThingMapper)
OK, so we clearly have 6 dependencies here, and they are all interfaces.
We don’t know where those interfaces are implemented, but hey - we’ve got
the best tooling in the industry, so right click on IGetThings
, then
Go To Implementation
.
public DapperThingRepository(
ICRUDAdapter adapter,
IDatabaseConnectionFactory connectionFactory,
IMap<Thing, ThingRow> thingRowMapper,
IMap<ThingRow, Thing> thingMapper)
Now we know that we get Thing
from Dapper, so probably from a SQL database.
Let’s go one level deeper and check where those Mappers are implemented.
Right click, Go To Implementation
… But instead of navigating to another code
file you see
Find Symbol Result - 28 matches found
Oh, right, looks like we use IMap<T, U>
in more places. OK, we’ll find the
right one later, let’s first check the connection factory…
Right click, Go To Implementation
. Nah:
The symbol has no implementation
What? But the application works! Ah, IDatabaseConnectionFactory
comes
from an internal library, so most probably the implementation is also
inside that library.
Clearly, navigation doesn’t go that well so far.
Dependency Graph
When code reading gets tricky, usually an image can boost the understanding. The picture below actually shows the graph of class dependencies from our example:
Each node is a class, each arrow is a dependency - an interface injected into the constructor.
Just by looking at the picture for a minute of two you can start seeing some structure, and get at least the high-level opinion about the application complexity and class relationships.
Picture is also a great way of communication. Once you understand the structure, you can explain it to a colleague much easier with boxes and lines on the screen in addition to a plain wall of code.
You can enrich such picture with comments at the time of writing and leave it to your future self or anyone who would read the code in 2 years time.
But now the question is - what’s the easiest way to draw such dependency graph?
DI Container
The assumption of this post is that a dependency injection (DI) container of some kind is used in the project. If so, chances are that you can get such dependency graph from the container registrations.
My example is based on Simple Injector DI container which is used by ourselves. So, further on I will explain how to draw a dependency graph from Simple Injector container.
My guess is that any mature DI library will provide you with such possibility, mostly because the dependency graphs are built internally by any container during its normal operations.
Implementation
The implementation idea of dependency graph visualization is quite simple, as the biggest chunk of work is done by Simple Injector itself. Here are the steps:
-
Run all your DI registrations as you do in the actual application. This will initialize Container to the desired state.
-
Define which class should be the root of the dependency tree under study. You can refine later, but you need to start somewhere.
-
Call
GetRegistration
method of DI container for the selected type. An instance ofInstanceProducer
type is returned. -
Call
GetRelationships
method of the instance producer to retrieve all interface/class pairs that the given type depends on. Save each relation into your output list. -
Navigate through each dependency recursively to load further layers of the graph. Basically, do the depth-first search and save all found relations.
-
Convert the list of found relations into GraphViz textual graph description.
-
Use a tool like WebGraphviz do the actual visualization by converting text to picture.
There are several potential pitfalls on the way, like cyclic graphs, decorator registrations etc. To help you avoid those I’ve created a small library to automate steps 3 to 6 from the list above. See my SimpleInjector.Visualization github repo and let me know if you find it useful.
Conclusion
People are good at making sense of visual representations - use that skill to improve understanding and communication within your development team.
Dependency injection practice requires a lot of ceremony to set it up and running. Leverage this work for the best: check what kind of insights you can get from that setup. Dependency graph visualization is one example of such leverage, but there might be other gems in there.
Just keep searching!