Microservice architecture has gained popularity as applications grow in complexity. But what exactly are microservices, and when should you use them?
What Are Microservices?
A microservice is a small, independently deployable service that focuses on a single business capability. Instead of one large monolithic application, you break it into smaller services that communicate over APIs.
Microservices are not always the answer. For small teams and simple applications, a well-structured monolith is often the better choice.
Monolith vs Microservices
Here's a simple comparison of the two approaches:
Monolith:[Frontend] → [Single Backend] → [Single Database]Microservices:[Frontend] → [API Gateway] → [Auth Service] → [Auth DB]→ [User Service] → [User DB]→ [Blog Service] → [Blog DB]
Your First Microservice
Let's look at a minimal service using Elysia.js:
import { Elysia } from "elysia";const app = new Elysia().get("/health", () => ({ status: "ok" })).post("/api/v1/auth/login", async ({ body }) => {// Validate credentials// Generate JWTreturn { success: true, data: { token } };}).listen(3001);
Monolith → Microservice
Switch between the tabs below to see how a monolith login handler transforms into a microservice. Watch the tokens animate as the code evolves:
import { Elysia } from "elysia";import { db } from "./database";import { hash, verify } from "./crypto";import { sign } from "./jwt";const app = new Elysia().post("/api/login", async ({ body }) => {const { email, password } = body;// Query the shared databaseconst user = await db.user.findUnique({where: { email },});if (!user) {return { success: false, error: "Not found" };}const valid = await verify(password, user.password);if (!valid) {return { success: false, error: "Bad credentials" };}const token = await sign({ sub: user.id });return { success: true, data: { token } };}).listen(3000);
Notice the subtle differences: the import changes from a shared db to a dedicated prisma client, the route moves under a versioned API path, and the service runs on its own port.
Communication Patterns
Services need to talk to each other. The two main patterns are:
- Synchronous (REST/gRPC) — Service A calls Service B directly and waits for a response
- Asynchronous (Message Queue) — Service A publishes an event, Service B consumes it later
const user = await fetch("http://user-service:3002/api/v1/users/123");const data = await user.json();
Start with synchronous REST calls. Only introduce message queues when you have a clear need for async processing.
When to Use Microservices
- Your team has grown beyond 5-8 developers
- Different parts of the system need different scaling
- You need independent deployment cycles
- Different services need different tech stacks
Conclusion
Microservices are a powerful pattern, but they come with complexity. Start with a monolith, identify natural boundaries, and extract services only when the benefits outweigh the costs.

