Microservices คืออะไร? สอนออกแบบ Microservices Architecture ตั้งแต่ API Gateway จนถึง Event-Driven 2026

Microservices คืออะไร? สอนออกแบบ Microservices Architecture ตั้งแต่ API Gateway จนถึง Event-Driven 2026

TECH Architecture · 3400 คำ · อ่าน ~17 นาที

Monolith vs Microservices — แตกต่างกันอย่างไร?

Monolith Architecture คือการสร้างแอปพลิเคชันเป็นก้อนเดียว ทุกฟีเจอร์ (User Management, Order Processing, Payment, Notification) อยู่ใน Codebase เดียว Deploy เป็นหน่วยเดียว ใช้ Database ร่วมกัน ข้อดีคือเรียบง่าย พัฒนาเร็วในช่วงแรก ไม่ต้องจัดการ Network Communication ระหว่าง Services แต่เมื่อแอปโตขึ้น ปัญหาจะเริ่มปรากฏ ทีม 50 คนแก้ไขโค้ดใน Repository เดียว Merge Conflict ตลอด Deploy ครั้งหนึ่งต้อง Deploy ทุกฟีเจอร์พร้อมกัน Bug เล็กๆ ในส่วนหนึ่งอาจทำให้ทั้งระบบล่ม

Microservices Architecture คือการแบ่งแอปพลิเคชันออกเป็น Services เล็กๆ อิสระจากกัน แต่ละ Service รับผิดชอบ Business Domain เดียว (เช่น User Service, Order Service, Payment Service) มี Database ของตัวเอง Deploy แยกกันได้ สื่อสารกันผ่าน Network (HTTP, gRPC, Message Queue) แต่ละ Service อาจเขียนด้วยภาษาโปรแกรมที่ต่างกันก็ได้ ทีมเล็กๆ ดูแล Service ของตัวเองได้อย่างอิสระ

คุณสมบัติMonolithMicroservices
Codebaseเดียว ทุกอย่างอยู่ที่เดียวแยกตาม Service แต่ละตัว
Databaseใช้ร่วมกันแยกตาม Service (Database per Service)
DeploymentDeploy ทั้งก้อนพร้อมกันDeploy แต่ละ Service แยกกัน
ScalingScale ทั้งก้อน (Vertical)Scale เฉพาะ Service ที่ต้องการ
Technologyภาษาเดียว Framework เดียวเลือกภาษา/เทคโนโลยีได้อิสระ
Teamทีมใหญ่ทำงานบน Codebase เดียวทีมเล็กดูแล Service ของตัวเอง
Failure ImpactBug เดียว ล่มทั้งระบบService เดียวล่ม ส่วนอื่นยังทำงานได้
Complexityต่ำในช่วงแรก สูงเมื่อโตสูงตั้งแต่เริ่ม (Network, Ops, Data)

เมื่อไหร่ควรใช้ Microservices?

Microservices ไม่ใช่คำตอบสำหรับทุกปัญหา ในความเป็นจริง Microservices เพิ่ม Complexity มหาศาล ต้องจัดการ Distributed Systems, Network Latency, Data Consistency, Service Discovery, Deployment Pipeline ของทุก Service การตัดสินใจว่าจะใช้ Microservices หรือไม่ ควรพิจารณาจากปัจจัยเหล่านี้

ใช้ Microservices เมื่อ:
1. ทีมใหญ่ — มีนักพัฒนา 20+ คนที่ทำงานบน Codebase เดียวแล้วเกิด Conflict
2. ต้องการ Scale เฉพาะส่วน — เช่น Search Service ต้องการ 10 Instances แต่ Auth Service ต้องการแค่ 2
3. ต้องการ Deploy อิสระ — ปล่อยฟีเจอร์ใหม่ของ Payment ได้โดยไม่ต้อง Deploy Order
4. ต้องการ Fault Isolation — ถ้า Notification Service ล่ม Order ยังทำงานได้
5. ต้องการใช้เทคโนโลยีต่างกัน — ML Service ใช้ Python, API ใช้ Go, Frontend ใช้ Node.js

ไม่ควรใช้ Microservices เมื่อ:
1. ทีมเล็ก (1-5 คน) — Overhead ของ Microservices ไม่คุ้ม
2. ยังไม่เข้าใจ Domain ดีพอ — ถ้าแบ่ง Service ผิด แก้ทีหลังยาก
3. Startup ช่วง MVP — ใช้ Monolith ก่อน แล้วค่อย Migrate เมื่อจำเป็น

