Chương 10. Khi Nào Nên Tách Một Service?

Tách service là một quyết định lớn. Nó không chỉ là di chuyển code sang một project khác. Nó thay đổi cách hệ thống deploy, giao tiếp, lưu dữ liệu, debug, test và vận hành.

Vì vậy, câu hỏi không nên là:

> Có nên dùng microservices không?

Câu hỏi tốt hơn là:

> Phần này có lý do đủ mạnh để tách thành service riêng chưa?

Chương này bàn về thời điểm nên tách, khi nào chưa nên tách, và trước khi tách cần hỏi những gì.

---

10.1. Ví dụ quán bánh: khi nào tách bộ phận thành đơn vị riêng?

Quán bánh ban đầu có nhiều bộ phận trong cùng một cửa hàng:

  • Nhận đơn.
  • Bếp.
  • Giao hàng.
  • Thu tiền.
  • Chăm sóc khách.

Đây giống modular monolith.

Vậy khi nào một bộ phận nên tách thành đơn vị riêng?

Giao hàng quá khác phần còn lại

Giao hàng cần:

  • Theo dõi vị trí shipper.
  • Tối ưu tuyến đường.
  • Giao tiếp realtime.
  • Xử lý hủy/chuyển đơn.

Nó có workload khác hẳn bếp và quầy bán hàng. Có thể đáng tách.

Bếp trung tâm cần scale riêng

Đơn online tăng mạnh, bếp cần hệ thống riêng để nhận phiếu, chia ca, tối ưu nguyên liệu. App đặt hàng không nên bị ảnh hưởng khi bếp xử lý nặng.

Kế toán cần bảo mật và audit riêng

Tiền bạc cần kiểm soát chặt hơn. Không phải ai trong quán cũng được sửa sổ kế toán.

Chăm sóc khách cần công cụ riêng

Họ cần lịch sử chat, ticket, email, phân công nhân viên.

Nhưng nếu quán còn nhỏ, tách các bộ phận này thành công ty riêng ngay từ đầu sẽ rất nặng. Họ sẽ mất thời gian phối hợp hơn là bán bánh.

Phần mềm cũng vậy: tách service phải đến từ áp lực thật.

---

10.2. Tách service nghĩa là tách những gì?

Nhiều người nghĩ tách service là copy code sang một repo mới.

Thật ra tách service có thể kéo theo:

  • Runtime riêng.
  • Deploy riêng.
  • Config riêng.
  • Logs riêng.
  • Metrics riêng.
  • Alert riêng.
  • Database hoặc schema ownership riêng.
  • API contract riêng.
  • Security boundary riêng.
  • Scaling rule riêng.
  • Failure mode riêng.

Trước khi tách, phải hiểu rằng ta không chỉ tách code. Ta đang tạo một đơn vị vận hành mới.

Nếu một phần chưa đủ quan trọng để có dashboard, alert, owner và rollback riêng, có thể nó chưa đáng tách thành service.

---

10.3. Lý do tốt 1: tách vì workload khác biệt

Một service đáng tách khi workload của nó khác phần còn lại rất rõ.

Ví dụ:

  • API chính xử lý request ngắn.
  • AI processing gọi model mất 90 giây.
  • Video processing cần CPU/GPU.
  • Chat realtime giữ nhiều connection.
  • Report chạy batch nặng.
  • Search cần index riêng.

Nếu để chung, một workload có thể ảnh hưởng phần còn lại.

Ví dụ:

Main App
  |-- API thường
  |-- AI job
  |-- report nặng

Nếu report ăn CPU hoặc AI job giữ quá nhiều connection, API thường có thể chậm.

Bước đầu có thể chưa cần service riêng. Có thể tách:

  • Queue riêng.
  • Worker riêng.
  • Machine riêng.

Nếu vẫn chưa đủ, hoặc cần deploy/scale riêng, lúc đó tách service hợp lý hơn.

Nguyên tắc:

> Workload khác biệt là một trong những lý do mạnh nhất để tách.

---

10.4. Lý do tốt 2: tách vì cần scale độc lập

Nếu một phần cần scale khác hẳn phần còn lại, tách có thể hợp lý.

Ví dụ:

  • Catalog đọc rất nhiều, payment ít hơn.
  • Notification cần gửi hàng trăm nghìn email.
  • Search cần nhiều node riêng.
  • AI worker cần concurrency lớn.
  • Realtime gateway cần giữ nhiều connection.

Nếu nằm chung monolith, ta phải scale cả app:

App x 20

Nhưng có thể chỉ một phần thật sự cần x20.

Tách giúp:

API x 3
Notification x 20
AI Worker x 30
Report x 1

