Recently, I went to a three-day workshop on OSGi and Amdatu and I enjoyed the shift in thinking that is required when working with modular applications. Modularization is one of the core principles of the Microservices architecture. Working with OSGi/Amdatu presents, in some aspects, the same difficulties as working with Microservices, that’s why I will compare building a modular OSGi/Amdatu application with building a monolithic Spring Boot application.
This comparison aims to give a feeling of how it is to work with both technologies, it avoids in-depth details or discussions about use cases. A common denominator for these technologies are RESTful web-services, so that will constitute the ‘comparison case’.
I wrote this article for developers that don’t have experience with Microservices or with OSGi/Amdatu.
What is Spring boot?
According to its creators, ‘Spring boot takes an opinionated view of building production-ready Spring applications. It favours convention over configuration and is designed to get you up and running fast.’ It’s been around for quite a while and it has become quite popular. In simple terms, with Spring Boot, you can create a Spring web application without the hassle of wiring in the application the various Spring components by yourself. Spring Initialzr goes a step further and allows you to generate a working scaffold of an application by using a wizard in which you can select the components you need. There are plenty examples online of how to use Spring Boot, I won’t delve now into further details.
What is OSGi? What is Amdatu?
I remember that a couple of years ago I was making my first contact with OSGi. It wasn’t that widely used back then and that didn’t change that much nowadays (even if it evolved a lot since then).
The OSGi technology is ‘a set of specifications that define a dynamic component system for Java. These specifications enable a development model where applications are (dynamically) composed of many different (reusable) components.’ One of the popular OSGi implementations is Apache Felix. In simple terms, OGSi is for Apache Felix what SQL is for MySQL.
‘Amdatu is a set of open source components and tools to build modular applications in Java.’ Its purpose is to make modular development of enterprise/web/cloud applications easy. It achieves that by providing a comprehensive documentation, an OSGi application bootstrap wizard and various mostly used components. For simplicity, in this article, I’ll refer to OSGi & Amdatu as OSGi.
As I mentioned before, I want to show how developer productivity is affected by the choice of technology (modular versus monolith). I feel it’s important to restate that each architecture excels in particular use cases, my goal is to show, as objectively as possible, how it affects development.
In the case study, I started off from two simple RESTful Hello World web applications with in-memory persistence (one with OSGi, the modular application, and one with Spring Boot, the monolithic application) and I added persistence with mongoDB and refactored some interfaces.
A major limitation of this comparison is that it does not look at bigger applications. That is where things can get more interesting/messy and the differences between architectures might become more obvious. To counter this, I’ve made sure that the observations below are general enough and will apply to any application size.
Dependencies of modules
With the modular application (OSGi), as I keep writing more and more modules, I notice that the task of adding the correct dependencies repeats itself. A module might need some dependencies that another module does not and it is a best practice to only include what you use.
Pro: the classpath for that specific module will be clean and lean and the module itself will have quite a small footprint. Additionally, since adding new dependencies is a manual task, the developer becomes more aware of concerns of the module (I see myself stopping and thinking whether a dependency on apache.common.lang belongs to a JPA module)
Con: the creation of new modules requires a bit more work because all the dependencies need to be explicitly defined. You will have to know exactly in which dependency a class is, there’s no support from the IDE here.
With the monolithic application (Spring Boot), all added dependencies are available across the application.
Pro: Development goes fast when you don’t have to worry about dependencies (most modern IDEs allow you to add dependencies automatically when you first use one of the classes they define).
Con: The classpath gets cluttered fast. If you stop using a dependency, it will stay on the application classpath until you manually remove it. You might end in jar hell.
Thinking with modules
With the modular application (OSGi), the need to think about modules appears from the beginning, in the application design phase. Nothing stops you from writing a modular application that has big modules, or even just one module (transforming the application into a monolith).
Pro: you are encouraged to think about modules from the start. Writing new modules is easy and resembles the mindset needed to write Microservices. Writing a small application with OSGi seems, to me, to be the easiest way of trying out the idea of modularity (setting up the OSGi environment is a lot easier that setting up the infrastructure needed to run Microservices).
Con: At the beginning, the modular thinking feels awkward, because it’s new. A bit later, problems with the infrastructure that enables inter-modules communication, might make it seem that the modular approach is not worth it.
With the monolithic application (Spring Boot), developing is familiar and feels easy.
Pro: Monolithic applications have been around since the beginning and everyone is used to working with them.
Con: Modularity is not enforced by the system but thought out good architecture and good coding practices. Tight deadlines or lack of discipline in the development team can easily affect the application modularity.
Day-to-day development activities usually revolve around refactoring, changing implementations and adding new features. Let’s see how easy or hard it is to to each of them.
Changing the implementation of an existing interface: Currently, both applications have in-memory persistence. Let’s switch to mongoDB persistence (see tag mongo-persistence; OSGi, spring-boot). The switch to a different implementation of an interface is straightforward: for the modular application, a new module needs to be created and the bindings need to be redone so they use the new implementation; for the monolith, the old implementation is simply replaced with the new one.
Refactoring: On the existing persistence service interface, let’s add a new method and let’s change an existing method (see tag refactoring; OSGi, spring-boot). For both applications adding a new method is easy: update the interface and then use the IDE to fix the implementations. Same goes for changing the signature of a method because it’s fully automated by the IDE.
Adding a new feature: Let’s add a feature that reports the number of records stored in the database every minute(see tag added-new-feature; OSGi, spring-boot). This feature is not a critical feature (the application can function well without it). For both applications, adding the feature was as easy as ‘Changing the implementation of an existing interface’ (due to the low coupling of the feature with existing application). But I need to make an interesting observation with the occasion of adding this feature. This is another moment at which the modular application excels: this feature can be enabled/disabled easily, without any downtime of the application. The monolith requires a complete restart. I like this free functionality that OSGi provides out of the box. And I can imagine that the architecture of the modular application could be created in such a way that it would leverage this feature even more.
In this last section, I will make a couple of remarks that don’t fit in the other sections.
Extra layer: OSGi adds an extra infrastructure layer to the application. This is a low-level layer that normally you don’t have to care about it, but it will make the application more complex (for example, modules must be bound manually, the application will have extra OSGi specific configuration). This is also true for Microservices. They also come with an extra infrastructure layer (that is a lot more complex than in the case of OSGi and, currently, you have to design and build yourself).
Out-of-the-box lower downtime: In modular architectures, like OSGi and Microservices, applications are broken down into smaller pieces. These pieces contain less code and are independent of each other. This means that you don’t have to take the whole application down when you want to update one of its parts. And this is a feature that comes together with the architecture, you get it for free.
In this article, I gave a broad image of how the choice of architecture, modular or monolithic, affects development efforts. I’ll repeat here the most valuable ideas that came up:
- OSGi can be used, at least, as a learning tool, to get a feel of how it is to write modular applications (thinking about modules, lower downtimes). This experience can be handy when using Microservices
- Modular architectures have an extra layer of complexity compared to monolithic architectures. Some of the development efforts will go into maintaining this layer
Hope you enjoyed the article. I’d like to hear your thoughts on it (use the comment form below).