Hero Image

gravity9 System Architecture Guide: Monolithic

 

05 Mar 2024 | Przemyslaw Serwicki

Architecture is a vital aspect of all systems, and applications, serving as a blueprint for engineers which defines behaviour and structure. There are numerous architectural patterns out there and they can be used on different levels of a system. In fact, a single system can use multiple architectural patterns!

As a digital consultancy, at gravity9 we have a rich history and heritage of development, picking the best architecture for the job. In this series of articles, we’ll introduce some of the most popular system architecture around. We’ll look at why they’re popular, where they’re useful and where they’re less useful.

What is a Monolith?

When beginning work on a new product, its final shape might not be fully understood. You need a simple architecture that can deliver without complication. When it comes to simplicity, monolith (or monolithic) architecture answers the call. Monolithic architecture defines a product built as a unified application, serving as the exclusive deployable and executable component. This singular system integrates all application subdomains within a solitary framework, encapsulating all business operations. Traditionally, this consolidated approach incorporates a single database and centralised data management. However, modern implementations may leverage multiple databases for specialised purposes or integration with external data sources.

An example of how Monolithic architecture might be presented:

Although this consolidation harnesses the simplicity previously discussed, it’s not without its own challenges and obstacles (for example, even a minor update requires re-deploying the entire system).

Below, we’ll look at both.

Modular Monolith.

Before we delve into the strengths and weaknesses of the monolithic architecture pattern, let’s take a closer look at a specific implementation known as the modular monolith. A modular monolith, while still adhering to the principles of a monolithic architecture, introduces a modular approach to system design. This means that the system remains a single deployment unit, maintaining the inherent simplicity of a monolith, but it also embraces modularity by organising functionalities into independent and interchangeable modules.

Key characteristics of a modular monolith include:

  • Independence and Interchangeability: Modules are designed to be independent entities, each responsible for specific functionalities. This independence is determined by the dependencies they have, allowing for seamless interchangeability where necessary.

  • Vertical Separation by Business Area: Each module encompasses everything necessary to provide a specific set of functionalities, promoting vertical separation by business area. This ensures a clear delineation of responsibilities and enhances maintainability.

  • Defined Interface / Contract: Modules in a modular monolith have well-defined interfaces or contracts, specifying how they interact with other modules or external components. This standardised approach facilitates integration and collaboration within the system and eliminates the need for specialised cross-module testing, as contracts are validated during the compilation phase. Overall, this streamlined approach enhances maintainability and reliability within the modular monolith.

 

An example layout of a modular monolith:

Each module operates with its own database, ensuring no cross-module transactions or queries without utilising module APIs. Well-defined contracts between modules streamline integration and communication, laying the foundation for easy extraction and deployment of individual modules as separate microservices. This approach provides the flexibility and adaptability necessary for the system to evolve towards a microservices architecture, if and when such a move is required.

The modular monolith strikes a balance between simplicity and flexibility, offering the benefits of a monolithic architecture while providing modularity for improved maintainability, scalability, and potential transition to microservices.

Let’s take a look at the advantages and disadvantages of Monolithic architecture in greater detail.

Monolithic Architecture Advantages.

As we quickly establish below; one of the key strengths of Monolithic architecture is its centralisation and simplicity.

  • Improved Development Team Collaboration: As Monolithic architecture involves a single comprehensive solution; numerous developers can easily collaborate on the same system.

  • Greater Cost Efficiency and Rapid Time-to-Market: A simplified release process accelerates development times and reduces initial costs. This makes monolithic architecture particularly attractive for startups and SMEs aiming to bring products to market quickly and cost effectively. By comparison, microservice architecture offers greater flexibility and scalability but is more complex and can be more costly to initially implement.

  • Deferred Architecture and Scalability Decisions: With a straightforward initial setup, Monolithic architecture allows you to postpone complex scalability decisions to deliver core functionality. Performance bottlenecks can be more easily addressed within the monolith and – if required – specific components can be refactored into separate services. This approach is particularly beneficial for new products, where usage patterns and scaling requirements may be uncertain.

  • Streamlined Testing Procedures: Testing monolithic solutions is relatively straightforward as there’s no communication between separate services, meaning testing can be centralised. If we compare that with popular alternatives like microservices architecture, we find testing can quickly become far more complex, where tests need to be set up for each independent service and integration testing is required to see how different services communicate with one another.

  • Effortless Debugging Process: The single-system structure of monolithic architecture simplifies the debugging process by consolidating log analysis to just one system. Unlike architectures composed of separate services generating high volumes of log data, dealing with a monolith means focusing on a singular source for log analysis.

  • Seamless System Communication: The absence of HTTP or message communication latency is a notable advantage within a single-system framework. Different segments of the system sharing the same codebase eliminates potential communication delays, streamlining interactions between various components.

Put simply; Monolithic architecture allows rapid development and deployment of small-scale and new products, getting core functionality built quickly and efficiently with focussed development and testing. Infrastructure requirements are cost effective, communication latency is minimal, and its implementation doesn’t require more advanced knowledge and investment in a complex microservice architecture (although it is quite possible to implement at a later date).

Monolithic Architecture Disadvantages.

