This content originally appeared on DEV Community and was authored by Aymane Harmaz
As software developers we have multiple tools at our disposal so that we can build softwares, if we take the example of softwares built with Java, we have methods, and once we have a bunch of methods that are related we can group them together into classes, and these classes can be grouped in packages, and these packages can be externalized in modules.
Software Architecture is all about how all these tools are linked and are having relationships between each other.
In this article we are going to cover the most commonly used software architecture styles including monolith and micro-services, then we will introduce another style that is considered as a middle ground between the 2 styles and that is called modulith (modular monolith)
What is a monolithic application
In a monolithic style, there is single code base and everything is packaged and deployed as a single unit,
The data used by a monolithic application is stored inside a single database.
For small to medium-sized applications, that handle moderate data volumes, this architecture style remains very practical and efficient and Here are some of its advantages :
Ease of development : developers can easily understand the flow of the processes of the application
Simple to refactor and debug : with modern IDEs, these 2 tasks became easy and straight forward
Low latency : calls are between functions belonging to the same process and there is no network overhead
The problems with monoliths starts when the application grows in terms of responsibilities, libraries, and data volumes that it should handle, in these scenarios we should deal with :
Inefficient scaling : if a single parts of the application gets heavy traffic, the only option to deal with this is to scale the whole application with all its parts
Complicated team parallelization : with this architecture style, it is very easy to make the business domains coupled between each other, and the teams working on the same code base may run into merge conflicts.
Poor resiliency : If one part of the application crashes, the whole application will be down.
What is a micro-services application
This is all about having independently deployed small softwares in terms of responsibility, running on separate processes and communicating between each other through network calls.
each software is supposed to have its own database.
This architecture style was introduced to overcome the challenges that were faced with monolithic applications when they get bigger. here are some of its advantages :
Selective scalability : It is possible to scale only the service that gets hot in terms of load and not the entire system.
Clear context boundaries : each service should focus on a specific business domain and have a clear responsibility, this separation makes the maintainability much easier.
Fault tolerance and enhanced resiliency : If one service goes down, the other services can still continue serving received requests.
Micro-services does not offer those benefits for free, they also have downsides :
Increased Latency : communication between services involves network calls, and who says network, says potentially unreliability, latency, timeouts
Additional development complexity : with this style developers have to provide additional efforts for dealing with service discovery, data consistency and distributed logging / tracing / monitoring
Is micro-services style better than monolithic style ?
Most of the time we feel like we have a negative view on the monolith, and on the other hand we have a very good impression on micro-services and this is generally because micro-services have been pushed by companies that are very dominent in the tech industry, However, this perspective is quite subjective.
Monoliths should be seen as very useful at the low level where we will have a small team producing simple applications and being very productive on that without any of the overhead of micro-services
Modular Monolith (Modulith) : A middle ground
There is another architecture style other than monolith and micro-services, it is called modular monolith (modulith) and it focuses on breaking an app into business modules, those modules are all in the same code base so it is still a monolith, however their source codes are not tangled up with, the modules are isolated from each other, and the communication between them happens using APIs or events.
When talking about modules in modular monolith, we are referring to business modules not to java modules or build tools modules, those are completely different concepts
What is interesting about that style is that you can benefit from advantages of both monoliths and micro-services :
- The structure of the application is clear making it easier for developers to understand the flows.
- The source code is in one place, and this point can be used to benefit from IDEs refactoring and debugging features
- There is no network overhead, meaning low latency.
- There is clear context boundaries between the modules
The things that you do not get from micro-services benefits are :
- Fault Tolerance (Resiliency)
- Selective Scalability
How to do moduliths in Java
In Java, there are three main approaches for implementing moduliths :
Package per business module
Build tool module per business module (Using Maven or Gradle)
Java module per business module (JPMS)
Modularization using packages
This is the simplest and most common approach. The application remains a single traditional Java module, and each business module is placed in a dedicated Java package (e.g., com.myapp.orders, com.myapp.billing, etc.).
This approach is simple to implement, however it does not enforce strict boundaries between the business modules neither at compile-time nor at runtime, and in order to ensure a low coupling between the modules, efforts should be make at code reviews, without discipline the project can easily turns into a tangled monolith.
Modularization with Maven/Gradle multi-module projects
In this approach, each business module is split into its own Maven or Gradle project. Each module compiles to a separate JAR, and module dependencies are declared explicitly at the build configuration.
The good point of this approach is that compile-time isolation is enforced by the build tool, a module can only access the modules that it has as dependencies, this reduces the possibility to end up with a tangled monolith, however at runtime, all classes from all projects are available at all classes by reflection.
Modularization with JPMS
With JPMS (introduced in Java 9), each business module is placed in its own Java module, defined with a module-info.java file.
JPMS enforces isolation between contexts both at compile-time and at runtime, however it is a bit complex to integrate with modern framework like Spring Boot
In the next blog post I will introduce Spring Modulith a project that was introduced to help us building modular monolith in a simple and robust way using package modularization.
This content originally appeared on DEV Community and was authored by Aymane Harmaz