Tuy nhiên, trước khi tách service, hãy hỏi:

  • Có thể scale worker riêng không?
  • Có thể tách queue riêng không?
  • Có thể chạy process riêng trong cùng codebase không?
  • Có thể tối ưu workload trước không?

Tách service là một cách scale, không phải cách duy nhất.

---

10.5. Lý do tốt 3: tách vì failure isolation

Một phần có thể lỗi thường xuyên hoặc phụ thuộc bên ngoài. Ta không muốn nó làm chết phần chính.

Ví dụ:

  • Email provider lỗi.
  • AI provider timeout.
  • Search cluster down.
  • Report job ăn hết RAM.
  • Payment provider chập chờn.
  • Video processing worker bị OOM.

Nếu nằm chung request path, lỗi lan rất nhanh.

Tách service hoặc ít nhất tách worker giúp hệ thống degrade tốt hơn:

Search lỗi -> hiển thị kết quả cơ bản
Notification lỗi -> đơn hàng vẫn tạo
AI quá tải -> bài ở trạng thái pending
Report lỗi -> API chính vẫn chạy

Nhưng failure isolation cần nhiều hơn việc tách code:

  • Timeout.
  • Retry có backoff.
  • Circuit breaker.
  • Fallback.
  • Queue.
  • Alert.
  • Dead letter.

Nếu service A vẫn gọi service B không timeout và chờ mãi, service B chết vẫn kéo service A chết theo.

---

10.6. Lý do tốt 4: tách vì deploy độc lập

Một phần có thể cần release thường xuyên hơn phần còn lại.

Ví dụ:

  • Search ranking thay đổi liên tục.
  • Recommendation model cập nhật thường xuyên.
  • Notification template thay đổi nhiều.
  • Payment hotfix cần deploy gấp.
  • AI prompt/model pipeline cần thử nghiệm riêng.

Nếu deploy chung cả monolith mỗi lần thay đổi nhỏ, rủi ro và thời gian release tăng.

Tách service giúp deploy độc lập.

Nhưng deploy độc lập chỉ thật sự có giá trị khi:

  • API contract ổn định.
  • Service không buộc các service khác deploy cùng.
  • Có backward compatibility.
  • Có monitoring riêng.
  • Có rollback riêng.

Nếu mỗi lần deploy service A vẫn phải deploy B và C, ta chưa đạt lợi ích deploy độc lập.

---

10.7. Lý do tốt 5: tách vì ownership

Khi team lớn, ownership rất quan trọng.

Ví dụ:

  • Team Identity sở hữu đăng nhập/phân quyền.
  • Team Payment sở hữu thanh toán.
  • Team Search sở hữu tìm kiếm.
  • Team Notification sở hữu email/SMS/push.
  • Team Data sở hữu analytics pipeline.

Nếu mọi team cùng sửa một monolith lớn, có thể:

  • Dẫm chân nhau.
  • Review khó.
  • Deploy phụ thuộc nhau.
  • Không ai chịu trách nhiệm rõ.

Tách service giúp mỗi team chịu trách nhiệm end-to-end:

  • Code.
  • Database.
  • API.
  • Deploy.
  • Monitoring.
  • Incident.

Nhưng nếu team chỉ có vài người, ownership riêng thường chưa phải vấn đề lớn. Khi đó tách service có thể tạo thêm việc hơn là giảm việc.

---

10.8. Lý do tốt 6: tách vì dữ liệu hoặc bảo mật

Một số dữ liệu cần ranh giới mạnh.

Ví dụ:

  • Payment data.
  • Password/token.
  • Medical/financial data.
  • Tenant enterprise.
  • Audit log nhạy cảm.
  • Secret/API key quan trọng.

Tách service có thể giúp:

  • Giới hạn ai được truy cập dữ liệu.
  • Giới hạn service nào biết secret.
  • Audit rõ hơn.
  • Áp dụng policy riêng.
  • Giảm blast radius nếu có lỗi.

Nhưng tách service không tự động an toàn. Nếu mọi service vẫn dùng chung database hoặc cùng quyền truy cập secret, boundary chỉ là hình thức.

Tách vì bảo mật phải đi cùng:

  • Least privilege.
  • Service-to-service auth.
  • Network boundary.
  • Secret management.
  • Audit log.
  • Encryption nếu cần.

---

10.9. Lý do xấu 1: tách vì code nhìn có vẻ nhiều

Codebase lớn không tự động nghĩa là cần microservices.

Có thể cần:

  • Chia module rõ hơn.
  • Xóa code chết.
  • Tách service layer.
  • Viết test.
  • Cải thiện folder structure.
  • Định nghĩa domain boundary.

Nếu code rối, tách service quá sớm có thể làm rối hơn:

Code rối trong một app
  -> code rối qua network

Trước khi tách service, hãy thử modular hóa.

---

10.10. Lý do xấu 2: tách vì muốn giống công ty lớn

