Web Totals
Microservices Trong Production: Khi Nào Tách, Cách Tách, Tại Sao Hầu Hết Đội Làm Sai

Microservices Trong Production: Khi Nào Tách, Cách Tách, Tại Sao Hầu Hết Đội Làm Sai

Microservices Trong Production: Khi Nào Tách, Cách Tách, Tại Sao Hầu Hết Đội Làm Sai

#Hướng dẫn toàn diện về kiến trúc microservices — trade-offs thực tế, migration patterns, chiến lược giao tiếp, và sự thật phũ phàng về khi nào KHÔNG nên dùng


#Giới Thiệu

"Chúng tôi đang tách monolith thành microservices!"

Câu nói này đã đi trước một số quyết định kiến trúc tốt nhất tôi từng chứng kiến. Nó cũng đi trước một số thất bại thảm khốc nhất.

Một startup với 5 kỹ sư đã dành 3 tháng xây dựng kiến trúc microservices.

Kết quả: Chi phí infrastructure tăng gấp ba, deploy trở thành cơn ác mộng, và vận tốc feature giảm 60%.

Họ merge tất cả trở lại thành monolith.

Một scale-up với 40 kỹ sư giữ nguyên monolith.

Kết quả: Xung đột deploy hàng ngày, các team chặn nhau, releases phải phối hợp giữa các phòng ban, vận tốc ì ạch.

  • Cùng một pattern.
  • Giải pháp ngược nhau.
  • Cả hai đều đúng trong bối cảnh của họ.

Đây là hướng dẫn toàn diện về microservices trong production — không phải phiên bản hội thảo nơi mọi thứ hoàn hảo, mà là phiên bản thực tế với trade-offs, chi phí, và câu hỏi quan trọng: đội của bạn có thực sự cần điều này không?

Bạn sẽ học:

  • Tại sao microservices tồn tại (vấn đề tổ chức, không phải kỹ thuật)
  • Khi nào tách services (quy mô team quan trọng hơn traffic)
  • Cách phân rã monolith (business capabilities, không phải technical layers)
  • Service communication patterns (sync, async, hybrid)
  • API Gateway implementation (cánh cửa trước của services bạn)
  • Chi phí và lợi ích thực tế (infrastructure, complexity, autonomy)
  • Chiến lược migration (strangler fig pattern, rủi ro thấp)

Kết thúc bài này, bạn sẽ biết liệu microservices giải quyết vấn đề thực tế của bạn hay tạo ra vấn đề mới bạn không cần.

Bắt đầu với lý do chúng tồn tại.


#Phần 1: Vấn Đề Monolith (Và Khi Nó Không Phải Vấn Đề)

#Monolith Là Gì?

Monolith là một ứng dụng duy nhất chứa tất cả chức năng:

[Single Application]
├─ User Management
├─ Product Catalog
├─ Shopping Cart
├─ Order Processing
├─ Payment
├─ Inventory
├─ Shipping
├─ Notifications
└─ Analytics

Tất cả module chia sẻ:

  • Một codebase
  • Một database
  • Một deployment
  • Một process

Với team nhỏ, điều này hoàn hảo.

#Khi Nào Monolith Hoạt Động

Team 5 kỹ sư xây dựng nền tảng e-commerce.

Lợi ích họ trải nghiệm:

  • Đơn giản để phát triển (một codebase, dễ điều hướng)
  • Nhanh để ship (một deployment, 5 phút)
  • Dễ test (integration tests chạy in-process)
  • Infrastructure đơn giản (3 servers, một database)
  • Chi phí thấp ($200/tháng tổng cộng)

Vận tốc của họ:

  • Ship features hàng ngày.
  • Deploy 5 lần mỗi ngày.
  • Không có coordination overhead.

Tại sao nó hoạt động:

  • 5 người có thể phối hợp bằng lời nói.
  • Không có độ phức tạp tổ chức.

#Khi Nào Monolith Phá Vỡ

Cùng công ty, 2 năm sau.

Team phát triển lên 40 kỹ sư qua 8 team.

Các vấn đề xuất hiện:

Vấn đề 1: Nút thắt deploy

  • Team A muốn deploy một fix analytics nhỏ.
  • Team B đang giữa quá trình refactor checkout.
  • Team C có feature dang dở trong codebase.