Core Principles — หลักการสำคัญ

Single Responsibility

แต่ละ Service ควรรับผิดชอบ Business Capability เดียว ไม่ใช่แค่ Technical Function เดียว ตัวอย่างเช่น "Order Service" รับผิดชอบทุกอย่างเกี่ยวกับ Order ตั้งแต่สร้าง อัพเดต ยกเลิก จนถึงประวัติ Order ไม่ใช่แค่ CRUD Table เดียว การแบ่ง Service ที่ดีควรใช้ Domain-Driven Design (DDD) เป็นแนวทาง แบ่งตาม Bounded Context

Decentralized Data Management

แต่ละ Service ต้องมี Database ของตัวเอง (Database per Service) ห้าม Service อื่นเข้าถึง Database โดยตรง ต้องสื่อสารผ่าน API เท่านั้น แม้จะดูเหมือนสิ้นเปลือง (มี Database หลายตัว) แต่เป็นสิ่งจำเป็นเพื่อให้แต่ละ Service เป็นอิสระจริงๆ ถ้าแชร์ Database Service หนึ่งเปลี่ยน Schema อาจทำให้ Service อื่นพัง

Independent Deployment

แต่ละ Service ต้อง Deploy ได้โดยไม่ต้อง Deploy Service อื่น มี CI/CD Pipeline ของตัวเอง มี Versioning ของตัวเอง ทีมสามารถปล่อยฟีเจอร์ใหม่ได้ทันทีโดยไม่ต้องรอทีมอื่น

Service Communication — การสื่อสารระหว่าง Services

Synchronous Communication — REST & gRPC

การสื่อสารแบบ Synchronous คือ Service ที่เรียกต้องรอ Response จาก Service ที่ถูกเรียก เหมาะสำหรับกรณีที่ต้องการ Response ทันที เช่น User เปิดหน้า Order Detail ต้อง Query ข้อมูลจาก Order Service และ Product Service พร้อมกัน

# REST API — ง่าย อ่านง่าย ใช้ HTTP/JSON
# Order Service เรียก Product Service
GET /api/products/12345 HTTP/1.1
Host: product-service:8080
Content-Type: application/json

# Response
{
  "id": 12345,
  "name": "iPhone 17 Pro",
  "price": 49900,
  "stock": 150
}

# gRPC — เร็วกว่า REST 2-10x ใช้ Protocol Buffers
# product.proto
syntax = "proto3";

service ProductService {
  rpc GetProduct (ProductRequest) returns (ProductResponse);
  rpc ListProducts (ListRequest) returns (stream ProductResponse);
}

message ProductRequest {
  int32 id = 1;
}

message ProductResponse {
  int32 id = 1;
  string name = 2;
  double price = 3;
  int32 stock = 4;
}
คุณสมบัติRESTgRPC
ProtocolHTTP/1.1 + JSONHTTP/2 + Protocol Buffers
Performanceช้ากว่า (JSON text)เร็วกว่า 2-10x (Binary)
Streamingไม่รองรับรองรับ (Bi-directional)
เรียนรู้ง่าย ทุกคนรู้จักต้องเรียน Proto files
เหมาะกับPublic API, เว็บInternal Service-to-Service

Asynchronous Communication — Message Queue

การสื่อสารแบบ Asynchronous คือ Service ส่ง Message ไปที่ Queue แล้วไม่ต้องรอ Response Service ปลายทางจะมาหยิบ Message ไปประมวลผลเมื่อพร้อม เหมาะสำหรับงานที่ไม่ต้องการ Response ทันที เช่น ส่ง Email, สร้าง Report, ประมวลผล Payment

# Producer — Order Service ส่ง Event เมื่อมี Order ใหม่
import json
from kafka import KafkaProducer