Công ty lớn dùng microservices vì họ có áp lực lớn:

  • Nhiều team.
  • Nhiều domain.
  • Traffic lớn.
  • Deploy liên tục.
  • Hạ tầng mạnh.
  • Observability tốt.
  • Platform team.

Nếu một team nhỏ bắt chước kiến trúc của công ty lớn mà không có cùng áp lực và năng lực vận hành, sẽ rất dễ tự làm chậm mình.

Đừng copy kiến trúc. Hãy copy cách họ suy nghĩ về trade-off.

---

10.11. Lý do xấu 3: tách khi domain chưa rõ

Nếu nghiệp vụ còn thay đổi liên tục, service boundary sẽ thay đổi liên tục.

Ví dụ ban đầu nghĩ:

Course Service
Teacher Service
Booking Service
Payment Service

Sau vài tháng phát hiện:

  • Course và booking dính rất chặt.
  • Teacher profile và marketplace listing không tách đơn giản.
  • Payment phụ thuộc nhiều trạng thái booking.

Nếu đã tách service quá sớm, mỗi lần đổi domain phải sửa nhiều API, event, database.

Khi domain chưa rõ, modular monolith giúp thay đổi nhanh hơn.

---

10.12. Lý do xấu 4: tách khi chưa có observability

Microservices làm tăng số nơi lỗi có thể xảy ra.

Nếu chưa có:

  • Logs tập trung.
  • Correlation ID.
  • Metrics từng service.
  • Distributed tracing.
  • Alert.
  • Dashboard.

Thì tách service sẽ làm debug khó hơn nhiều.

Trước khi tách, hãy hỏi:

Nếu request đi qua 4 service và lỗi ở service thứ 3, ta có nhìn thấy không?

Nếu câu trả lời là không, hãy cải thiện observability trước.

---

10.13. Lý do xấu 5: tách nhưng vẫn dùng chung database lung tung

Một anti-pattern phổ biến:

Order Service
Payment Service
User Service

Nhưng cả ba cùng đọc/ghi trực tiếp một database, join bảng của nhau, sửa dữ liệu của nhau.

Khi đó service boundary không thật.

Bạn có cái khó của microservices:

  • Network.
  • Deploy nhiều service.
  • Debug khó.

Nhưng không có lợi ích dữ liệu độc lập.

Nếu chưa thể tách ownership dữ liệu, hãy cân nhắc modular monolith trước.

---

10.14. Các mức tách nhẹ hơn microservice

Không phải cứ cần tách là phải tách service hoàn chỉnh.

Tách module

Code vẫn cùng app, nhưng module rõ hơn.

Tách queue

Mỗi loại job có queue riêng:

email queue
ai queue
report queue
video queue

Tách worker

Worker riêng cho workload riêng:

web app
email worker
ai worker
report worker

Tách process

Cùng codebase nhưng chạy process khác nhau.

Tách read model

Dữ liệu đọc/tìm kiếm/phân tích đi sang cache/search/warehouse.

Tách database schema

Vẫn cùng database server nhưng schema/module ownership rõ hơn.

Tách service

Runtime, deploy, contract, ownership riêng.

Tư duy:

> Tách có nhiều mức. Hãy chọn mức tách nhỏ nhất giải quyết được vấn đề.

---

10.15. Quy trình tách service thực dụng

Nếu quyết định tách, đừng làm ồ ạt. Làm từng bước.

Bước 1: Xác định lý do tách

Tách vì:

  • Scale?
  • Deploy?
  • Failure isolation?
  • Ownership?
  • Security?
  • Workload?

Nếu không nói rõ được lý do, chưa nên tách.

Bước 2: Xác định boundary

Service này sở hữu domain nào?

Nó không sở hữu gì?

Bước 3: Xác định dữ liệu

  • Dữ liệu nào thuộc service?
  • Dữ liệu nào chỉ là bản sao?
  • Service khác truy cập bằng cách nào?
  • Có cần migration không?

Bước 4: Xác định giao tiếp

  • HTTP/gRPC hay message?
  • Cần sync hay async?
  • Timeout bao nhiêu?
  • Retry thế nào?
  • Event schema là gì?

Bước 5: Xác định failure mode

  • Service chết thì sao?
  • Có fallback không?
  • Job có mất không?
  • Có circuit breaker không?
  • Có degrade được không?

Bước 6: Xác định observability

  • Log gì?
  • Metrics gì?
  • Trace ra sao?
  • Alert nào?
  • Dashboard nào?

Bước 7: Tách dần

  • Tạo interface trước.
  • Chuyển logic vào module rõ.
  • Chạy song song nếu cần.
  • Di chuyển data cẩn thận.
  • Cutover từng bước.
  • Có rollback plan.

---

10.16. Ví dụ: có nên tách notification service?

