Quick reference microservices patterns. Communication, resilience, data management, API gateway, service discovery, dan best practices.
Prinsip-prinsip fundamental yang menjadi dasar arsitektur microservices.
| Principle | Description |
|---|---|
| Single Responsibility | One service = one business capability |
| Autonomous | Independent deployment & database |
| Loose Coupling | Minimal dependencies between services |
| Resilient | Failures don't cascade |
| Observable | Comprehensive logging & monitoring |
Situasi-situasi dimana microservices cocok digunakan atau tidak cocok.
Good for:
Bad for:
Berbagai cara komunikasi antar microservices.
Komunikasi real-time menggunakan HTTP calls langsung.
// Service A calls Service B
const response = await fetch('http://service-b/api/endpoint');
const data = await response.json();Kelebihan: Simple, immediate response Kekurangan: Tight coupling, blocking, cascading failures
// Service A publishes event
await messageQueue.publish('order.created', { orderId: '123' });
// Service B subscribes
messageQueue.subscribe('order.created', async (event) => {
await processOrder(event.orderId);
});Kelebihan: Loose coupling, fault-tolerant, async Kekurangan: Complex, eventual consistency
// Define proto
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
}
// Client
const response = await client.getUser({ id: '123' });Kelebihan: Fast, type-safe, bidirectional streaming Kekurangan: More setup, HTTP/2 required
class CircuitBreaker {
state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
failures = 0;
threshold = 5;
async call(fn: Function
async function retry(fn: Function, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn
async function withTimeout(fn: Function, ms = 5000) {
return Promise.race([
fn(),
new Promise((_, reject) =>
setTimeout(()
async function getUser(id: string) {
try {
return await fetchFromService(id);
} catch (error) {
return getCachedUser(id); // Fallback
}
}Client → API Gateway → [Services]
API Gateway:
- Routing
- Authentication
- Rate limiting
- Request/response transformation
- Load balancing// Express.js example
import { createProxyMiddleware } from 'http-proxy-middleware';
app.use('/api/users', createProxyMiddleware({
target: 'http://user-service:3000',
changeOrigin: true
}));
app.use('/api/orders'
const registry = {
'user-service': 'http://user-service:3000',
'order-service': 'http://order-service:3001'
};
const url = registry['user-service'];
const response = await fetch(`${urlapiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user
ports:
- port: 80
targetPort: 3000User Service → PostgreSQL
Order Service → MongoDB
Payment Service → PostgreSQLManfaat: Autonomy, tech diversity, independent scaling Challenges: No joins, data consistency
Distributed transactions:
async function orderSaga(order) {
try {
// Step 1: Create order
const orderId = await orderService.create(order);
try {
// Step 2: Process payment
await paymentService.
// Store events
const events = [
{ type: 'OrderCreated', orderId: '123', amount: 100 },
{ type: 'PaymentProcessed', orderId: '123' },
{ type: 'OrderShipped', orderId: '123' }
];
// Write model
class OrderCommandService {
async createOrder(data) {
const order = await this.db.insert(data);
await this.eventBus.publish('order.created', order);
// Generate trace ID
const traceId = generateUUID();
// Pass to downstream services
await fetch('http://service-b/api/endpoint', {
headers: {
'X-Trace-Id': traceId
}
});
// Log with trace ID
logger.info('Order created'
logger.info('User logged in', {
userId: user.id,
timestamp: new Date(),
service: 'auth-service',
traceId: req.headers['x-trace-id']
});app.get('/health', async (req, res) => {
const health = {
status: 'healthy',
timestamp: new Date(),
checks: {
database:
Blue (old) → 100% traffic
Green (new) → 0% traffic
Deploy Green → Test
Switch traffic:
Blue → 0%
Green → 100%v1.0 → 90% traffic
v2.0 → 10% traffic (canary)
If metrics good:
v1.0 → 50%
v2.0 → 50%
Then:
v1.0 → 0%
v2.0 → 100%3 instances:
1. Update instance 1 → wait
2. Update instance 2 → wait
3. Update instance 3 → done// API Gateway validates JWT
app.use(async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
try {
// Use mTLS or service tokens
const response = await fetch('http://service-b/api/endpoint', {
headers: {
'Authorization': `Bearer ${serviceToken}`,
'X-Service-Name': 'service-a'
}
});/api/v1/users)Distributed Monolith
Nanoservices
Shared Database
No API Gateway
Ignoring Failures
1. Identify module to extract
2. Build microservice alongside monolith
3. Route some traffic to new service
4. Gradually increase traffic
5. Remove code from monolith
6. Repeat for next moduleMonolith (100%) → Monolith (90%) + Service A (10%)
→ Monolith (50%) + Service A (50%)
→ Monolith (0%) + Service A (100%)| Factor | Monolith | Microservices |
|---|---|---|
| Team size | < 5 | > 10 |
| Complexity | Simple | Complex |
| Deployment frequency | Weeks/months | Daily/hourly |
| Scaling needs | Uniform | Variable |
| Tech diversity | Single stack | Multiple stacks |
| Time to market | Fast | Slower initially |
| Operational overhead | Low | High |
Happy architecting! 🏗️