Chapter 3 – The Two Facets of Modularity

by kirk knoernschild

There are two aspects to modularity – the runtime model and the development model. Today, emphasis is on the runtime model, with frameworks emerging that provide runtime support for modularity. But eventually, as the runtime model gains adoption, the importance of the development model will take center stage. The patterns in this book focus on an aspect of the development model we refer to as the design paradigm.

3.1 – The Runtime Model

The runtime model focuses on how we manage modular software systems at runtime. It includes support for the dynamic deployment of bundles, different versions of bundles, dependency resolution, enforcement of module boundaries, and the overall dynamism of the runtime environment. The runtime model is relatively mature. The defacto standard module system on the Java platform is OSGi, and many application platforms are leveraging OSGi to take advantage of the runtime capabilities resulting from increased modularity. To an extent, this allows the enterprise to realize the advantages of OSGi without knowing much about it. Faster application startup times and platform adaptability are two advantages organizations will realize as vendors bake OSGi into their products.

Until recently, however, many of the most widely used platforms encapsulated OSGi, and have chosen to keep it hidden from enterprise developers. Because of this, developers were unable to build applications that took advantage of the modular runtime.  However, this is changing as the platforms are exposing the virtues of OSGi and allowing developers to tap into its powerful runtime capabilities. No longer will we be subject to classpath hell and the monolithic applications that plague us today. Instead, modules will be able to discover other modules at runtime, and the artificial walls that separate applications will no longer exist.

Eventually, as support for modularity is baked into the platform, enterprise developers will be able to leverage the frameworks and technologies that aid them in developing more modular software systems. When this happens, the development model will become very relevant.

3.2 – The Development Model

The development model deals with how developers use the runtime framework to build their software applications. The development model can be further broken down into two categories – the programming model and the design paradigm. Each are important in helping developers build more modular software applications.

3.2.1 – The Programming Model

Taking advantage of the runtime module system demands that programmers have the ability to interact with the module system’s API. However, dependencies on the API result in heavyweight modules that are difficult to test and execute outside of the runtime system. To reduce the dependency on the module system API, frameworks and technologies must provide levels of abstraction so that our code doesn’t depend directly upon the framework’s API. Examples of these frameworks and technologies include the OSGi Blueprint Services, Spring Dynamic Modules, and iPojo.

Developers can leverage these frameworks and tools to tap into the capabilities of the runtime module system without worrying about the programming model. These frameworks encapsulate dependencies on the API so your code doesn’t have to talk directly to the API. The separation of concerns achieved through these frameworks ensures Java classes remain POJOs that aren’t dependent on the module system’s framework. This makes programming and testing much easier.

3.2.2 – The Design Paradigm

But the design paradigm must also be addressed. How does an organization create a more modular architecture? What is the right granularity for a module? How heavily dependent should modules be on each other? How do we minimize module dependencies? How do we break apart larger modules into a smaller set of more cohesive modules? When do we do this? These, among others, are the important architectural and design questions that surround modularity in the enterprise. This is the focus of the patterns in this book.

There are important lessons that can be learned from technology’s history. To examine this more deeply, let’s look at two different examples of technologies that prove the pending relevance of the module design paradigm – object-oriented (OO) programming and EJB.

3.2.2.1 – Object-Orientation

In the early 1990’s, OO was touted as the savior. Development teams would be able to build systems by composing reusable objects. This promised significantly reduced time-to-market and higher quality software. The promises were not met, and the benefits of object oriented technologies were never fully realized. There are a few reasons for this. Objects are too granular to serve as the foundation of reuse. Developments teams also had difficulty grasping and applying object oriented concepts correctly. Deep inheritance hierarchies laden with base classes rich in functionality contributed to poorly designed systems. In general, object oriented development was an early failure.

The runtime capabilities of object oriented programming languages provided features such as polymorphism and dynamic binding, and developers were able to easily understand many aspects of the programming model. Using dot notation to invoke methods and defining private member variables were trivial concepts. But it took a long time for us to understand how to design good programs using object oriented design techniques. What we accept today as simple truths surrounding object oriented design (”favor object composition over object inheritance” and “program to an interface, not an implementation”) were unknown, or at least a mystery to us, 15 years ago. Even now, we continue to learn new ways to design better software systems using object technology.

3.2.2.2 – Enterprise JavaBeans

Enterprise Java Beans (EJB), and especially entity beans, were presented as part of Java as a way to componentize business applications. The runtime capabilities of EJB were very alluring – transactions, persistence, security, transparency, etc. – and baked directly into the platform. Unfortunately, there were two glaring problems. The development model was complex and it was not well understood.

It was a number of years ago, but I recall vividly my first experience with EJB. I arrived on the team mid-way through their development effort. At that point, the team had more than 100 entity beans, the local development environment took more than four hours to start-up, and problems were rampant. I stuck around for three weeks before I voluntarily left the project. The project wound up getting canceled shortly thereafter. The problem with EJB was not the runtime model nor the programming model. The runtime model fulfilled many of its promises and the code generation wizards accompanying many tools made working with the programming model relatively straightforward. Frameworks even emerged that allowed developers to decouple their code from the EJB programming model and focus on designing simpler POJOs. However, EJB was a new technology and many developers lacked the design wisdom to leverage the technology effectively.

3.2.2.3 – Lessons Learned

Object oriented programming and EJB were two promising technologies that, arguably, failed to live up to their initial hype. The problems were not that object oriented programming languages, nor with the platform implementations of the EJB specification, but with how we chose to design our applications using these technologies. The most significant challenges were centered around the design paradigm.

These lessons serve as examples of the difficult road that lies ahead for OSGi, and specifically, modularity on the Java platform. If the design paradigm isn’t understood, with principles and patterns that guide how developers leverage the technology, the benefits of the runtime model will not be realized. I firmly believe that modularity is a key element of designing software systems with a flexible and adaptable architecture. There is a need for modularity on the Java platform, especially in developing large enterprise software systems. But if we do not begin to understand how to design more modular applications today, we’ll face significant challenges when platform support for modularity arrives.

3.3 – Modularity Today

It might appear that the runtime model and development model are inextricably linked. Untrue! While a runtime module systems offers additional facilities, such as enforcing module relationships, that help design more modular applications, it is not a requirement. We can still design modular software even in the absence of a runtime module system. In fact, given the significant advantages of modularity discussed in Chatpers 4, 5, and 6, it’s a good idea for development teams to start modularizing their applications right now, even if they aren’t deploying to a runtime module system. How can we do this?

We know a lot about the OSGi runtime model. It’s likely the platform we’re using is leveraging the runtime model internally, even if it’s not exposed to us right now. We know the unit of modularity is the JAR file, and we can start modularizing our applications today by emphasizing the JAR file as the unit of modularity. In Chapter 7, we’ll see firsthand how we can take a system and modularize it by emphasizing the JAR file as the unit of modularity, even in the absence of a runtime module system. The sample code for many of the patterns intentionally avoid using OSGi, though discussion of how the pattern can be implemented when using OSGi is included where it helps offer further clarification. In Chapter 5, Section 5.7, I illustrate visually the advantages of modularity.

Even when OSGi hits your platform, it’s pertinent to remember that the intent of modularizing our applications is not so that we are able to take advantage of OSGi. That’s simply a positive side affect. The real value is the modular architecture that results.