producer = KafkaProducer(
    bootstrap_servers=['kafka:9092'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

def create_order(order_data):
    # 1. บันทึก Order ลง Database
    order = db.orders.insert(order_data)

    # 2. ส่ง Event ไป Kafka
    producer.send('order-events', {
        'event': 'ORDER_CREATED',
        'order_id': order.id,
        'user_id': order.user_id,
        'total': order.total,
        'items': order.items,
        'timestamp': datetime.utcnow().isoformat()
    })

    return order

# Consumer — Notification Service รับ Event
from kafka import KafkaConsumer

consumer = KafkaConsumer(
    'order-events',
    bootstrap_servers=['kafka:9092'],
    group_id='notification-service',
    value_deserializer=lambda v: json.loads(v.decode('utf-8'))
)

for message in consumer:
    event = message.value
    if event['event'] == 'ORDER_CREATED':
        send_email(event['user_id'], f"Order #{event['order_id']} confirmed!")
        send_push_notification(event['user_id'], "Your order is being processed")

# Consumer — Inventory Service รับ Event เดียวกัน
for message in consumer:
    event = message.value
    if event['event'] == 'ORDER_CREATED':
        for item in event['items']:
            reduce_stock(item['product_id'], item['quantity'])

API Gateway — ประตูหน้าบ้าน

API Gateway คือ Single Entry Point สำหรับ Client ทุกตัว แทนที่ Client จะต้องรู้ว่า Service แต่ละตัวอยู่ที่ไหน (User Service อยู่ที่ port 8001, Order Service อยู่ที่ port 8002) Client ติดต่อ API Gateway ตัวเดียว แล้ว Gateway จะ Route Request ไปยัง Service ที่ถูกต้อง นอกจากนี้ API Gateway ยังทำหน้าที่ Authentication, Rate Limiting, Load Balancing, Request/Response Transformation, Caching และ Logging

# NGINX as API Gateway — nginx.conf
upstream user_service {
    server user-service:8001;
    server user-service:8002;  # Load Balancing
}

upstream order_service {
    server order-service:8003;
}

upstream product_service {
    server product-service:8004;
}

server {
    listen 80;
    server_name api.example.com;

    # Rate Limiting
    limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;

    location /api/users {
        limit_req zone=api burst=20;
        proxy_pass http://user_service;
        proxy_set_header X-Request-ID $request_id;
    }

    location /api/orders {
        limit_req zone=api burst=20;
        proxy_pass http://order_service;
    }

    location /api/products {
        limit_req zone=api burst=50;
        proxy_pass http://product_service;
        proxy_cache api_cache;
        proxy_cache_valid 200 5m;
    }
}
API Gatewayประเภทจุดเด่น
KongOpen-source / EnterprisePlugin system, Lua extensible, Kubernetes-native
NGINXOpen-sourceHigh performance, ใช้งานง่าย, Configuration-based
TraefikOpen-sourceAuto-discovery, Docker/K8s integration, Let's Encrypt
AWS API GatewayManagedServerless, Lambda integration, Pay-per-request

Service Discovery — ค้นหา Services อัตโนมัติ

ในระบบ Microservices Services ไม่ได้อยู่ที่ IP Address คงที่ อาจมีหลาย Instance อาจถูกย้าย Restart หรือ Scale ขึ้นลงตลอดเวลา Service Discovery คือกลไกที่ให้ Services ค้นหากันเองได้ โดยไม่ต้อง Hardcode IP Address

# Kubernetes DNS — Service Discovery อัตโนมัติ
# ทุก Service ใน Kubernetes ได้ DNS อัตโนมัติ
# Format: service-name.namespace.svc.cluster.local

# Order Service เรียก User Service
import requests

# ใน Kubernetes ใช้ DNS ได้เลย
response = requests.get("http://user-service.default.svc.cluster.local:8080/api/users/123")

# หรือย่อเป็น (ถ้าอยู่ Namespace เดียวกัน)
response = requests.get("http://user-service:8080/api/users/123")

# Consul — Service Discovery สำหรับ non-K8s
# Register Service
curl -X PUT http://consul:8500/v1/agent/service/register   -d '{
    "Name": "user-service",
    "Port": 8080,
    "Check": {
      "HTTP": "http://localhost:8080/health",
      "Interval": "10s"
    }
  }'

# Discover Service
curl http://consul:8500/v1/catalog/service/user-service

Event-Driven Architecture — สถาปัตยกรรมขับเคลื่อนด้วย Event

Event-Driven Architecture (EDA) คือรูปแบบสถาปัตยกรรมที่ Services สื่อสารกันผ่าน Events แทนการเรียก API โดยตรง เมื่อเกิดเหตุการณ์สำคัญ (Order Created, Payment Completed, User Registered) Service จะ Publish Event ไปที่ Event Bus (เช่น Kafka, RabbitMQ) แล้ว Services อื่นที่สนใจ Event นั้นจะ Subscribe และประมวลผล ข้อดีคือ Loose Coupling Services ไม่ต้องรู้จักกัน Producer ไม่รู้ด้วยซ้ำว่ามี Consumer กี่ตัว

# Event-Driven Flow: สั่งซื้อสินค้า
# 1. Order Service → Publish "ORDER_CREATED"
# 2. Payment Service → Subscribe → Process Payment → Publish "PAYMENT_COMPLETED"
# 3. Inventory Service → Subscribe → Reserve Stock → Publish "STOCK_RESERVED"
# 4. Shipping Service → Subscribe → Create Shipment → Publish "SHIPMENT_CREATED"
# 5. Notification Service → Subscribe → Send Email/SMS

# Event Schema — ควรมีโครงสร้างชัดเจน
{
  "event_id": "evt-abc-123",
  "event_type": "ORDER_CREATED",
  "aggregate_id": "order-789",
  "aggregate_type": "Order",
  "timestamp": "2026-04-08T10:30:00Z",
  "version": 1,
  "data": {
    "order_id": "order-789",
    "user_id": "user-456",
    "items": [
      {"product_id": "prod-123", "quantity": 2, "price": 990}
    ],
    "total": 1980
  },
  "metadata": {
    "correlation_id": "req-xyz-789",
    "source": "order-service",
    "user_agent": "Mozilla/5.0"
  }
}

Kafka vs RabbitMQ vs NATS

คุณสมบัติKafkaRabbitMQNATS
แนวคิดDistributed LogMessage BrokerCloud-native Messaging
Throughputสูงมาก (ล้าน msg/s)ปานกลาง (หมื่น msg/s)สูง (แสน msg/s)
Persistenceเก็บ Message ถาวรลบหลัง ConsumeOptional (JetStream)
Orderingรับประกันใน Partitionรับประกันใน Queueไม่รับประกัน
เหมาะกับEvent Sourcing, LogTask Queue, RPCReal-time, IoT

Patterns — รูปแบบสำคัญสำหรับ Microservices

Saga Pattern — จัดการ Distributed Transactions

Saga คือรูปแบบสำหรับจัดการ Transaction ที่ข้ามหลาย Services ในระบบ Monolith ใช้ Database Transaction ธรรมดา (BEGIN, COMMIT, ROLLBACK) แต่ใน Microservices แต่ละ Service มี Database แยก ไม่สามารถใช้ Transaction เดียวข้ามหลาย Database ได้ Saga แก้ปัญหานี้โดยแบ่ง Transaction ใหญ่เป็นหลาย Local Transactions ถ้าขั้นตอนใดล้มเหลว จะเรียก Compensating Transaction (Undo) ของขั้นตอนก่อนหน้า

# Saga: สั่งซื้อสินค้า (Choreography-based)
# Success Flow:
# 1. Order Service: Create Order (PENDING)
# 2. Payment Service: Charge Payment → Success
# 3. Inventory Service: Reserve Stock → Success
# 4. Shipping Service: Create Shipment → Success
# 5. Order Service: Update Status (CONFIRMED)

# Failure Flow (Payment failed):
# 1. Order Service: Create Order (PENDING) ✓
# 2. Payment Service: Charge Payment → FAILED ✗
# 3. Order Service: Cancel Order (CANCELLED) ← Compensating

# Failure Flow (Stock insufficient):
# 1. Order Service: Create Order (PENDING) ✓
# 2. Payment Service: Charge Payment → Success ✓
# 3. Inventory Service: Reserve Stock → FAILED ✗
# 4. Payment Service: Refund Payment ← Compensating
# 5. Order Service: Cancel Order (CANCELLED) ← Compensating

# Orchestrator-based Saga (ใช้ Central Coordinator)
class OrderSaga:
    def execute(self, order):
        try:
            payment = payment_service.charge(order.total)
            stock = inventory_service.reserve(order.items)
            shipment = shipping_service.create(order)
            order_service.confirm(order.id)
        except PaymentError:
            order_service.cancel(order.id)
            raise
        except StockError:
            payment_service.refund(payment.id)
            order_service.cancel(order.id)
            raise
        except ShippingError:
            inventory_service.release(order.items)
            payment_service.refund(payment.id)
            order_service.cancel(order.id)
            raise

CQRS — Command Query Responsibility Segregation

CQRS คือการแยก Read (Query) กับ Write (Command) ออกจากกัน ใช้ Model คนละตัว Database คนละตัว เหมาะสำหรับระบบที่มี Read:Write Ratio สูง เช่น 90% Read 10% Write สามารถ Optimize แต่ละด้านได้อย่างอิสระ ฝั่ง Write ใช้ Normalized Database สำหรับ Data Integrity ฝั่ง Read ใช้ Denormalized Database (เช่น Elasticsearch) สำหรับ Query ที่เร็ว

# CQRS Architecture
# Command Side (Write)
class CreateOrderCommand:
    def handle(self, data):
        # Validate
        if not data.items:
            raise ValueError("Order must have items")

        # Write to primary database (PostgreSQL)
        order = Order.create(
            user_id=data.user_id,
            items=data.items,
            total=calculate_total(data.items)
        )
        db.session.commit()

        # Publish event for Read Side to update
        event_bus.publish("ORDER_CREATED", order.to_dict())
        return order.id

# Query Side (Read)
class OrderQueryService:
    def __init__(self):
        self.es = Elasticsearch()  # Read from Elasticsearch

    def search_orders(self, user_id, status=None, date_from=None):
        query = {"bool": {"must": [{"term": {"user_id": user_id}}]}}
        if status:
            query["bool"]["must"].append({"term": {"status": status}})
        if date_from:
            query["bool"]["must"].append({"range": {"created_at": {"gte": date_from}}})

        return self.es.search(index="orders", query=query)

# Event Handler: Sync Read Database
@event_handler("ORDER_CREATED")
def sync_order_to_read_db(event):
    es.index(index="orders", id=event["order_id"], body=event)

Event Sourcing — เก็บทุก Event แทน Current State

Event Sourcing คือการเก็บทุก Event ที่เกิดขึ้นกับ Entity แทนที่จะเก็บเฉพาะ State ปัจจุบัน ตัวอย่างเช่น บัญชีธนาคาร แทนที่จะเก็บแค่ "ยอดเงิน 10,000 บาท" จะเก็บทุก Transaction ที่เกิดขึ้น (+5000, -2000, +7000) แล้วคำนวณยอดจาก Events ข้อดีคือมี Audit Trail ครบถ้วน สามารถ Replay Events เพื่อสร้าง State ใหม่ได้ เหมาะกับระบบการเงินและระบบที่ต้องการ Traceability

Circuit Breaker — ป้องกัน Cascading Failure

Circuit Breaker คือ Pattern ที่ป้องกันไม่ให้ Service เรียก Service ที่ล่มซ้ำแล้วซ้ำเล่า เมื่อ Service ปลายทางไม่ตอบสนอง Circuit Breaker จะ "เปิดวงจร" (Open) หยุดส่ง Request ไปยัง Service นั้น แล้วส่ง Fallback Response แทน หลังจากรอสักครู่ จะเปลี่ยนเป็น Half-Open ลอง Request 1-2 ครั้ง ถ้าสำเร็จก็ปิดวงจร (Closed) กลับมาทำงานปกติ ถ้ายังล้มเหลวก็เปิดวงจรต่อ

# Circuit Breaker Implementation (Python)
import time
from enum import Enum

class CircuitState(Enum):
    CLOSED = "closed"       # ปกติ ส่ง Request ได้
    OPEN = "open"           # เปิดวงจร หยุดส่ง Request
    HALF_OPEN = "half_open" # ลองส่ง Request ทดสอบ

class CircuitBreaker:
    def __init__(self, failure_threshold=5, recovery_timeout=30):
        self.state = CircuitState.CLOSED
        self.failure_count = 0
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.last_failure_time = None

    def call(self, func, *args, **kwargs):
        if self.state == CircuitState.OPEN:
            if time.time() - self.last_failure_time > self.recovery_timeout:
                self.state = CircuitState.HALF_OPEN
            else:
                raise CircuitOpenError("Service unavailable")

        try:
            result = func(*args, **kwargs)
            self._on_success()
            return result
        except Exception as e:
            self._on_failure()
            raise

    def _on_success(self):
        self.failure_count = 0
        self.state = CircuitState.CLOSED

    def _on_failure(self):
        self.failure_count += 1
        self.last_failure_time = time.time()
        if self.failure_count >= self.failure_threshold:
            self.state = CircuitState.OPEN

# ใช้งาน
payment_breaker = CircuitBreaker(failure_threshold=5, recovery_timeout=30)

try:
    result = payment_breaker.call(payment_service.charge, order.total)
except CircuitOpenError:
    # Fallback: Queue payment for later
    payment_queue.add(order)

Bulkhead Pattern — แบ่งส่วน ป้องกันการลาม

Bulkhead (ผนังกั้นน้ำในเรือ) คือการแบ่ง Resource Pool ออกเป็นส่วนๆ สำหรับแต่ละ Service ถ้า Service A ใช้ Connection Pool หมด ไม่กระทบ Connection Pool ของ Service B เหมือนเรือที่มีผนังกั้น ถ้าน้ำรั่วเข้าห้องหนึ่ง ห้องอื่นยังลอยได้

Service Mesh — Istio & Linkerd

Service Mesh คือ Infrastructure Layer ที่จัดการ Service-to-Service Communication โดยไม่ต้องแก้โค้ดของ Application ทำงานผ่าน Sidecar Proxy (เช่น Envoy) ที่ติดตั้งข้างทุก Service Sidecar จัดการ Load Balancing, Retry, Circuit Breaking, mTLS Encryption, Observability ให้ทุก Service โดยอัตโนมัติ

# Istio — Service Mesh ยอดนิยม
# ติดตั้ง Istio ใน Kubernetes
istioctl install --set profile=default -y

# Enable Sidecar Injection สำหรับ Namespace
kubectl label namespace default istio-injection=enabled

# Virtual Service — Traffic Routing
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: order-service
spec:
  hosts:
    - order-service
  http:
    - match:
        - headers:
            x-canary:
              exact: "true"
      route:
        - destination:
            host: order-service
            subset: v2
    - route:
        - destination:
            host: order-service
            subset: v1
          weight: 90
        - destination:
            host: order-service
            subset: v2
          weight: 10  # Canary: 10% traffic

# Destination Rule — Circuit Breaker + Load Balancing
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: order-service
spec:
  host: order-service
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        h2UpgradePolicy: UPGRADE
        maxRequestsPerConnection: 10
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 30s
      baseEjectionTime: 30s

Data Management — จัดการข้อมูลในระบบ Distributed

Database per Service

แต่ละ Service เลือก Database ที่เหมาะกับ Workload ของตัวเอง Order Service อาจใช้ PostgreSQL สำหรับ ACID Transactions Product Search Service อาจใช้ Elasticsearch สำหรับ Full-text Search Session Service อาจใช้ Redis สำหรับ Low Latency Analytics Service อาจใช้ ClickHouse สำหรับ OLAP Queries

Eventual Consistency — ความสอดคล้องในที่สุด

ในระบบ Microservices ไม่สามารถมี Strong Consistency ข้ามหลาย Services ได้ เพราะแต่ละ Service มี Database แยก ต้องยอมรับ Eventual Consistency คือข้อมูลอาจไม่ตรงกันชั่วครู่ แต่ในที่สุดจะ Sync กันสำเร็จ ตัวอย่างเช่น เมื่อสร้าง Order สำเร็จ Inventory อาจยังไม่ลด Stock ทันที แต่ผ่านไป 1-2 วินาที Event ถูกประมวลผล Stock ก็จะถูกลด

# Outbox Pattern — ป้องกัน Data Inconsistency
# ปัญหา: ถ้า DB Commit สำเร็จแต่ส่ง Event ไม่สำเร็จ = Data inconsistent
# แก้ไข: เขียน Event ลง Outbox Table ใน Transaction เดียวกับ Business Data

# 1. ใน Transaction เดียว
BEGIN TRANSACTION;
  INSERT INTO orders (id, user_id, total, status)
  VALUES ('order-789', 'user-456', 1980, 'PENDING');

  INSERT INTO outbox (id, aggregate_type, aggregate_id, event_type, payload)
  VALUES ('evt-123', 'Order', 'order-789', 'ORDER_CREATED',
    '{"order_id":"order-789","user_id":"user-456","total":1980}');
COMMIT;

# 2. Background Worker อ่าน Outbox → Publish ไป Kafka → ลบ Outbox Record
# ถ้า Publish ล้มเหลว จะ Retry จาก Outbox

Observability — มองเห็นทุกอย่างในระบบ Distributed

Distributed Tracing

Distributed Tracing คือการติดตาม Request ตั้งแต่เข้า API Gateway จนถึง Service สุดท้าย ใน Monolith ดู Stack Trace ก็พอ แต่ใน Microservices Request เดียวอาจผ่าน 5-10 Services ถ้ามีปัญหา ต้องรู้ว่าช้าที่ Service ไหน ใช้เวลาเท่าไหร่ในแต่ละ Service เครื่องมือเช่น Jaeger และ Zipkin ช่วยแสดง Trace เป็น Timeline ที่เห็นได้ชัด

# OpenTelemetry — มาตรฐานสำหรับ Observability
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.exporter.jaeger.thrift import JaegerExporter

# Setup Tracer
trace.set_tracer_provider(TracerProvider())
jaeger_exporter = JaegerExporter(agent_host_name="jaeger", agent_port=6831)
trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(jaeger_exporter)
)