Tình huống:

  • Hệ thống gửi email xác nhận đơn hàng.
  • Email provider đôi khi chậm.
  • Request đặt hàng đang chờ gửi email.

Không nên tách service ngay.

Bước đầu:

Đưa gửi email vào queue/worker

Nếu sau này:

  • Có email, SMS, push.
  • Có template phức tạp.
  • Có campaign lớn.
  • Có nhiều team dùng notification.
  • Cần rate limit provider.
  • Cần deploy notification riêng.

Lúc đó tách Notification Service hợp lý hơn.

Quyết định:

Chậm vì email -> queue trước.
Notification trở thành capability lớn -> service sau.

---

10.17. Ví dụ: có nên tách payment service?

Payment là phần nhạy cảm hơn.

Lý do có thể tách:

  • Cần bảo mật cao.
  • Cần audit rõ.
  • Cần idempotency nghiêm túc.
  • Cần tích hợp nhiều provider.
  • Nhiều module cần dùng payment.
  • Không muốn logic payment rải khắp app.

Nhưng tách payment cũng khó:

  • Transaction với order phức tạp hơn.
  • Webhook phải xử lý cẩn thận.
  • Refund/reconciliation cần đúng.
  • Dữ liệu tiền bạc không được sai.

Nếu hệ thống nhỏ, có thể bắt đầu bằng payment module rõ trong modular monolith:

payment/
  create_payment
  handle_webhook
  refund
  reconcile

Sau này khi áp lực đủ lớn, tách thành service.

---

10.18. Ví dụ: có nên tách AI service?

AI workload thường có đặc điểm:

  • Latency cao.
  • I/O-bound nếu gọi provider.
  • CPU/GPU-bound nếu tự host model.
  • Cost theo request/token.
  • Rate limit.
  • Cần version prompt/model.
  • Có thể lỗi tạm thời.

Bước đầu:

Main app -> queue -> AI worker

Nếu AI worker trong cùng codebase vẫn đủ, chưa cần service riêng.

Tách AI service khi:

  • Cần scale riêng.
  • Cần runtime riêng.
  • Cần team AI deploy riêng.
  • Cần GPU/model serving riêng.
  • AI lỗi không được ảnh hưởng main app.
  • Cần quản lý rate limit/cost/observability riêng.

Kiến trúc có thể thành:

Main App -> Queue -> AI Service/AI Workers -> Result Event

Tư duy:

> Tách AI service không phải vì AI nghe đặc biệt, mà vì workload và failure mode của nó khác phần còn lại.

---

10.19. Checklist quyết định tách service

Trước khi tách, trả lời:

Về lý do

  • Tách để giải quyết vấn đề cụ thể nào?
  • Nếu không tách thì đau ở đâu?
  • Có cách nhẹ hơn không?

Về domain

  • Service này sở hữu domain nào?
  • Boundary có ổn định không?
  • Nó có đang thay đổi quá nhiều không?

Về dữ liệu

  • Dữ liệu nào thuộc service?
  • Service khác có cần dữ liệu đó không?
  • Cần sync/read model/event không?
  • Transaction hiện tại có bị phá không?

Về giao tiếp

  • Service khác gọi nó bằng API hay message?
  • Cần kết quả ngay không?
  • Timeout/retry/idempotency thế nào?
  • Event có version không?

Về vận hành

  • Ai deploy service?
  • Ai nhận alert?
  • Logs/metrics/traces đã có chưa?
  • Có health check không?
  • Có rollback không?

Về failure

  • Service chết thì hệ thống chính có sống không?
  • Có fallback không?
  • Có queue giữ việc không?
  • Có dead letter không?

Nếu checklist này làm bạn thấy quá nhiều câu chưa trả lời được, đó là tín hiệu tốt: chưa nên tách vội.

---

10.20. Kết luận của chương

Nên tách service khi có lý do thật:

  • Workload khác biệt.
  • Cần scale độc lập.
  • Cần failure isolation.
  • Cần deploy độc lập.
  • Cần ownership rõ.
  • Cần boundary dữ liệu/bảo mật.

Không nên tách chỉ vì:

  • Code nhiều.
  • Muốn giống công ty lớn.
  • Microservices nghe hiện đại.
  • Domain chưa rõ.
  • Chưa có observability.
  • Chưa có năng lực vận hành.

Điều quan trọng nhất:

> Chọn mức tách nhỏ nhất giải quyết được vấn đề.

Đôi khi chỉ cần tách module. Đôi khi chỉ cần queue riêng. Đôi khi chỉ cần worker riêng. Đôi khi cần service thật sự.

Một kiến trúc sư thực dụng không hỏi "có nên microservices không", mà hỏi "phần nào cần tách, tách đến mức nào, và cái giá đó có đáng không".