Cuộc họp phối hợp deploy: Thứ Ba 2 PM.

  • Mọi người phải sẵn sàng.
  • Nếu một team chưa sẵn sàng, không ai deploy được.

Kết quả: Deploy 1 lần/tuần thay vì 5 lần/ngày.

Vấn đề 2: Xung đột code

  • 40 kỹ sư, một codebase.
  • Merge conflicts hàng ngày.
  • Pull requests bị chặn bởi thay đổi của team khác.
  • Code review backlog tính bằng ngày.

Vấn đề 3: Blast radius

  • Bug nhỏ trong analytics làm sập toàn bộ ứng dụng.
  • Checkout hỏng vì ai đó thay đổi shared utility function.

Mọi thay đổi ảnh hưởng đến tất cả mọi người.

Vấn đề 4: Lãng phí scaling

  • Chỉ checkout trải qua traffic spikes (giờ trưa, tối).
  • Nhưng scaling có nghĩa là scaling toàn bộ ứng dụng bao gồm analytics, user management, và mọi thứ khác.
  • 3 servers lúc rảnh → 12 servers lúc spike.
  • Chi phí: $600/tháng → $2.400/tháng.
  • Thực tế cần: Scale chỉ checkout (sẽ là $800/tháng).

Vấn đề 5: Technology lock-in

  • Toàn bộ ứng dụng trong Python.
  • Team muốn dùng Go cho payment service (hiệu suất tốt hơn, typing mạnh hơn).
  • Không thể.
  • Phải viết lại toàn bộ.

Thực tế tổ chức:

Với 40 kỹ sư, monolith trở thành nút thắt tổ chức, không phải kỹ thuật.


#Phần 2: Microservices — Giải Pháp Và Chi Phí Của Nó

#Microservices Là Gì?

Microservices tách monolith thành các service độc lập:

[API Gateway]
  
├─ User Service (Node.js, Team A)
├─ Product Service (Python, Team B)
├─ Cart Service (Go, Team C)
├─ Order Service (Java, Team D)
├─ Payment Service (Go, Team E)
├─ Inventory Service (Python, Team F)
├─ Shipping Service (Ruby, Team G)
└─ Notification Service (Node.js, Team H)

Mỗi service có:

  • Codebase riêng
  • Database riêng
  • Deployment riêng
  • Team riêng
  • Công nghệ tự chọn

#Lợi Ích (Metrics Thực Tế)

Cùng công ty 40 kỹ sư sau khi migrate lên microservices:

Tần suất deploy:

  • Trước: 1 deploy/tuần (nút thắt phối hợp)
  • Sau: 10 deploys/ngày/service (team độc lập)

Vận tốc phát triển:

  • Trước: 2 tuần trung bình cho feature (chờ team khác)
  • Sau: 3 ngày trung bình cho feature (tự chủ hoàn toàn)

Hiệu quả scaling:

  • Trước: Scale toàn bộ app ($2.400/tháng lúc spike)
  • Sau: Scale chỉ checkout service ($800/tháng lúc spike)
  • Tiết kiệm: $1.600/tháng trong giờ cao điểm

Tự chủ team:

  • Trước: Họp phối hợp, PR bị chặn, phối hợp deploy
  • Sau: Teams ship độc lập, sở hữu service end-to-end

Tự do công nghệ:

  • Trước: Mọi thứ trong Python (lock-in)
  • Sau: Mỗi service chọn công cụ tốt nhất (Go cho payment, Node.js cho real-time)

Cải thiện vận tốc: Nhanh hơn 3 lần.

#Chi Phí (Cũng Rất Thực)

Độ phức tạp infrastructure:

Tiêu chí Monolith Microservices
Application servers 3 16 (8 services × 2)
Databases 1 8
Redis cache 1 1
Message queue 0 1
API Gateway 0 1
Monitoring servers 0 3
Tổng servers 5 30

Tăng chi phí: $600/tháng → $1.200/tháng (+100%)

Độ phức tạp debug:

Trước (Monolith):
Error in logs  Stack trace  Exact line of code
Debug time: 10 phút

Sau (Microservices):
Error reported  Which service failed?
   Check API Gateway logs
   Check User Service logs
   Check Order Service logs
   Check message queue
   Check distributed tracing
   Find: Network timeout between services
Debug time: 2 giờ