tracer = trace.get_tracer("order-service")

# ใช้งาน
@app.route("/api/orders", methods=["POST"])
def create_order():
    with tracer.start_as_current_span("create_order") as span:
        span.set_attribute("user.id", request.user_id)

        # Call Payment Service
        with tracer.start_as_current_span("call_payment_service"):
            payment = payment_client.charge(order.total)

        # Call Inventory Service
        with tracer.start_as_current_span("call_inventory_service"):
            inventory = inventory_client.reserve(order.items)

        return jsonify(order.to_dict())

Centralized Logging

เมื่อมี 20 Services แต่ละตัวมี Log ของตัวเอง การหา Error ข้ามหลาย Services เหมือนหาเข็มในมหาสมุทร ต้องรวม Log ไว้ที่เดียว (Centralized Logging) ด้วย ELK Stack หรือ Grafana Loki และใช้ Correlation ID เชื่อมโยง Log จากหลาย Services เข้าด้วยกัน

Containerization — Docker + Kubernetes สำหรับ Microservices

Microservices กับ Docker + Kubernetes เป็นคู่ที่ดีที่สุด Docker ทำให้ทุก Service เป็น Container ที่ Run ได้ทุกที่ Kubernetes จัดการ Orchestration ให้ Scale Service ขึ้นลง Health Check Restart Service ที่ล่ม Load Balance Traffic ไปยัง Instances ต่างๆ