Next, let’s look at some potential disadvantages of Monolithic Architecture:

  • Challenges in Team Coordination: A large team working on the same codebase can present challenges to ensure uninterrupted workflow. On the other hand, in a distributed system (such as microservice architecture) made up of numerous small components, large teams can be broken down to focus on different system parts allowing for more consistent workflow across the board.

  • Challenges in Rapid, Reliable Software Delivery: For even minimal changes, monolithic architecture mandates the testing and re-deployment of the entire system which can lead to a serious bottleneck in complex systems.

  • Scalability Challenges: Due to the unified nature of monolithic architecture, it becomes difficult to scale high-traffic systems. By contrast when scaling a multiple-service product, individual services can be independently scaled. As your product grows and increases in complexity, a monolithic approach can become a constraint.

  • Impact of Individual Errors on Application Availability: In substantial products, the process of adding and releasing new features can be difficult and potentially show-stopping if something goes wrong. Errors in a monolithic architecture can cascade, leading to an entire system crashing instead of the issue isolating itself to a specific feature.

Monolithic architecture is not ideal in all situations, as it really does “put all of your eggs in one basket” and can become cumbersome as your product grows (both adapting to shifting requirements and dealing with increasing traffic in certain areas) – furthermore – any error could become disastrous for the entire system.

How Does gravity9 Work with Monolithic Architecture?

gravity9 has implemented Monolithic architecture in numerous projects but has often gone a step further, following the modular monolith pattern. This allows modules that exhibit independence, vertical separation by business area, and defined interfaces/contracts. For the purposes of this article, our clients’ names are redacted.

Major Healthcare Provider:

Our client wanted a streamlined digital system to facilitate documentation sharing and communication between medical equipment suppliers and recipients (such as hospitals). The previous process was manual and decentralised, so it did not support information sharing between different recipients and suppliers.

Following thorough requirement gathering through event storming, gravity9 established the potential subdomains (and functionalities) within the product, which included: PDF generation, data search capabilities, data input forms, support for equipment variants and a notification system.

Our initial team was 3 Developers and 1 Product Owner and this, coupled with the client’s Microsoft Azure infrastructure requirement, made a modular monolithic approach preferable – primarily for its streamlined implementation of separated subdomains and lower cost and complexity requirements vs. alternatives like Microservices (which would be overkill at this point).

This approach enabled rapid project onboarding, quick setup and deployment processes, and clear subdomain division.

Technically, our modular monolith adhered to best practices, incorporating inter-modular communication via an asynchronous approach using queues and event handlers to reduce coupling.

The project was delivered to the client’s satisfaction, offering quicker and easier communication between supplier and recipient, and scope for further functionality enhancement to suit evolving needs.

Global Engineering Consultancy:

Our client wanted a new Project Planning System; an intuitive method for visualising and controlling projects budget and delivery time constraints. When gravity9 first engaged on the project, we encountered a legacy system of extensive, unnecessarily complex code (excessive endpoints, an overly extensive codebase for controllers / services, multiple repositories for service communication or a single database underpinning the system).

Having reviewed the challenges, we refactored and redesigned the codebase in order to move away from a messy legacy system to a more maintainable modular monolith architectural approach which would lay the foundation for future scalability.

We transitioned existing features from old code to the new by shifting from the conventional controllers / services synchronous approach to the Mediator pattern. This reduction in coupling simplified the identification of elements suitable for migration to subsequent modules. We also broke down those features into smaller, more manageable pieces of code that were more testable and maintainable.

When implementing new features (such as Tags) we adopted the Mediator as an in-memory service bus. New modules were introduced within dedicated folders to uphold the modular architecture. Each module could be characterised by four key principles:

  • Separate Databases for Enhanced Modularity: Separate databases for each module provides advantages in data isolation and security, performance optimisation, ease of maintenance and scalability.

  • Internal Functionality for Enhanced Security: By removing external endpoints and maintaining functionality internally, security was improved thanks to a reduced ‘attackable surface’ and limited external access.

  • Encapsulating Module Interaction with Contract Definitions: Contract definitions were made accessible exclusively outside the module, promoting clear boundaries, improving maintainability and predictability within the system. Changes to a module’s internal implementation – as a result – would not impact external components and as a result overall system stability is improved.

  • Implementing a Modular Startup System: Each module uses a single setup method with dependency injection (DI) which promotes encapsulation and further improves maintainability. Any resources required by the module are provided externally during setup rather than within the module itself, and the module operates through a well-defined interface and seamless integration is enabled with legacy code. This allows the Mediator to orchestrate communication in a decoupled manner.

Here’s an example of the approach in action, showing how the move to a modular monolith can transform convoluted legacy systems to a modular, flexible structure.

Is Monolithic Architecture Right For You?

We’ve taken a look at what Monolithic Architecture is – its greatest strengths and weaknesses – as well as some examples where it proved an invaluable choice for an established company’s product needs.

A monolith-based system is relatively quick to get up and running, and with low overhead costs for infrastructure and development. It can, however, develop growing pains that may inevitably make it a burden over time and encourage a move to other more scalable options like microservices.

When evaluating system architecture, it’s important to remain introspective and see what works best for you at any given moment in your product’s lifecycle. If you require a rapid start and ease of use, then a monolith or modular monolithic approach may be the answer – but if you’re at a point where it’s holding you back (or you know you’ll need to scale rapidly and soon) then perhaps microservices is a better choice.

Why not check out other articles in our architecture series to learn more about your options, or get in touch for a more tailored review of your options?