Cần distributed tracing:

  • Tools như Jaeger, Zipkin.
  • Additional infrastructure.

Độ phức tạp test:

// Trước (Monolith) - một process, nhanh
test('checkout flow', async () => {
  const user = await createUser();
  const cart = await addToCart(user.id, productId);
  const order = await checkout(cart.id);
  expect(order.status).toBe('confirmed');
});
// 50ms, dễ debug

// Sau (Microservices) - 5 services, network calls
test('checkout flow', async () => {
  await startUserService();
  await startCartService();
  await startOrderService();
  await startPaymentService();
  await startInventoryService();

  const user = await userService.create();
  const cart = await cartService.add(user.id, productId);
  const order = await orderService.checkout(cart.id);
  expect(order.status).toBe('confirmed');
});
// 2 giây, flaky
javascript

Độ phức tạp vận hành:

  • Trước: Một deployment pipeline, một monitoring dashboard, một log file
  • Sau: 8 deployment pipelines, 8 dashboards, 8 log streams, 8 databases backup

Yêu cầu team:

  • Trước: 2 developers có thể chạy toàn bộ stack local.
  • Sau: Cần dedicated DevOps team (2–3 kỹ sư) chỉ cho infrastructure.

#Phần 3: Khi Nào Chọn Gì

#Quy Tắc Quy Mô Team

Đây là yếu tố quyết định quan trọng nhất:

< 10 Kỹ sư: Monolith

Tại sao:

  • Team nhỏ có thể phối hợp bằng lời nói
  • Không có nút thắt tổ chức
  • Chi phí độ phức tạp vượt quá lợi ích
  • Không thể có dedicated DevOps team

Ví dụ: Startup giai đoạn đầu, MVP development, side project.

10–30 Kỹ sư: Modular Monolith

Sự cân bằng tốt nhất:

[Single Deployment]
  
[Well-Defined Modules]
├─ User Module
   ├─ Clear API boundaries
   ├─ Own database tables (users, sessions)
   └─ Can be extracted later

├─ Product Module
   ├─ Clear API boundaries
   ├─ Own database tables (products, categories)
   └─ Can be extracted later

└─ Order Module
    ├─ Clear API boundaries
    ├─ Own database tables (orders, order_items)
    └─ Can be extracted later

Lợi ích:

  • Đơn giản của monolith deployment
  • Kỷ luật của microservices (clear boundaries)
  • Dễ dàng extract services sau
  • Teams có thể sở hữu modules

Implementation:

/app
  /modules
    /users
      - routes.py
      - models.py
      - service.py
    /products
      - routes.py
      - models.py
      - service.py
    /orders
      - routes.py
      - models.py
      - service.py

30+ Kỹ sư: Microservices

Khi độ phức tạp tổ chức biện minh cho độ phức tạp kỹ thuật:

  • Nhiều teams (5–8 teams)
  • Ranh giới business rõ ràng
  • Nhu cầu scaling khác nhau
  • Nút thắt phối hợp deploy
  • Có thể đầu tư DevOps

#Ma Trận Quyết Định

Chọn Microservices nếu TẤT CẢ đều đúng:

  • Team > 30 engineers
  • Nhiều teams độc lập
  • Ranh giới service rõ ràng
  • Các service có nhu cầu scaling khác nhau
  • Có thể có 2–3 DevOps engineers
  • Có chuyên môn distributed systems
  • Nút thắt phối hợp deploy là vấn đề hiện tại

Giữ Monolith nếu BẤT KỲ đúng:

  • Team < 10 engineers
  • MVP hoặc giai đoạn đầu
  • Ranh giới service chưa rõ
  • Ngân sách hạn chế
  • Không có khả năng DevOps
  • Ưu tiên deployment đơn giản

Chọn Modular Monolith nếu:

  • Team 10–30 engineers
  • Muốn lợi ích của cả hai
  • Cần giữ tùy chọn mở
  • Có thể tách sau

#Phần 4: Service Decomposition — Vẽ Ranh Giới

Nếu bạn đã quyết định tách, vẽ ranh giới ở đâu?

#Pattern 1: Phân Rã Theo Business Capability

Tách theo việc business làm, không phải cách software được cấu trúc.

Ví dụ E-commerce:

Business capabilities:

  • User Management: Đăng ký, xác thực, profiles
  • Product Catalog: Danh sách, tìm kiếm, categories, inventory
  • Shopping Experience: Cart, wishlist, recommendations
  • Order Fulfillment: Xử lý đơn hàng, theo dõi, trả hàng
  • Payment Processing: Giao dịch, hoàn tiền, billing

Mỗi capability = một service.

Tại sao hiệu quả:

  • Maps với ngôn ngữ business
  • Product managers hiểu được
  • Alignment team tự nhiên

Ví dụ service:

Order Service sở hữu:
- Tạo orders
- Trạng thái order
- Lịch sử order
- Hủy order

Dữ liệu  sở hữu:
- orders table
- order_items table
- order_events table

Những   KHÔNG sở hữu:
- User data (gi User Service)
- Product data (gi Product Service)
- Payment processing (gi Payment Service)

#Pattern 2: Phân Rã Theo Subdomain (Domain-Driven Design)

Xác định domains:

Core Domain: Lợi thế cạnh tranh của bạn

  • Product recommendations (công thức bí mật)
  • Pricing algorithms (logic kinh doanh)
  • Đầu tư mạnh. Giữ in-house.

Supporting Domain: Cần thiết nhưng không khác biệt hóa

  • Order processing (quan trọng nhưng tiêu chuẩn)
  • Inventory management (cần thiết)

Xây dựng nhưng đơn giản hơn.

Generic Domain: Chức năng hàng hóa

  • Payment processing (dùng Stripe)
  • Shipping (dùng FedEx/UPS APIs)
  • Email delivery (dùng SendGrid)

Dùng third-party services.

#Anti-Pattern: Đừng Tách Theo Technical Layers

SAI:

[Frontend Service]  Renders UI
  
[Business Logic Service]  All business rules
  
[Data Access Service]  Database CRUD
  
[Database]

Tại sao thất bại:

  • Mọi request chạm TẤT CẢ services
  • Không có tính độc lập
  • Thảm họa hiệu suất

Ví dụ flow:

User adds to cart
 Frontend Service (render form)
 Business Logic Service (validate)
 Data Access Service (save)
 Database

4 network calls cho operation đơn giản!

Latency cộng dồn: 10ms + 15ms + 20ms + 50ms = 95ms (vs 5ms trực tiếp)

#Strangler Fig Migration Pattern

Đừng viết lại tất cả cùng lúc. Dần dần extract services.

Tháng 1: Extract User Service

[Monolith]  Contains everything
  
[User Service]  Newly extracted

Monolith route user requests đến service mới:

@app.get("/users/{user_id}")
def get_user(user_id):
    return requests.get(f"http://user-service/users/{user_id}")
python

Tháng 3: Extract Product Service

[Monolith]  Shrinking
          
[User]   [Product]

Tháng 6: Extract Order Service

[Monolith]  Smaller
            
[User] [Product] [Order]

Tháng 12: Monolith gone (hoặc tối thiểu)

[API Gateway]
           
[User] [Product] [Order] [Payment]

Lợi ích:

  • Rủi ro thấp (từng bước)
  • Học khi làm
  • Có thể dừng bất kỳ lúc nào
  • Team thích nghi dần
  • Xác thực lợi ích trước khi cam kết hoàn toàn

#Phần 5: Giao Tiếp Giữa Các Services

Services cần nói chuyện với nhau.

Chọn sai pattern?

Tight coupling, cascading failures.

#Pattern 1: Synchronous (REST/HTTP)

// Order Service cần user email
async function createOrder(userId, items) {
  const userResponse = await fetch(`http://user-service/users/${userId}`);
  const user = await userResponse.json();

  return await db.orders.create({
    userId,
    userEmail: user.email,
    items,
  });
}
javascript

Ưu điểm:

  • Đơn giản để hiểu
  • Phản hồi ngay lập tức
  • Strong consistency
  • Dễ debug (stack traces hoạt động)

Nhược điểm:

  • Tight coupling (Order phụ thuộc User phải online)
  • Cascading failures (User down = Order down)
  • Độ trễ cao (network overhead)
  • Blocking tài nguyên (thread chờ)

Khi nào dùng:

  • Critical path operations (checkout, payment)
  • Cần phản hồi ngay
  • Dữ liệu phải current
  • User-facing requests

#Pattern 2: Asynchronous (Events/Message Queue)

