How to detect circular dependencies with Degraph


Posted by Steven

Recently, a good friend of mine blogged about Degraph. Degraph is a tool to visualize dependencies between packages. Not only are these dependencies shown in a graph, but the nodes of the graph can be expanded so that dependencies can be traced into packages. In this article, I want to document my first use of Degraph and how it helped me to discover a flaw in the architecture of my application SimFX.

One of my goals with SimFX is to build a nice architecture. That made a look at Degraph worthwhile because the structure of the packages are an important part of a software architecture. So I went to the Degraph wiki and had a look at how to use it. The application scans a folder with sources and generates a file with the graph information. I ran Degraph with the following configuration file:

output = simFX.graphml
classpath = ../../workspace/de.stevenschwenke.java.javafx.SimFX/target/classes

exclude = com.**
exclude = org.**
exclude = java.**
exclude = javafx.**

layer = {
   main.java.(*).**
}

Jens told me that it is important to let Degraph scan only the source OR the binary folder; that is what the classpath-line is about. If you scan both, there might be multiple classes in the resulting graph. I also excluded some  packages, so libraries like Apache Common would not appear in the graph. Finally, I predefined the first layer of my application. This makes the three main packages visible right at the start. Running Degraph with that configuration generates a simFX.graphml that can be viewed with yEd.

Letting Degraph analyze SimFX resulted in a surprise right at the start. I didn't expect to build in huge flaws at this early stage of development, but I did. My nice and clear top-level package structure already included a circular dependency, as you can see by the red arrows:

There is one dependency from the business package to the package ui that shouldn't be there. Let's zoom in to have a look (within yEd, click at the "+" at the packages. This is not shown here at these pictures).

World, the class that handles all business objects, notifies the user interface about changes that should be displayed at the screen. I placed it in the ui package because clearly, it had something to do with the user interface. I simply wasn't aware that the business object World would need a reference to the ui package. The solution is a simple one: I placed the UserInterface in the business package. That resulted in a far better architecture:

Now, there is a clear calling stream from left to right without any (red) circular dependencies. However, not all work is done ... let's have a look atthe packages ui and business:

Well, there's still room for improvement in the business package.

After learning dealing with yEd, I spend a couple of hours to trace and fix wrong dependencies in my application. Some dependencies didn't seem to be wrong at first. However, after thinking it through I allways concluded that the architecture was badly broken and that a refactoring had to be done. I would not have seen this without the easy to understand graphical representation of Degraph. And I absolutely like the approach of a zoomable graphical representation of call hierarchies. It helps me to explain my application on a 10.000-feet level while at the same time,  I'm able to zoom in whenever and wherever I want, right down to single classes.

It would be nice to have Degraph as an executable jar in some Maven repository. I could add my configuration file to the project and push it to github. This would allow other coders to watch my architecture grow. Also, this could be a first step to an automated architecture documentation of the code.Integrated into continuous integration, Degraph could be executed every night and the resulting file could be stored and analyzed. I imagine a timeline of package structures that allow watching a system develop.

Thanks for this tool, Jens!

Category: 
Share: