Monolith vs Microservices
The Monolith
A monolith is one application that contains every feature: products, cart, orders, users and payments live in one codebase, are built into one JAR, and usually share one database.
+------------------------------------------+
| shopease.jar (:8080) |
| |
| ProductController OrderController |
| ProductService --> OrderService | method calls
| UserService PaymentService | (in memory, fast)
+--------------------+---------------------+
|
shopease_db (one database, all tables)A monolith is not bad. It is simple to build, test, debug and deploy. Most successful products started as a monolith.
Side-by-Side Comparison
| Monolith | Microservices | |
|---|---|---|
| Codebase | One repository, one app | Many small apps (often one repo each) |
| Deployment | Deploy everything together | Deploy each service on its own |
| Scaling | Scale the whole app | Scale only the busy service |
| Database | Usually one shared database | One database per service |
| Calls between parts | In-memory method calls (nanoseconds) | Network calls (milliseconds, can fail) |
| Transactions | Simple ACID transactions | Distributed; needs Saga/events (advance level) |
| Technology | One stack | Each service can choose its own |
| Debugging | One log, one stack trace | Needs tracing across services |
| Team size fit | Small teams | Many teams working in parallel |
Example: The Cost of a Network Hop
Example
Placing one order touches the user, product, payment and order code. In a monolith those are method calls; in microservices each is a network call. This program counts the calls and adds a typical cost per call (fixed numbers, so the output is the same every run):
import java.util.*;
public class Main {
static int calls = 0;
static int latencyMicros = 0;
// In a monolith, a call is a plain method call (~0.01 microseconds, we count it as 0)
// Between services, a call goes over HTTP inside a data centre (~2 ms = 2000 microseconds)
static void call(String from, String to, boolean network) {
calls++;
int cost = network ? 2000 : 0;
latencyMicros += cost;
System.out.printf(" %-14s -> %-16s %s%n", from, to, network ? "HTTP +2 ms" : "method call");
}
static void placeOrder(boolean network) {
calls = 0;
latencyMicros = 0;
call("OrderService", "UserService", network); // is the user valid?
call("OrderService", "ProductService", network); // price and stock of item 1
call("OrderService", "ProductService", network); // price and stock of item 2
call("OrderService", "PaymentService", network); // charge the card
call("OrderService", "ProductService", network); // reduce stock
}
public static void main(String[] args) {
System.out.println("Monolith:");
placeOrder(false);
System.out.println(" calls = " + calls + ", network time = " + latencyMicros / 1000 + " ms");
System.out.println("Microservices:");
placeOrder(true);
System.out.println(" calls = " + calls + ", network time = " + latencyMicros / 1000 + " ms");
System.out.println("Each network call can also time out or fail.");
}
}Output
Monolith: OrderService -> UserService method call OrderService -> ProductService method call OrderService -> ProductService method call OrderService -> PaymentService method call OrderService -> ProductService method call calls = 5, network time = 0 ms Microservices: OrderService -> UserService HTTP +2 ms OrderService -> ProductService HTTP +2 ms OrderService -> ProductService HTTP +2 ms OrderService -> PaymentService HTTP +2 ms OrderService -> ProductService HTTP +2 ms calls = 5, network time = 10 ms Each network call can also time out or fail.
Tip: This is why a good split keeps things that talk to each other a lot inside the same service. A chatty design (dozens of calls per request) makes microservices slow.
Practice
Exercise: ShopEase gets 50,000 product page views per minute but only 300 orders per minute. In which architecture can you run 10 copies of the product code and only 2 copies of the order code? Why?
Show answer
Microservices. Product and Order are separate services, so each is scaled on its own:
product-service x 10 instances (heavy read traffic)
order-service x 2 instances (light traffic)
In a monolith you must run 10 copies of the WHOLE app, including order,
user and payment code that does not need the extra capacity.