# docker-compose.yml — Microservices ในเครื่อง Dev
version: '3.8'

services:
  api-gateway:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - user-service
      - order-service
      - product-service

  user-service:
    build: ./services/user
    environment:
      - DB_HOST=user-db
      - KAFKA_BROKERS=kafka:9092
    depends_on:
      - user-db
      - kafka

  order-service:
    build: ./services/order
    environment:
      - DB_HOST=order-db
      - KAFKA_BROKERS=kafka:9092
    depends_on:
      - order-db
      - kafka

  product-service:
    build: ./services/product
    environment:
      - DB_HOST=product-db
      - REDIS_HOST=redis
    depends_on:
      - product-db
      - redis

  user-db:
    image: postgres:16
    environment:
      POSTGRES_DB: users
      POSTGRES_PASSWORD: secret

  order-db:
    image: postgres:16
    environment:
      POSTGRES_DB: orders
      POSTGRES_PASSWORD: secret

  product-db:
    image: mongo:7
    environment:
      MONGO_INITDB_DATABASE: products

  kafka:
    image: confluentinc/cp-kafka:7.6.0
    environment:
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092

  redis:
    image: redis:7-alpine

CI/CD สำหรับ Microservices

ในระบบ Microservices แต่ละ Service ควรมี CI/CD Pipeline ของตัวเอง เพื่อให้ Deploy ได้อิสระ Pipeline ควรรวม Unit Test, Integration Test, Build Docker Image, Push to Registry และ Deploy to Kubernetes การใช้ Monorepo (เช่น Turborepo, Nx) ช่วยจัดการ Shared Code ระหว่าง Services ส่วน Polyrepo ให้แต่ละ Service มี Repository แยก เหมาะเมื่อทีมต่างกันดูแลแต่ละ Service

