Software Monoliths


A monolith in software architecture a term often understood a some database or software that is large, complex and brittle. It is not modular and therefore shaping it to new business needs is problematic. The microservices need is based on this desire to have modular, changeable in parts and stable in others type software

However, in thinking of monoliths this way we miss a key element of what software engineers which prevents change in the first place leading to the presence of modular monoliths which are equally bad if not worse

Monoliths are not bad

Monolithic software is great because it is a single place where we can make a change, find all the documentation, manage the chaos and collaborate. When two teams needs to get into a partnership, they often find it optimal to use shared resources to prevent duplicating effort – there is a higher return on investment (ROI) if you build once and reuse, right? It is this process that leads to a shared library, database, service to be reused in various business contexts (software solutions) and over time we reap the benefits of shared collaboration

Maybe one team owns it and we all depend on that team to be experts at this shared service and there is a neat boundary of responsibility. But what if all teams share it? Imagine different teams have engineers who are subject matter experts at this one shared artefact (excel sheet, library, database etc) and they need to make changes at different times (releases) to parts of this shared thing. It is the impact to other teams that now becomes a concern teams need to make a change, slowing release of new features because you are sharing this one thing (or multiple) with other teams. Monolithic software is not bad until it becomes a bottleneck for change. In strategic DDD terms, this is a shared kernel

What is slowing us down?

So you have to ask, what is slowing us down? Is it the one database we have been sharing across product teams that is becoming more complex and harder to change as we release to new markets? Or perhaps it is the spreadsheet that worked great as a collaborative tool when we were a small startup but is now the bottleneck and needs to be split? or maybe it is the core banking or core insurance monolith that is hard to change because it is the single shared software across multiple business contexts?

The observations from the field has been that large enterprise organisations have been successful in delivering software rapidly in internal systems integration context but struggling with change and part

especially rapidly growing software-as-service organisations looking to scale to their existing core software systems to markets look to de-compose their monoliths. Breaking shared collaboration patterns through software de-composition has shown direct revenue growth and scale as it leads to speed up in delivery and improvements in time-to-market

How do you start to de-compose a monolith?

Decomposing or breaking a complex monolith has been talked about by many industry leaders, blog posts and books but mostly as a technical software engineering process. I believe this is as much a cultural change as an engineering one where we collaborate via boundaries instead of sharing the same resource

The entire organisation needs to be aligned to the outcome and see the value to the end customer before starting on the journey, otherwise old habits kick in and we build hacks around the core with some excuse perpetuating the misery for the organisation and customers

As teams look to decompose monolithic software into domain oriented, nimble, maintainable, changeable units – a good start is to be able to identify the areas of shared collaboration yourself

To do this we can classify the type of monolith they are dealing with so that we can articulate the challenges and solutions better

In this post, we explore 7 different types of monolithic software based on my experience. It is a bit of a read, so grab some coffee!

Monolithic Service Types

The 7 types monolithic practices and systems that I have seen and curious to hear your experience. Remember, these classifcations are based on collaboration across teams and impact to scale. For example, a single endpoint serving different usecases might be created by one integration team today but may need different teams or projects to manage

  1. Endpoint: Single shared endpoint
  2. Repository: Single shared repository
  3. Component: Single shared component
  4. Runtime: Single runtime
  5. Database: Single shared database (schema) for different service implementations for multiple contexts leading different teams and services depending on the same model
  6. Core System: Single core system with services for multiple contexts leading different teams consuming different or same services from a common system
  7. Distributed: Single library with logic for different contexts depended on by different services

Let us explore each in detail below

Endpoint Monolith

The endpoint monolith is when single endpoint handles multiple contexts. For example, a CRUD-style service with a SOAP endpoint or an anaemic REST API with HTTP methods (GET, PUT, POST, DELETE) acting on a single endpoint (e.g. /api/orders)

The key thing to notice here is that the endpoint is used for different business contexts and provides a large canonical schema with a super-set of attributes for all contexts. While a mashup of attributes presents its own problems with a bulky model, the key aspect of what makes this hard to change is the complexity in testing and collaboration

How: The endpoint monolith happens either due to an architectural style (SOA-style architecture) or gradually over time where a an endpoint for a specific context is expanded (attributes added) to show service reuse

Remediation: While the first team building the endpoint may follow a model-driven architecture with context specific resources, it is up to the future maintainers to ensure that the endpoint remains specific to its domain. Applying DDD practices vs pure integration reuse is recommended

Single endpoint servicing different contexts

Repository Monolith

A repository monolith as the name suggests is a single code repository with many different service implementations. These implementation can span multiple business contexts and teams

The key challenge with a repository monolith is how engineering teams collaborate and manage change especially when working in parallel streams of work

How: The repository monolith can be a conscious engineering decision or a result of a common source code that over time has modules or sections specific to different business contexts ( models, interfaces, services)

