In the latest release 6.3 of the CUBA platform, there are a few interesting new features coming up. One of the most interesting ones is the feature of application components. In this article, we will try to understand the use cases for them and make an example of how to use them.
From Big Balls of Mud and Majestic Monoliths
In recent years, there has been a lot of debate about how to decompose monoliths, mainly in terms of deployment. Microservices are an extreme variant of this approach. Other approaches appear more moderate compared to a real monolithic architecture like self-contained systems. Finally, there is the end of the spectrum that either unconsciously or consciously chooses to make a monolithic architecture (see @DHH’s blog post The Majestic Monolith for further information).
All of these approaches care about the outermost layer of a monolith, the deployment artifacts. Although it is the most important layer to think about to avoid (or not avoid) monolithic structures, there are other layers that are notable, as well.
The one that we want to take a look at right now concerns the components within one deployment artifact. They are at the code reuse level. Something that has been there for a very long time with something like Windows DLLs, Ruby Gems, or Jar files.
Reuse of Code: A Solved Problem?
So you may ask be wondering, what are application components then, and why could it not be solved with something like plain Jar files? Well, with something like plain Jar files, you can share a lot of code. But there are situations when you don’t just want a packet for class definitions but a slightly better integration with the actual ecosystem you are in with your application.
A very good example of this is the Grails plugin system. With a Grails plugin, there is an additional integration to the Grails framework compared to a plain Jar packet. In a Grails plugin, you can register required Spring beans for this plugin, create Domain classes and pre-define UIs for your application part that is encapsulated in the plugin. Mainly the difference is that since Grails is a full-stack framework it is a good idea to have a composition model that is full-stack as well.
CUBA application components are, from a 10,000-foot view, pretty much the same but just for CUBA applications. So let’s dive a little bit deeper into what we can do with this and what the use cases are.
Application Components as a Way to Decompose Your Monolith
As far as I understand, the idea of decomposition for CUBA application had already been in the framework under the name “base project,” but the docs were not really talking about it. In the 6.3 release, though, the idea has been rethought and generalized. This is at least what the term “application components” suggests.
There are three common use cases that come to my mind for this feature. They allow the end result either to share code in various degrees or customize it in a more structured manner. We’ll go through this uses cases right now.
1. Product Line Platform
An example of the first case would be something like creating a MappedSuperclass for reference data or for entities with temporal validities, or if you want to share certain UI scenarios like a wizard in a superclass or a helper class. However, in this first scenario, the main business logic remains in the applications that use this application component.
2. Component Composition
The next possible option would be useful if you want to create a deployment monolith, but within this monolith, there are several independent parts like HR, controlling, and order management within your ERP application. These parts could potentially be independently sold but should end within one war file where I just have to log in once and stay in this application for “creating a new employee” as well as “rejecting an order.” In this scenario, every part can be a CUBA application component and then there is one application that literally has no business code in it, but just includes these components.
3. Product Customizations
The third option is something like the people at Haulmont describe in their article, How to Develop a Highly Customizable Product. In this case, they have a base product like this taxi management solution which is the application component. Then there are customizations above the component that only adjust the main application very slightly. This might have different layers of customization as they did with Sherlock.
The three use cases differ mainly in the amount of code that is placed in the components and obviously the purpose of use. Of course, these patterns of component usage can be combined if required. E.g. a component composition can also have a product line platform for each (or a subset) of the components. Product customizations can additionally be part of this scenario in order to customize either the application as a whole or just certain business components.
Actually, there is a fourth use case that I will not really go through since it is currently not possible in CUBA, but is a very interesting one: share components in marketplaces. Grails has a central plugin mechanism that allows users to share plugins with the community. You can think of them as open source add-ons to the framework. But they can also be implemented as paid extensions for common business problems like it’s done with the CUBA application components: reports, fts, charts, and bpm. Currently, CUBA has only a distribution channel for its own commercial components, but probably it’s not a huge deal to get something like this going.
Application Components in Action
As an example of this, I created a GitHub repository (CUBA example: application components) that has different of the described use cases implemented.
- Appointments (component composition): An application component that is a subsystem for managing appointments.
- Project-management-platform (product line platform): An application component which provides certain base classes or common shared code
- Project-management-app: The application that has code for managing projects combined with the possibility to add appointments to projects and use shared code from the platform component.
First, we can take a look at the appointments component.
1. Component Composition: Appointments
When you clone the repository and open up the appointments subdirectory in Studio, it appears as a normal CUBA application. You can run it and what you will see is everything that this component contains. A CRUD UI for creating appointments together with reminders for this appointments. So nothing special about this one.
The only difference from a normal CUBA application lies in two things:
- The module prefix for this component has to be specified and changed from “app” to something like “company” in this case.
- The “App component descriptor” has to be created (see the corresponding Link in Project Properties).
I did this already for the appointments component - so you don’t have to do any of these steps for this example. But what you have to do is to get a compiled version of this component into your local maven repository. To do this run the Gradle install task. In Studio this is possible via the global search icon and then typing “install”. This is necessary because you downloaded the raw sources for this dependency. But when you want to use this in the upstream components or applications (like project-management-app), you need a compiled version that can be just pulled via maven dependency management.
Alternatively, I created a bash script install-components.sh that you can run in the root of the git repository via:
2. Product Line Platform: Project-management-platform
The next component is an example of an application component that falls in the first category: product line platform. Is will probably contain code that is reused across different CUBA applications or application components. Examples of code that could be shared with these types of components would be:
- Static layout files.
- Abstractions for UI screen patterns.
- Common services (like the SayHelloService in the project-management-platform component).
- MappedSuperclasses for entities.
For the third approach, I don’t have a running example. It would probably be something like extending existing functionality. In the case of the appointment component, it could be that the project-management-app would define the Appointment entity to add another property “priority.”
You should be able to imagine what you can do with this decomposition mechanism. For me, at least, coming mainly from the Grails world, it was one important missing piece of the puzzle and I’m very happy it is now included in the platform and easily accessible through Studio.