# GitHub Actions — CI/CD สำหรับ Microservice
# .github/workflows/order-service.yml
name: Order Service CI/CD

on:
  push:
    paths:
      - 'services/order/**'
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Tests
        run: |
          cd services/order
          pip install -r requirements.txt
          pytest --cov=.

  build-and-deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build & Push Docker Image
        run: |
          docker build -t registry.example.com/order-service:${GITHUB_SHA} services/order
          docker push registry.example.com/order-service:${GITHUB_SHA}

      - name: Deploy to Kubernetes
        run: |
          kubectl set image deployment/order-service             order-service=registry.example.com/order-service:${GITHUB_SHA}             --namespace=production

ข้อผิดพลาดที่พบบ่อยในการทำ Microservices

ข้อผิดพลาดปัญหาวิธีแก้
เริ่มด้วย Microservices ตั้งแต่แรกยังไม่เข้าใจ Domain แบ่ง Service ผิดเริ่มจาก Monolith แล้วค่อย Extract Service
แชร์ Database ข้าม ServicesTight Coupling, Schema Change กระทบหลาย ServicesDatabase per Service เสมอ
Synchronous Call Chain ยาวLatency สูง, Single Point of Failureใช้ Async Event-Driven เมื่อเป็นไปได้
ไม่มี Distributed TracingDebug ปัญหาข้ามหลาย Services ไม่ได้ใช้ OpenTelemetry + Jaeger ตั้งแต่เริ่ม
ไม่มี Circuit BreakerCascading Failure ลามทั้งระบบใส่ Circuit Breaker ทุก External Call
Service ใหญ่เกินไปกลายเป็น Distributed Monolithแบ่งตาม Bounded Context ของ DDD
ไม่จัดการ Data Consistencyข้อมูลไม่ตรงกันข้ามหลาย Servicesใช้ Saga Pattern + Outbox Pattern

