Avoiding Circular Dependencies

Posted Monday, June 09, 2008 3:45 PM by Nathan Zaugg

The term "circular dependency" may be foreign to some programmers (especially if you do Java as it is a pretty common practice).  However, anyone who has done some scripting for a referential database knows that you have to run scripts in a certain order.  Running scripts out of order causes errors when you run.  The interesting trick is that if you run that same incorrectly-ordered DDL script again and again you will eventually get it to run without errors.  If you were unaware of the order being incorrect and thought to yourself in that moment "Stupid database!" then this blog post is for you!

What is a circular dependency?

It is simply two libraries that use each other (either directly or indirectly) as shown below:

circular depencency 

Figure 1: Circular Dependency

 

 

image

Figure 2: Complex Circular Dependency

 

The complexity of a circular dependency may vary.  If you are using Visual Studio and have all of your projects loaded into a single solution AND you add Project References (Right click on project -> Add Reference -> Projects Tab -> {Project Name}) then the IDE will not allow you to create Circular Dependencies.  In fact, this is a good practice as Visual Studio will ensure the correct build order. 

Why are circular dependencies bad?

Just like our Database example above, a circular dependency makes it so you can not guarantee that your application has the latest code.  That is a big deal!  Here is why:

  1. I make changes to Application 1 (in Figure 2)
  2. I build my project, The changes I made in Application 1 may or may not have gotten into Application 2 (depending on build order).  It may have taken a copy of the compiled code that was left over from the last time I built.
  3. Application 2 depends on this new functionality to provide services to Application 3; This functionality will not work correctly with this build.
  4. Application 3 may or may not depend on these same services to provide back to Application 1

As you can see in this scenario, there is no such thing as a "correct" build order when there are these circular dependencies.  The only way you can arrive at the correct version of the code is to build it as many times as there are nodes in our circle.  That would mean for Figure 1 that we would need to build twice and three times for Figure 3.  Some of these dependencies can get really ugly!  Here is some actual code running in an actual company that I did analysis on some time ago using a tool called Structure 101.

image image image image

 

How do I fix circular dependencies?

There are some steps to take to solve even the most complex tangles!  They all involve refactoring your code though.

  1. Refactor common code into a "base" dependency; I usually call this "Common" (figure 3).  BEST SOLUTION
  2. Remove code that is unused.  In the tangles shown above many of them are using deprecated/unused code.
  3. Duplicate the sections of code used.  This should be seen as a last resort but given the choice between code duplication and circular dependencies, I take code duplication ever time!

 

image

Figure 3: Refactor a Common

 

Summary

There are two kinds of design concepts for nTier (and other types of architectures as well) called Logical Layout Design and Physical Layout Design.  The Logical Layout is simply that your software occupies the same project/package but leverage different classes.  In contrast Physical Layout Design forces each tier to be separated into different Projects/Packages.  So long as we are careful to manage the dependencies between these packages from the start this is the preferable way to code.  While the logical layout does not suffer from the dependency problem eventually you may wish to break these classes apart and find that there are a lot of inner-dependency that should not exist simply because they occupied the same project.  Remember to keep it clean!

Comments

# Visual NDepend Blows My Socks Off!

Thursday, February 26, 2009 5:10 PM by Nates Stuff

I’m not easily impressed by even the best of software.  There is usually some little thing that

# re: Avoiding Circular Dependencies

Tuesday, May 26, 2009 3:59 PM by Alejandro Afonso Spinola

Would you consider the Business Objects in the Common layer? I have some trouble using them because I am using them in the BLL but I also want to receive them in DAL... What do you suggest? Thanks in advance

Excellent post

# re: Avoiding Circular Dependencies

Wednesday, September 09, 2009 10:57 AM by Nancy Grigas

I get it now !  Thanks !

# How to refactor or fix circular reference in this case? java – Best Java Answer

Pingback from  How to refactor or fix circular reference in this case? java – Best Java Answer

# How to refactor or fix circular reference in this case? java – Best Java Answer

Pingback from  How to refactor or fix circular reference in this case? java – Best Java Answer

# What can I do about “ImportError: Cannot import name X” or “AttributeError: … (most likely due to a circular import)”? – Code D3

Pingback from  What can I do about “ImportError: Cannot import name X” or “AttributeError: … (most likely due to a circular import)”? – Code D3