A monolith isn’t necessarily a bad thing; it’s just a single, indivisible unit of deployment, and that can be a feature, not a bug.

Let’s see what this looks like in practice. Imagine a simple e-commerce platform. In a monolithic architecture, all the code for user authentication, product catalog, shopping cart, order processing, and payment gateway integration lives within a single codebase, deployed as one application.

+---------------------------------+
|          Monolith App           |
+---------------------------------+
| - User Auth Service             |
| - Product Catalog Service       |
| - Shopping Cart Service         |
| - Order Processing Service      |
| - Payment Gateway Integration   |
+---------------------------------+

When a user browses products, the product catalog service code runs within the same process as the user authentication code. When they add to cart, that code also runs in the same process. Communication between these "services" is just function calls within the same memory space. This is incredibly fast and simple.

Now, consider a microservices approach for the same e-commerce platform. Each of the previous "services" is now an independent application, deployed separately and communicating over a network.

+-----------------+   +-----------------+   +-----------------+
| User Auth Svc   |---| Product Catalog |---| Shopping Cart   |
+-----------------+   +-----------------+   +-----------------+
        |                     |                     |
        v                     v                     v
+-----------------+   +-----------------+   +-----------------+
| Order Processing|---| Payment Gateway |   | ... other svcs  |
+-----------------+   +-----------------+   +-----------------+

Communication here isn’t a simple function call. It’s typically done via HTTP requests (REST) or asynchronous messaging (like Kafka or RabbitMQ). This introduces network latency and complexity.

The fundamental problem microservices solve is managing complexity at scale. As a monolith grows, the codebase becomes harder to understand, test, and deploy. Different teams working on different features can step on each other’s toes. A bug in one part of the application could bring down the entire system. Microservices break down this large, complex application into smaller, independently deployable units. Each microservice focuses on a specific business capability, has its own codebase, and can be developed, deployed, and scaled independently by a small, dedicated team. This allows for faster iteration, technology diversity, and improved resilience – if one microservice fails, the others can often continue operating.

The choice between monolith and microservices hinges on your team’s maturity, the complexity of your domain, and your scaling needs. For small teams and simpler applications, a monolith offers faster initial development and simpler operations. As the application grows in complexity and the team scales, the benefits of microservices – independent deployments, technology choices, and fault isolation – begin to outweigh the operational overhead. You can even start with a well-structured monolith and strategically break out services as needed, a pattern often called the "monolith first" or "modular monolith" approach.

When you’re dealing with microservices, you’ll often find yourself needing to manage distributed transactions. The common approach is to use a saga pattern, where a sequence of local transactions is coordinated across multiple services. Each local transaction updates its own database and publishes an event to trigger the next step in the saga. If a step fails, compensating transactions are executed to roll back the changes made by previous steps. This avoids the need for traditional distributed two-phase commit protocols, which are notoriously difficult to implement and scale in a microservices environment, by trading ACID guarantees for eventual consistency and higher availability.

The next logical step after grappling with microservices is understanding how to effectively monitor and trace requests across these distributed services.

Want structured learning?

Take the full Microservices course →