// User Service - publish event
async function updateEmail(userId, newEmail) {
  await db.users.update(userId, { email: newEmail });

  await eventBus.publish('user.email.updated', {
    userId,
    newEmail,
    timestamp: Date.now(),
  });

  return { success: true };
}

// Order Service - subscribe event
eventBus.subscribe('user.email.updated', async (event) => {
  await db.orders.updateMany({ userId: event.userId }, { userEmail: event.newEmail });
  console.log(`Updated orders for user ${event.userId}`);
});
javascript

Ưu điểm:

  • Loose coupling (services độc lập)
  • Resilient (consumer down = messages được queue)
  • Hiệu suất tốt hơn (không chờ đợi)
  • Scale dễ dàng (thêm consumers)

Nhược điểm:

  • Eventual consistency (1–5 giây delay)
  • Debug phức tạp (distributed flow)
  • Thách thức về thứ tự message
  • Cần xử lý duplicate

Khi nào dùng:

  • Background processing
  • Nhiều consumers cần cùng dữ liệu
  • Eventual consistency chấp nhận được
  • Fan-out patterns

#Pattern 3: Hybrid Approach (Tốt Nhất Trong Thực Tế)

Kết hợp cả hai dựa trên yêu cầu:

// Checkout flow - synchronous cho critical path
async function checkout(cartId, userId) {
  // SYNCHRONOUS (critical path)
  // Phải thành công, phải tức thì
  const inventory = await inventoryService.reserve(cartId); // Wait
  const payment = await paymentService.charge(userId); // Wait

  if (!payment.success) {
    await inventoryService.release(cartId);
    throw new PaymentError();
  }

  const order = await createOrder({
    cartId,
    userId,
    paymentId: payment.id,
  });

  // ASYNCHRONOUS (non-critical)
  // Fire and forget, xử  trong background
  await eventBus.publish('order.created', {
    orderId: order.id,
    userId,
    items: order.items,
  });

  return order; // Return immediately
}

// Background consumers (async)
eventBus.subscribe('order.created', async (event) => {
  await sendConfirmationEmail(event);
  await trackPurchase(event);
  await createPickingList(event);
});
javascript

Critical path nhanh (200ms). Non-critical tasks xảy ra bất đồng bộ.


#Phần 6: API Gateway — Cánh Cửa Trước

API Gateway nằm giữa clients và services.

#Tại Sao Cần API Gateway?

Không có Gateway:

// Mobile app cần user profile - 3 network calls
const user = await fetch('http://user-service/users/123');
const orders = await fetch('http://order-service/orders?userId=123');
const payments = await fetch('http://payment-service/methods?userId=123');
javascript

Có Gateway:

// Mobile app - 1 network call
const profile = await fetch('https://api.example.com/profile/123');
javascript

#Trách Nhiệm Của Gateway

1. Authentication

Xác thực một lần tại gateway:

async function authenticate(req, res, next) {
  const token = req.headers.authorization;
  try {
    const user = await verifyJWT(token);
    req.user = user;
    req.headers['X-User-Id'] = user.id;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Unauthorized' });
  }
}
javascript

Services không xử lý auth. Họ trust gateway.

2. Rate Limiting

Ngăn chặn abuse tập trung:

const rateLimiter = new RateLimiter({
  points: 100, // 100 requests
  duration: 60, // per 60 seconds
});

async function rateLimit(req, res, next) {
  const userId = req.user.id;
  try {
    await rateLimiter.consume(userId);
    next();
  } catch {
    res.status(429).json({
      error: 'Rate limit exceeded',
      retryAfter: 60,
    });
  }
}
javascript

3. Request Aggregation

Kết hợp nhiều service calls:

app.get('/profile/:userId', async (req, res) => {
  const { userId } = req.params;

  const [user, orders, payments] = await Promise.all([
    fetch(`http://user-service/users/${userId}`),
    fetch(`http://order-service/orders?userId=${userId}`),
    fetch(`http://payment-service/methods?userId=${userId}`),
  ]);

  res.json({
    user: await user.json(),
    recentOrders: await orders.json(),
    paymentMethods: await payments.json(),
  });
});
javascript

Client gọi 1 lần. Gateway gọi 3 lần (nhanh hơn, internal network).

4. Circuit Breaking

Bảo vệ khỏi cascading failures:

const CircuitBreaker = require('opossum');