FAQ — คำถามที่พบบ่อยเกี่ยวกับ Microservices

Q: Microservices ต้องใช้ Kubernetes ไหม?

A: ไม่จำเป็น แต่แนะนำอย่างยิ่ง Kubernetes ช่วยจัดการ Service Discovery, Load Balancing, Auto Scaling, Health Check, Rolling Update ให้อัตโนมัติ ถ้าไม่ใช้ K8s ก็ต้องจัดการเองทุกอย่าง ทางเลือกอื่น เช่น Docker Swarm (ง่ายกว่าแต่ Feature น้อยกว่า) หรือ Managed Services (ECS, Cloud Run, App Runner) ที่ให้ Cloud Provider จัดการให้

Q: กี่ Services ถึงเรียกว่า Microservices?

A: ไม่มีจำนวนขั้นต่ำ ไม่ใช่ว่ามี 3 Services แล้วเรียก Microservices ประเด็นสำคัญคือ แต่ละ Service ต้องเป็นอิสระจริงๆ มี Database แยก Deploy แยก ทีมแยก ถ้ามี 10 Services แต่ใช้ Database เดียวกัน Deploy พร้อมกัน ก็ไม่ใช่ Microservices แค่ Distributed Monolith

Q: Micro Frontend คืออะไร?

A: Micro Frontend คือการนำแนวคิด Microservices มาใช้กับ Frontend แบ่งหน้าเว็บออกเป็นส่วนย่อยที่ทีมต่างกันดูแล เช่น Header เป็น React, Product List เป็น Vue, Cart เป็น Svelte ใช้เทคนิคเช่น Module Federation (Webpack 5), Single-SPA หรือ Web Components เหมาะกับทีมขนาดใหญ่ที่มีหลายทีมดูแล Frontend

