Friday, July 03, 2009

OSGi Complexity?

Functional decomposition and modularization of applications is a popular topic. Instead of building singular, monolithic applications, we can build little Bundles or Modules that represent business functions and put them together Jigsaw Puzzle style.

The idea is that any overhead of such an approach is greatly offset by the benefits of encapulations - from software development to enterprise function placement.

For example, an application can "Shake bread crumbs with some chicken" and "Bake dinner", these two concerns are agnostic of each other, but can be composed into business flow "Shake 'N Bake". Or other business flows can be "Shake 'N Freez" or "Defrost 'N Bake".

Anytime we need to add/remove/change anything in the application we know exactly the place where the logic lives - as opposed to hunting throughout large codebases of potentially manyy application. It works great for testability, enables effective refactoring and short iterations.

OSGi has done a terrific job in championing of the modular concerns. But OSGi implementation can get quite complicated, for a reason that is not part of its core value proposition ...

Large portion of OSGi complexity comes from the assumption that the whole system is highly dynamic - OSGi bundles can come and go during the runtime and the system needs to constantly adapt. That is great and usefull for certain applications - I imagine telco world is a great example, but for most applications, this ability introduces too much complexity. Take the stallwart of OSGi examples Eclipse - the dynamic bundle update (called Plugins) is not used everyday and most new bundles force restart of the application after update, anyway.

Here's a solution: As soon as I replace "highly dynamic" with "fairly static" environment assumptions, it gets much simpler. I know that my application will either "shake" or "bake" sometime during application's runtime, anyway. I can live without introducing brand new business functions (say brand new operation "DeepFry") while the application is running. Now I could concentrate on the real issue at hand - modularity and functional decomposition, which is the core reason I arrived at OSGi gates.

Most of the OSGi complexity goes away with this change. It is a simple change, but it has far reaching impact. Bundles still have dynamic lifecycle, the major change is that all the Bundles are present from the start of the application and the application doesn't have to deal with Bundles exiting or brand new Bundles entering the application on a whim. Maybe this could be called SimpleOSGi.


Thanks to Ricky Bobby and Cal Naugton Jr. for inspiration in naming business functions examples. Dear Kraft Foods, please do not sue me.

3 comments:

Patrick Paulin said...

I agree completely. Especially when explaining OSGi to someone unfamiliar with it, the focus should be on modularity and the benefits of reuse, maintainability and ease of refactoring. Dynamic behavior is much less important.

--- Patrick

Neil Bartlett said...

I partially agree. Yes, modularity is the key benefit, but dynamic is an important part of the modularity picture.

What many people don't consider is that ALL applications must cope with dynamic addition/removal of functionality during start-up and (less importantly) during shutdown. If we code OSGi components robustly then they can tolerate being started in any order.

Consider a large application based on dependency injection framework such as Spring or PicoContainer. Each component (or "bean") has a dependency on some other components, and therefore it must be started after those. They in turn have a dependency on others and so on. Starting up an application like this becomes a problem of finding the "root nodes" and then working down the tree of dependencies. Unfortunately circular dependencies will screw everything up.

With well-written dynamic OSGi components, each component will detect for itself when its dependencies are present and will then start itself up. Parts of the application can start to be used before the whole has started. This approach ultimately leads to easier maintenance and faster start times -- and, as an added bonus, we get the ability to update functionality on the fly.

Unknown said...

Thanks for the comments! I agree that the dynamic aspect can be a great bonus. But the keyword is "well written component". The very dynamic nature of OSGi sometimes makes it complex to make sure that every component is "well written OSGi component".

At times I prefer sacrificing some of the dynamic nature and salvage a bit more simplicity, with focus on modularization.

I guess it mainly depends on the maturity of the whole system, as I could very well imagine adding more dynamic aspects to the system (add/remove), as it matures and modularization is second nature to all participants in the system.

As far as dependencies in the system, circular dependencies are a plaque. I think it mainly stems from modularization/design failure - too coarse or too fine components of the system are the culprit here. OSGi with its modularization focus is a great spot light for these issues.

In a more static system (with less dynamic benefits), I can create my dependency chart once, prior modules startup, and know that it won't change. If a circular dependency is discovered during the process, the system can abort. I can run this at every single build of the system.

Once I add dynamic features, I may have to revisit such approach :-).