const userServiceBreaker = new CircuitBreaker(
  async (userId) => {
    return await fetch(`http://user-service/users/${userId}`);
  },
  {
    timeout: 3000, // 3 giây timeout
    errorThresholdPercentage: 50, // 50% lỗi = mở circuit
    resetTimeout: 30000, // Thử lại sau 30 giây
  },
);

app.get('/users/:id', async (req, res) => {
  try {
    const user = await userServiceBreaker.fire(req.params.id);
    res.json(user);
  } catch (error) {
    // Fallback - trả về cached data
    res.json(getCachedUser(req.params.id));
  }
});
javascript

Khi User Service thất bại liên tục:

  • Circuit mở (ngừng gọi service đang lỗi)
  • Trả về cached/fallback data
  • Thử lại sau 30 giây
  • Tự động phục hồi

#Cấu Hình Gateway Production

upstream user_service {
    least_conn;
    server user-service-1:8001;
    server user-service-2:8001;
}

upstream order_service {
    least_conn;
    server order-service-1:8002;
    server order-service-2:8002;
}

server {
    listen 443 ssl;
    server_name api.example.com;

    ssl_certificate /etc/ssl/api.crt;
    ssl_certificate_key /etc/ssl/api.key;

    limit_req_zone $http_x_user_id zone=user_limit:10m rate=100r/m;

    location /api/users {
        limit_req zone=user_limit burst=20;
        proxy_pass http://user_service;
        proxy_set_header X-User-Id $http_x_user_id;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_connect_timeout 3s;
        proxy_read_timeout 10s;
    }

    location /api/orders {
        limit_req zone=user_limit burst=20;
        proxy_pass http://order_service;
        proxy_set_header X-User-Id $http_x_user_id;
    }
}
nginx

#Phần 7: Thách Thức Về Data Consistency

Microservices có database riêng biệt. Làm thế nào để giữ dữ liệu nhất quán?

#Vấn Đề

Monolith transaction (ACID):

BEGIN TRANSACTION;
    INSERT INTO orders (user_id, total) VALUES (123, 99.99);
    UPDATE inventory SET quantity = quantity - 1 WHERE product_id = 456;
    INSERT INTO payments (order_id, amount) VALUES (789, 99.99);
COMMIT;
sql
  • Tất cả hoặc không có gì.
  • Atomic.
  • Dễ dàng.

Microservices (database riêng biệt):

Order Service DB: orders table
Inventory Service DB: inventory table
Payment Service DB: payments table

Không thể dùng database transaction xuyên services!

#Giải Pháp: Saga Pattern

Saga = chuỗi các local transactions với các compensating actions.

Ví dụ: Order creation saga

// Bước 1: Order Service
const order = await db.orders.create({
  userId: 123,
  items: [{ productId: 456, quantity: 1 }],
  total: 99.99,
  status: 'pending',
});
await events.publish('order.created', { orderId: order.id });

// Bước 2: Inventory Service
events.on('order.created', async (event) => {
  try {
    await db.inventory.update({
      productId: 456,
      quantity: db.raw('quantity - 1'),
    });
    await events.publish('inventory.reserved', {
      orderId: event.orderId,
    });
  } catch (error) {
    await events.publish('inventory.failed', {
      orderId: event.orderId,
      reason: 'Out of stock',
    });
  }
});

// Bước 3: Payment Service
events.on('inventory.reserved', async (event) => {
  try {
    const payment = await stripeAPI.charge({
      orderId: event.orderId,
      amount: 99.99,
    });
    await events.publish('payment.completed', {
      orderId: event.orderId,
      paymentId: payment.id,
    });
  } catch (error) {
    await events.publish('payment.failed', {
      orderId: event.orderId,
    });
  }
});

// Bước 4: Xử  thành công
events.on('payment.completed', async (event) => {
  await db.orders.update(event.orderId, {
    status: 'confirmed',
  });
});

// Bước 5: Xử  thất bại (Compensating Transactions)
events.on('inventory.failed', async (event) => {
  await db.orders.update(event.orderId, {
    status: 'cancelled',
    cancellationReason: event.reason,
  });
});

events.on('payment.failed', async (event) => {
  // Hoàn lại inventory
  await events.publish('inventory.release', {
    orderId: event.orderId,
  });
  await db.orders.update(event.orderId, {
    status: 'cancelled',
    cancellationReason: 'Payment failed',
  });
});

