OSGi and cyclic dependencies
I'm currently working on the "bundlisation" of the JTheque Core. I choose to cut core into several bundles each one representing a service provided by the core.
I quickly have experencied problems with
I quickly realized that the decoupling of my various services was almost nil. I had a huge number of dependencies for each bundle and worse, I had a lot of cyclic dependencies, either direct or indirect.
For information, a direct cyclic dependency is a situation in which a bundle X depends on a bundle that depends on Y who is also depending itself of X. An indirect dependency cycle is the situation where X depends on Y, Y depends on Z and Z depends on X. I speak here of bundles, but it may relate to projects, classes or packages.
Note that the different techniques that I will present also apply for dependencies between components non-OSGi, the principles are exactly the same.
In the case of packages within the same application, even if we avoid this, it is possible to live with. However, in the case of bundle or projects, it is generally not even possible for the application builder. For me, the bundles are Maven modules. It is impossible to start the build of a bundle in a cycle since the builder must first bundle before the second and the second before the first, which is obviously not possible.
In addition to be very bad in a system build, cyclic dependencies between components are also a huge design problem. Indeed, we cannot work on a bundle without working on the bundle B and vice versa. In addition to this, the two components are very difficult to evolve in a healthy way.
The cyclic dependencies at the class level within a component does not pose real problems. It is always in the same component, classes can thus be linked in a cyclical way, even if it would still avoid it as soon as possible (it is not always).
To return to JTheque so I had to solve the problem of cyclic dependencies before advancing further. There are many techniques to do this. Here are the tracks that I explored to resolve these dependencies. Note that these techiques cannot only solve the problem of cyclic dependencies, but also improves the architecture of its application.
- Move the classes to the right location : In some cases, I had classes in a bundle when they had nothing to do here. In this case, it may suffice to move them to another bundle. Note that this case is the easiest to solve and it's unfortunately not the most common. In addition to this, in case the classes to move depends on another bundles, it is possible that this creates new cyclic dependencies. An example is the collection management module, which was contained in the bundle of graphical interfaces.
- Move functionalities : I also found a lot of features, typically methods within a bundle that was not at the right place. For example, the bundle "modules" provided methods for updates while a bundle "update" existed. It was enough to move these features to the bundle update to solve my cycle.
- Badly cutted features : Often, a feature, usually a method or a class, do too much things and causes dependencies that are not welcome here. Sometimes, thing again about the functionality and the role of each class / method may be enough to solve the problem. Note that this does not concern only the concepts of dependency, but is a basic principle of object-oriented design. In the case of JTheque I had for example a method allowing to select a collection that returns true if we could open the collection with the login / password entered false otherwise. But besides that, it displays an error in the collection view if there was a problem. But that was clearly not his role. I have updated this method so it does that test if we could open the collection or not and I implemented the display of the error in the Swing Action.
- Separate functionalities of bundle : It may happen that the bundle to do too much things or has several distinct aspects. In this case, you should immediately separate the bundle into several others with each one a clear responsibility. Often this will help solve a cyclic dependency, as fewer bundles have dependencies to each new bundles and each new bundles should have fewer dependencies to other bundles. But it can also introduce new cycles. In JTheque, I cut the modules "views" in 2 two bundles: views and ui. The views module contains the implementation of JTheque views and the "ui" module contains utility classes for creating views and generic Swing components that can be used elsewhere.
- Introduce callback system : A callback system is a simple listeners system that could solve dependencies. Rather than a bundle directly notify another bundle, the second register as the first listener. Thus the first does not need to know the second. It is immediately obvious that listeners are more powerful than they might initially appear. In the case of JTheque, I implemented this for selecting collections. In fact, the collections manager called directly the view that the collection had been chosen. Using a listener, it is much more flexible and collections manager automatically notifies all listeners. In addition to this, if I need later a second listener, I have nothing to change in the collections bundle.
- Separate specification and implementation : As it is not always possible to remove cyclic dependencies directly, we can bypass them by separating the specification part and the implementation part. This technique can sometimes be useful when it is really difficult to solve the cycle. It therefore separates a bundle in any specific part that is called from the outside and part of implementation which can then have dependencies outside. Note that this is clearly not often feasible in the state because it is not always possible to completely separate the implementation of the specification without cycles.
- Group to modules in one : In some cases, we realize that two modules are so intrinsically linked they are in fact only one module. In this case, the best solution is to group them in one bundle.
Well, now I have list the various techniques I used to resolve cyclic dependencies in bundles of JTheque Core. There are certainly many others, but it was enough for me.
I hope this will be useful to people trying to solve problems of cyclic dependencies.
CommentsComments powered by Disqus