Q: เลือก Saga แบบ Choreography หรือ Orchestration?

A: Choreography (แต่ละ Service Publish/Subscribe Events เอง) เหมาะกับ Flow ที่เรียบง่าย 2-3 Steps เพราะไม่มี Central Coordinator แต่ยากต่อการ Debug Orchestration (มี Saga Orchestrator คอยสั่งทุก Step) เหมาะกับ Flow ที่ซับซ้อน 4+ Steps เพราะเห็น Flow ทั้งหมดในที่เดียว Debug ง่ายกว่า แต่ Orchestrator เป็น Single Point of Failure

Q: เริ่มต้นอย่างไรถ้าจะย้ายจาก Monolith ไป Microservices?

A: ใช้ Strangler Fig Pattern คือค่อยๆ ย้ายทีละฟีเจอร์ 1) เลือกฟีเจอร์ที่เปลี่ยนบ่อยหรือต้อง Scale (เช่น Search, Notification) 2) สร้าง Service ใหม่ 3) Route Traffic ส่วนนั้นไปที่ Service ใหม่ 4) เมื่อ Service ใหม่ทำงานได้ดี ลบโค้ดส่วนนั้นออกจาก Monolith 5) ทำซ้ำจนเหลือแต่ Core ใน Monolith หรือ Extract ทั้งหมด