events.on('inventory.release', async (event) => {
  await db.inventory.update({
    productId: 456,
    quantity: db.raw('quantity + 1'), // Hoàn lại
  });
});
javascript

Kết quả:

  • Eventual consistency (2–5 giây)
  • Mỗi service tự chủ
  • Failures được xử lý gracefully
  • Compensating transactions để rollback

Trade-off:

  • Không phải ACID.
  • Nhưng services vẫn độc lập.

#Phần 8: Câu Chuyện Migration Có Thật

#Bối Cảnh

  • SaaS platform.
  • 15 kỹ sư.
  • Ứng dụng Rails monolith.
  • Phát triển lên 40 kỹ sư.

Nỗi đau:

  • Phối hợp deploy (họp release hàng tuần)
  • Merge conflicts hàng ngày
  • Không thể scale checkout độc lập
  • Teams bị chặn bởi nhau

Quyết định: Migrate lên microservices.

#Phase 1: Lên Kế Hoạch (2 tuần)

Xác định services:

  1. User Service (authentication, profiles)
  2. Product Service (catalog, search)
  3. Order Service (checkout, order management)
  4. Payment Service (transactions, billing)
  5. Notification Service (email, SMS)

Phân bổ team:

  • 8 teams × 5 engineers
  • Mỗi team sở hữu 1–2 services

#Phase 2: Infrastructure (4 tuần)

Setup:

  • Kubernetes cluster
  • API Gateway (Kong)
  • Message queue (RabbitMQ)
  • Monitoring (Prometheus + Grafana)
  • Distributed tracing (Jaeger)
  • CI/CD pipelines mỗi service

Chi phí: 1 dedicated DevOps engineer full-time trong 4 tuần.

#Phase 3: Extract Service Đầu Tiên (6 tuần)

Tuần 1–2: Extract User Service

  • Copy user module code
  • Setup database mới
  • Deploy cùng với monolith
  • Route 1% traffic đến service mới

Tuần 3–4: Validate

  • Monitor error rates
  • So sánh responses (monolith vs service)
  • Fix discrepancies

Tuần 5–6: Tăng traffic

  • 1% → 10% → 50% → 100%
  • Xóa user module khỏi monolith

Kết quả:

  • Service đầu tiên được extract.
  • Team học được migration pattern.

#Phase 4: Extract Các Service Còn Lại (6 tháng)

Một service mỗi 4–6 tuần:

  • Tháng 1–2: User Service
  • Tháng 3: Product Service
  • Tháng 4–5: Order Service
  • Tháng 6: Payment Service
  • Tháng 7: Notification Service

Trạng thái cuối: 5 microservices + monolith nhỏ (admin panel).

#Kết Quả

Tiêu chí Trước (Monolith) Sau (Microservices)
Tần suất deploy 1/tuần 8 deploys/ngày/service
Lead time 2 tuần 3 ngày
Chi phí infrastructure $600/tháng $1.200/tháng (+100%)
Xung đột team Hàng ngày Không (team độc lập)
Scaling Toàn bộ app Theo service (tiết kiệm 60%)

Happiness survey: Cải thiện từ 6/10 lên 8.5/10.

Trade-offs được chấp nhận:

  • 2x chi phí infrastructure (đáng giá cho vận tốc team)
  • Độ phức tạp (quản lý được với tooling tốt)
  • DevOps investment (2 dedicated engineers)

Có đáng không?

  • Với 40 kỹ sư, hoàn toàn xứng đáng.
  • Với 15 kỹ sư? Sẽ đợi.

#Phần 9: Những Sai Lầm Thường Gặp Và Cách Tránh

#Sai Lầm 1: Microservices Quá Sớm

Chuyện gì đã xảy ra:

Startup 5 người xây dựng 8 microservices từ ngày đầu.

Kết quả:

  • 60% thời gian engineering cho infrastructure
  • 40% cho features
  • Đốt $50K trên AWS (over-engineered)
  • Ship 3 features trong 3 tháng (đáng lẽ 15)

Fix: Merge lại thành monolith. Ship 12 features tháng sau.

Bài học: Đừng giải quyết vấn đề tương lai. Giải quyết vấn đề hiện tại.

#Sai Lầm 2: Shared Database

Chuyện gì đã xảy ra:

Tách thành 5 services nhưng giữ shared database:

[User Service] ────┐
[Order Service] ───┼──→ [Shared Database]
[Product Service] ─┘

Vấn đề:

  • Schema changes ảnh hưởng tất cả services
  • Không thể scale databases độc lập
  • Tight coupling qua database
  • Mất tính độc lập

Fix: Database per service:

[User Service]  [Users DB]
[Order Service]  [Orders DB]
[Product Service]  [Products DB]

Giao tiếp qua APIs, không phải truy cập database trực tiếp.

#Sai Lầm 3: Nano-Services

Chuyện gì đã xảy ra:

Tách thành 50 services. Mỗi service có 1–2 endpoints.

Ví dụ:

  • GetUserName Service
  • GetUserEmail Service
  • GetUserAddress Service

Kết quả:

  • Network overhead giết chết hiệu suất
  • 50 deployment pipelines để quản lý
  • Debugging nightmare (trace qua 10 services)

Fix: Gộp lại thành 8 services. Mỗi service 5–10 endpoints.

Quy tắc:

  • Service nên được sở hữu bởi 2–3 engineers
  • Có thể viết lại trong 1–2 tuần

#Sai Lầm 4: Synchronous Chains

Chuyện gì đã xảy ra:

Mọi service gọi service tiếp theo đồng bộ:

Gateway  User Service  Order Service  Product Service  Inventory Service

Latency: 50ms + 50ms + 50ms + 50ms = 200ms (best case)

Nếu một service chậm (500ms)? Tổng: 700ms.

Fix: Hybrid approach. Critical path synchronous. Others asynchronous.

#Sai Lầm 5: Không API Versioning

Chuyện gì đã xảy ra:

Đổi format response API trong User Service.

Tất cả consuming services bị hỏng.

Trước: { "userId": "123", "name": "John" }

Sau (breaking change): { "user": { "id": "123", "fullName": "John Doe" } }

Fix: Version APIs từ ngày đầu:

GET /v1/users/123  (format  - vẫn hoạt đng)
GET /v2/users/123  (format mi)

Gradual migration. Không breaking changes.


#Tổng Kết

#Hành Trình Từ Monolith Đến Microservices

  • Bắt đầu với: Tại sao microservices tồn tại (vấn đề tổ chức)
  • Học được: Khi nào dùng (team size > 30)
  • Khám phá: Cách tách (business capabilities, strangler fig)
  • Triển khai: Communication patterns (sync, async, hybrid)
  • Xây dựng: API Gateway (routing, auth, aggregation)
  • Giải quyết: Data consistency (saga pattern)

#Sự Thật Phũ Phàng

Microservices không phải là:

  • Best practice
  • Luôn luôn tốt hơn
  • Cách "hiện đại"
  • Dành cho mọi công ty

Microservices :

  • Một sự đánh đổi
  • Complexity để đổi lấy scale
  • Đáng giá ở quy mô phù hợp (30+ engineers)
  • Overkill cho team nhỏ

#Bài Học Chính

  1. Quy mô team quyết định kiến trúc — Không phải traffic, không phải scale, mà là tổ chức team
  2. Bắt đầu với monolith, tách khi cần — Đừng over-engineer
  3. Modular monolith bị đánh giá thấp — Tốt nhất của cả hai thế giới cho 10–30 engineers
  4. Communication pattern quan trọng — Sync cho critical path, async cho background
  5. Chấp nhận eventual consistency — Không thể có ACID xuyên services
  6. Đầu tư vào tooling — Monitoring, tracing, deployment automation là thiết yếu
  7. Gradual migration thắng — Strangler fig pattern, rủi ro thấp

#Framework Quyết Định

Hãy tự hỏi:

  • Team tôi có > 30 engineers? (Nếu không, hãy giữ monolith)
  • Deploy conflicts có đang chặn chúng tôi? (Nút thắt tổ chức?)
  • Các phần khác nhau có cần scaling khác nhau? (Nhu cầu kỹ thuật?)
  • Chúng tôi có thể có 2–3 DevOps engineers? (Yêu cầu tài nguyên?)
  • Chúng tôi có chuyên môn distributed systems? (Kiểm tra năng lực?)

Nếu tất cả đều có: Cân nhắc microservices.

Nếu bất kỳ không có: Giữ monolith hoặc modular monolith.