DDD: Phần 11 – Tiến hóa quá trình ra quyết định

“The only constant in life is change.” — Heraclitus

DDD không chỉ là thiết kế cho hiện tại mà còn phải chuẩn bị sẵn cho tương lai, cho sự thay đổi đến từ:

  • 🧭 Business domain (các subdomain thay đổi loại hình)
  • 🏢 Organizational structure
  • 🎓 Domain knowledge
  • 📈 Product growth

🧱 1. Thay đổi loại Subdomain

Kiểu chuyển đổiĐộng lực chuyển đổiVí dụ
Core → GenericMất tính cạnh tranh vì có sản phẩm tốt hơn ngoài thị trườngBuyIT tự tối ưu giao hàng → DeliverIT ra đời tốt hơn → core → generic
Generic → CoreXây giải pháp riêng để giải quyết vấn đề tồn tạiBuyIT thay phần mềm tồn kho bằng giải pháp AI nội bộ
Supporting → GenericCó giải pháp mã nguồn mở thay thế tốt hơnQuản lý hợp đồng chuyển sang xài open source có OCR
Supporting → CoreLogic trở nên phức tạp và ảnh hưởng đến lợi nhuậnETL ban đầu đơn giản → thêm các logic tối ưu chi phí
Core → SupportingLogic không còn tạo ra giá trị cạnh tranhCắt bớt complexity không cần thiết
Generic → SupportingTự làm lại vì tích hợp bên ngoài quá phức tạpOpen source khó tích hợp → quay lại dùng hệ thống cũ

📌 → Khi subdomain đổi loại, bounded context và chiến lược thiết kế cũng phải đổi theo.

🔄 1. Core → Generic

Một Core Subdomain chuyển thành Generic Subdomain khi giải pháp tốt hơn xuất hiện rộng rãi ngoài thị trường, khiến tính cạnh tranh của giải pháp cũ bị mất.

📌 Ví dụ cụ thể:

  • BuyIT từng tự xây dựng giải pháp tối ưu giao hàng, đạt lợi thế cạnh tranh.
  • Sau đó, DeliverIT xuất hiện, cung cấp dịch vụ tối ưu đường đi tiên tiến hơn (giải được “bài toán người bán hàng”), với giá rẻ hơn nhiều.
  • Lúc này, giải pháp nội bộ của BuyIT mất lợi thế, biến từ Core thành Generic, vì giải pháp tốt nhất đã phổ biến và không còn độc quyền.

🔄 2. Generic → Core

Một Generic Subdomain chuyển thành Core Subdomain khi doanh nghiệp quyết định đầu tư xây dựng giải pháp nội bộ nhằm tạo lợi thế cạnh tranh.

📌 Ví dụ cụ thể:

  • BuyIT trước đây dùng giải pháp quản lý tồn kho thương mại (off-the-shelf).
  • Nhưng dự đoán tồn kho thường xuyên sai lệch, dẫn đến mất cơ hội bán hàng tốt.
  • BuyIT quyết định đầu tư phát triển hệ thống riêng, sử dụng thuật toán dự đoán tồn kho chính xác và hiệu quả hơn hẳn đối thủ.
  • Việc tự xây dựng này biến quản lý tồn kho từ Generic thành Core Subdomain.

💡 Ví dụ thực tế: Amazon phát triển cơ sở hạ tầng cho riêng mình, sau đó biến thành Amazon Web Services (AWS).


🔄 3. Supporting → Generic

Một Supporting Subdomain biến thành Generic Subdomain khi có một giải pháp chung bên ngoài vượt trội hơn giải pháp nội bộ hiện tại.

📌 Ví dụ cụ thể:

  • Hệ thống quản lý hợp đồng (Vendor Contract Management) của BuyIT vốn đơn giản, kiểu CRUD thông thường (Supporting).
  • Sau vài năm, một giải pháp mã nguồn mở mới xuất hiện với các tính năng vượt trội như OCR, full-text search—những tính năng BuyIT luôn muốn nhưng không ưu tiên phát triển.
  • BuyIT quyết định bỏ hệ thống nội bộ, chuyển sang sử dụng giải pháp mã nguồn mở, biến quản lý hợp đồng thành Generic.

🔄 4. Supporting → Core

Supporting Subdomain trở thành Core Subdomain khi doanh nghiệp tìm ra cách tối ưu và phức tạp hóa logic, mang lại lợi ích kinh doanh rõ rệt.

📌 Ví dụ cụ thể:

  • Ban đầu, Supporting Subdomain chỉ gồm các chức năng đơn giản (CRUD hoặc ETL đơn giản).
  • Theo thời gian, logic nghiệp vụ ngày càng trở nên phức tạp, nhưng lại mang lại lợi ích rõ rệt về tài chính hoặc cắt giảm chi phí.
  • Khi đó, logic này trở thành lợi thế cạnh tranh và được nâng cấp thành Core Subdomain.

🔄 5. Core → Supporting

Một Core Subdomain có thể biến thành Supporting khi logic hiện tại quá phức tạp, nhưng lại không còn tạo ra lợi thế kinh doanh rõ ràng.

  • Khi complexity không còn mang lại lợi nhuận, tổ chức sẽ đơn giản hóa logic nghiệp vụ, loại bỏ sự phức tạp không cần thiết.
  • Subdomain lúc này chỉ cần đơn giản, đủ để hỗ trợ các phần còn lại của hệ thống (vì không còn lợi thế cạnh tranh).

🔄 6. Generic → Supporting

