Monolithic applications are those with a single codebase, a single executable and a single process. They are often built in a single programming language and deployed to a single server.
When we start developing, monoliths are usually our first experience. They are easy to develop, test and deploy. However, as the application grows, the monolith starts to become a problem.
Scalability is one of the most important things to consider in software today. Being able to handle the growth of users and data dynamically is essential to a software product's success.
Monolithic systems are hard to scale. They are built as a single block of code, which means that to scale the application you need to replicate all of the code or scale the server vertically. That can be expensive and inefficient.
Vertical scalability is the ability to increase a server's capacity by adding more resources, such as CPU, RAM and disk. It is done to improve a server's performance.
Horizontal scalability is the ability to increase a system's capacity by adding more instances of it. It is done to improve a system's availability and reliability.
Microservices is a software architecture that solves this problem (but creates others). Instead of building a single monolithic application, you build several small, independent applications called microservices. Each microservice is responsible for a specific part of the application and communicates with the others.
Although microservices seem in theory to offer an ideal solution to scalability problems, in practice they bring challenges of their own. One of the main ones is managing the coupling between them.
In an ideal world, microservices should be independent and decoupled. That means one application should be able to work without depending on another. However, especially when migrating from monoliths to microservices, it is common for them to depend on each other, and that is what we call coupling.
There are two kinds of coupling in microservices: static coupling and dynamic coupling, and those are what we are going to talk about today.
Example of a microservices architecture

Components:
API Gateway:
Acts as the entry point for all external requests to the system.
Receives HTTP requests from clients and routes them to the appropriate internal services (such as ProductCatalogService, OrderService and UserService).
Handles access control, authentication, request routing and response aggregation.
ProductCatalogService:
Manages information about products for sale, such as descriptions, prices and availability.
Can query a discovery service such as Consul to locate other services it needs.
OrderService:
Creates purchase orders.
Interacts with other services to get product information (via ProductCatalogService) and user details (via UserService).
Handles orders in the MongoDB database and publishes order events to RabbitMQ, enabling asynchronous communication with other services.
UserService:
Manages user information, such as profiles, preferences and purchase history.
Lets other services query user information securely.
PaymentService:
Responsible for processing payment transactions.
Interacts with the MongoDB database to store information about financial transactions.
Publishes transaction events to RabbitMQ so that other services (such as NotificationService) are notified of financial events.
RabbitMQ:
Enables asynchronous messaging between services, making decoupled communication easier.
Delivers order and transaction events to interested parties.
NotificationService:
- Sends notifications to users based on events received through RabbitMQ, such as order status updates or payment confirmation messages.
MongoDB:
Stores data critical to the system, such as order, user and transaction information.
A NoSQL database like MongoDB is well suited to systems that need schema flexibility and efficient handling of unstructured data.
Consul:
Service used for service discovery, configuration and management.
Lets microservices dynamically discover the location of other services, enabling a highly flexible and resilient architecture.
Nginx:
- Can act as a reverse proxy or load balancer that distributes requests across several API Gateway instances, improving capacity and availability.
Static coupling
The components in green represent static coupling.
Static coupling happens when a microservice depends on another microservice at compile time or at the system's initial configuration.
Most of the time, static coupling is easy to spot. The most common forms are:
Code dependency: a microservice calls another one directly through a function call or an API.
Database dependency: a microservice accesses another application's database directly.
Configuration dependency: a microservice depends on specific settings of another microservice.
Another form of static coupling is when a microservice depends on a specific contract of another one. For example, a microservice expects another service to return a specific JSON object. If the contract changes, the microservice that depends on it may break, so the application that depends on the contract has to be updated.
Dynamic coupling
The components in blue represent dynamic coupling.
Dynamic coupling happens when a microservice depends on another one at runtime.
Most of the time, dynamic coupling is harder to spot. The most common forms are:
Messaging dependency: a microservice sends a message to a message bus and expects another service to respond.
Service discovery dependency: a microservice queries a discovery service to locate another one.
The interesting thing about dynamic coupling is that it lets microservices be more independent and decoupled. If a microservice is unavailable, the one that depends on it can keep working, even if in a limited way.
Conclusion
When designing microservices, it is important to consider the coupling between them. Static coupling and dynamic coupling are two forms of coupling that can be used in distributed systems.
There is no hard rule about which kind of coupling is better. In fact, most microservices systems will have a combination of static and dynamic coupling.
As an architect, you need to decide which kind of coupling makes the most sense for the problem you are solving right now. In some cases, static coupling can be simpler and more efficient. In others, dynamic coupling can be more flexible and resilient.
What matters is understanding the differences between static and dynamic coupling and knowing when to use each one.
Comments