Remediation: Evaluating the benefits of extending vs creating a new repository is key. While we look to avoid duplication and maximize reuse, we need to ask if there are models, logic or functionality in the repository we are extending that is not a part of our domain

Single repository with different contexts

Component Monolith

A monolithic component contains implementation for different business contexts within the same service component. This is similar to the repository monolith where different features from different business contexts are part of a common source code. The key difference is that the source code is compiled into a single component (the repository monolith could bake different deployable components)

This type of architecture is an improvement over the common endpoint by providing a common root for the service with sub-contexts for specific implementations. For example, instead of a single /api/orders endpoint we handle different types with /api/orders/books, /api/orders/beverage, /api/orders/electronics

The key challenge with building a single component emerges when different teams need to change the different endpoints and in the runtime coupling it introduces when the component is deployed

How: We see the component monolith emerge when teams expand an existing domain service component for context it was not originally intended for. This may be due to speed up delivery through reuse of the component shell or underlying technical adapters/classes or even to avoid having to create a new deployment pipeline etc

The component monolith can also emerge in technologies where the deployment unit creates additional cost to operate. For example in platforms where vendors charge licensing costs per core and when cores are tied to components this can drive a trend to maximise what goes into the component

Remediation: Thinking about the components as domain services with specific business-aligned objectives is key. Avoiding this pattern is hard when the considerations are short-term and focus on local constraints such as operational costs

Thinking about long-term constrains such as maintenance, impact of runtime coupling etc are key. For example, consider the scenario when one implementation (e.g. /api/books) becomes resource intensive and causes the entire component to fail (shutdown or return 504s) would that be acceptable to the other business teams?

Single component with different contextual services

Runtime Monolith

A runtime monolith is a process with one or more deployable units within it. We see this with service-bus vendor technologies, especially on-premise ones where a single runtime process EAR or WAR would have multiple projects deployed to it (as zip, jar etc)

As the name suggests, this introduces coupling in independent components through the act of deploying to a common runtimes process. All process resources are shared, resulting in a lower operational cost but higher operational impact when one or more resources become constrained

How: A runtime monolith emerges either due to the constraints of the underlying platform (e.g. a single service-bus EAR) or due to deliberate choice to operate all services in a common runtime to reduce licensing costs

Remediation: A common runtime might suit a small team and budget to kick-off a services team but is not recommended as the number of services, contexts and demands grow due to the unpredictable use of resources and impact. Most teams work to scale horizontally with the same set of components still deployed to the same runtime, however if there is an opportunity to operate them independently as individually deployable units then that is the recommended approach

Single common runtime with different components

Database Monolith

A monolithic database is a single shared model across various independent components. This type of monolith hides the coupling on the surface unlike the previous examples, i.e. we see independent teams able to make changes to their services easily. However the key challenge with this pattern emerges when an existing shared database schema needs to change

How: A database monolith emerges when teams have direct access to a database and implement commands/queries on a particular schema without realising if this is shared across with other teams and contexts. Often openly available datasource connectors or even services exposing SQL-like construct (OData) can introduce accidental coupling across different domains and contexts

Remediation: Encapsulating a database with an API and events for change data capture can help decouple. If multiple teams are already tied to a single database then there are database monolith decomposition techniques which can help

Core System Monolith

A monolithic core system is a legacy system that is used in various contexts and is depended-on by various services for different business contexts. While key to the business operation of the enterprise, the core system becomes the a monolith in its own right with a slow pace of chance and hindering the release of new features as different teams depend on it to deliver new interfaces or changes. As the core systems become costly to customise and change over time due to use in multiple contexts and specialisation, the impact is felt outwards

How: A core system monolith evolves over time as a COTs product is used in multiple contexts. For example, core insurance system used in Home, Life, Motor etc. As various other systems are integrated to the core system, the reuse creates complexity regardless of the architectural style (point-to-point, service oriented, domain oriented etc) as the model in the back-end system services different contexts

Remediation: Working around a core system requires domain centric vision at an enterprise level especially within the architecture team. The tradeoff between a standard COTs product vs flexibility comes to the fore

Core System monolith

Distributed Monolith

A distributed monolith, unlike the earlier patterns, is spread across the components through a common library or shared logic. Essentially all the worse things about monolithic architecture and distributed architecture are at play as there are multiple component to observe and track while there is a hidden underlying monolith

How: A common library shared across components starts to contain context specific logic and is embedded within components. For example, a library to read from a database with a handling logic is reused across components and as teams add their context specific logic to it – the library expands

Remediation: Avoiding business logic in common libraries and keeping them context free

Summary

Monolithic software can couple contexts making software delivery slower, maintenance and operation harder. We saw 7 types of monolithic service patterns and how they are accidentally introduced through common software delivery processes that focus on systems and tactical optimisation vs business contexts and strategic value

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s