Một Generic Subdomain có thể chuyển thành Supporting khi việc tích hợp giải pháp chung bên ngoài quá tốn kém hoặc phức tạp, và doanh nghiệp quay lại dùng giải pháp nội bộ đơn giản hơn.

📌 Ví dụ cụ thể:

  • Quay lại ví dụ quản lý tài liệu của BuyIT, giả sử giải pháp mã nguồn mở tuy tốt nhưng việc tích hợp quá phức tạp và không đáng công sức.
  • BuyIT quyết định quay lại dùng giải pháp nội bộ cũ, biến subdomain từ Generic thành Supporting.

🧭 2. Chiến lược thiết kế chiến lược (Strategic Design)

Khi các subdomain thay đổi loại hình (Core, Supporting, Generic), điều này lập tức ảnh hưởng đến các quyết định chiến lược (strategic design) trong thiết kế phần mềm, đặc biệt là cách chúng ta thiết kế bounded context và lựa chọn integration patterns.

  • Core Subdomain:
    • Bắt buộc dùng Anticorruption Layer hoặc Open-Host Service
    • Không được duplicate → phải có 1 team chính phát triển
  • Supporting / Generic:
    • Có thể duplicate / outsource
    • Dùng Separate Ways hoặc Shared Kernel

🌀 Khi loại subdomain thay đổi → cần chuyển đổi mô hình tích hợp cho phù hợp.


🛠️ 3. Chiến lược thiết kế chiến thuật (Tactical Design)

📉 Dấu hiệu subdomain đang thay đổi loại:

  • Codebase ngày càng đau đớn (painful) khi thêm chức năng mới → đã vượt qua mức phức tạp phù hợp với thiết kế ban đầu
  • Tăng số lượng invariant, rule, case đặc biệt

⚙️ Cách refactor chiến thuật:

✅ Transaction Script → Active Record

  • Khi việc truy vấn dữ liệu trở nên rối rắm
  • Tạo các lớp ActiveRecord đại diện cho dữ liệu

✅ Active Record → Domain Model

  • Logic trở nên phức tạp, lặp lại
  • Tạo các Value Object, xác định ranh giới Transaction
  • Di chuyển toàn bộ logic vào object:
public class Player
{
    public Guid Id { get; private set; }
    public int Points { get; private set; }

    public void ApplyBonus(int percentage)
    {
        Points *= 1 + percentage/100.0;
    }
}

✅ Domain Model → Event Sourced Domain Model

  • Khi cần audit, track lifecycle, phục vụ analytics

Có 2 cách migrate:

  1. Generate giả lập chuỗi sự kiện từ trạng thái hiện tại
  2. Model một sự kiện migration ghi nhận “đây là dữ liệu legacy”

🏢 4. Tổ chức thay đổi → Integration thay đổi

Thay đổi tổ chứcTác động
Mở rộng team → chia bounded context lớn raTách thành các context nhỏ hơn
Team chuyển ra xa → giảm giao tiếpPartnership → Customer-Supplier
Giao tiếp tệ → tách hẳnCustomer-Supplier → Separate Ways

🧠 5. Domain Knowledge tiến hoá (hoặc bị mất)

  • Khi hiểu rõ hơn → tách lại bounded context cho hợp lý hơn
  • Khi mất kiến thức domain (rời team, thiếu doc) → dễ rơi vào legacy

🎯 EventStorming là công cụ hữu ích để khôi phục domain knowledge bị mất.


🌱 6. Quản lý sự phát triển sản phẩm (Growth)

☠️ Nguy hiểm: “Big Ball of Mud” nếu không refactor theo kịp

🧩 Giải pháp: Dùng DDD để kiểm soát tăng trưởng

📌 Revisit Subdomains

  • Subdomain ban đầu có thể đúng, nhưng khi lớn lên:
    • Use case lan rộng
    • Dữ liệu thay đổi
  • Heuristic: gom các use case có cùng dữ liệu → tạo subdomain mới

📊 Hành động: xem lại subdomain nào đã phình to → chia tách nếu cần.


📌 Rà soát lại Bounded Context

“Jack of all trades, master of none.”

  • Một bounded context ban đầu chỉ lo 1 việc, nhưng giờ đang:
    • Làm 4–5 thứ không liên quan
    • Phụ thuộc vào các context khác để hoàn thành tác vụ

📊 Dấu hiệu cảnh báo:

  • Bounded context “chatty” → phải gọi context khác liên tục

🛠️ Giải pháp:

  • Tách lại context theo focus cụ thể
  • Đảm bảo mỗi context có mô hình tập trung giải quyết 1 vấn đề chính

📌 Kiểm tra lại Aggregate

Aggregates chỉ nên chứa những dữ liệu cần strong consistency.

📉 Khi phát triển thêm tính năng:

  • Đừng “nhét” thêm mọi thứ vào aggregate cũ
  • Nếu dữ liệu mới không cần strongly consistent với phần cũ → nên tạo aggregate mới

⚡ Lợi ích:

  • Đơn giản hóa aggregate cũ
  • Có thể khám phá ra 1 mô hình mới chưa được hiện thực hóa

✅ Tổng kết

Thiết kế tốt không phải là “đúng mãi mãi”, mà là thiết kế có thể tiến hoá linh hoạt.

🎯 Hành động:

  • Khi business thay đổi → rà soát lại subdomain type
  • Khi logic đau đớn → đánh giá lại pattern phù hợp
  • Khi tổ chức thay đổi → điều chỉnh integration
  • Khi hiểu rõ hơn domain → đơn giản hoá mô hình
  • Khi phát triển nhiều hơn → phân tách context, aggregate, và subdomain

Để lại một bình luận