DDD: Phần 8 – Các pattern kiến trúc trong Domain-Driven Design

Phần này này giải thích rõ sự khác biệt giữa logic nghiệp vụ (business logic) và các mẫu kiến trúc (architectural patterns). Trong khi các mẫu chiến thuật (tactical patterns) tập trung vào cách xây dựng logic nghiệp vụ, các mẫu kiến trúc quan tâm đến cách tổ chức tương tác và phụ thuộc giữa các thành phần trong hệ thống.


🎯 1. Business Logic vs. Architectural Patterns

Business logic là cốt lõi của ứng dụng, nhưng không phải là thành phần duy nhất. Hệ thống phần mềm còn cần:

  • Tương tác với người dùng (input/output).
  • Lưu trữ và truy xuất dữ liệu.
  • Tích hợp hệ thống bên ngoài.

Khi logic nghiệp vụ bị phân tán, hệ thống sẽ trở nên khó bảo trì, thay đổi dễ gây ra lỗi không lường trước.

Architectural patterns đưa ra nguyên tắc tổ chức hệ thống rõ ràng, giúp quản lý tương tác giữa các thành phần, giảm độ phức tạp, dễ bảo trì và phát triển lâu dài.

Lưu ý: Chọn mẫu kiến trúc phù hợp rất quan trọng cho việc bảo trì và mở rộng phần mềm.


🧩 2. Layered Architecture (Kiến trúc phân lớp)

Kiến trúc phân lớp tổ chức hệ thống theo chiều ngang, chia thành các tầng có trách nhiệm rõ ràng:

  • Presentation Layer (PL): Giao diện người dùng (web, CLI, API, event).
  • Business Logic Layer (BLL): Xử lý logic nghiệp vụ (Domain Model, Active Record).
  • Data Access Layer (DAL): Lưu trữ dữ liệu và tích hợp hệ thống bên ngoài (databases, APIs).

Giao tiếp giữa các lớp diễn ra theo hướng từ trên xuống, lớp trên chỉ phụ thuộc vào lớp ngay dưới, giúp giảm kết nối chặt chẽ và tăng khả năng thay đổi.

Variant – Service Layer: Một lớp trung gian để điều phối tương tác giữa Presentation và Business Logic, giúp hệ thống rõ ràng hơn, dễ tái sử dụng, và test.

Ví dụ minh họa Service Layer:

// Presentation Layer (MVC Controller)
public ActionResult Create(ContactDetails contactDetails) {
    var result = _userService.Create(contactDetails);
    return View(result);
}

// Service Layer
public OperationResult Create(ContactDetails contactDetails) {
    try {
        _db.StartTransaction();
        var user = new User();
        user.SetContactDetails(contactDetails);
        user.Save();
        _db.Commit();
        return OperationResult.Success;
    } catch (Exception ex) {
        _db.Rollback();
        return OperationResult.Exception(ex);
    }
}

Khi nào sử dụng?

  • Phù hợp khi sử dụng Transaction Script hoặc Active Record.
  • Khó áp dụng Domain Model thuần túy vì bị lệ thuộc vào tầng dưới (DAL).

🔄 3. Ports & Adapters Architecture

Giải quyết nhược điểm của Layered Architecture bằng cách đảo ngược sự phụ thuộc giữa business logic và infrastructure:

  • Business Logic (core) ở trung tâm, không phụ thuộc vào thành phần kỹ thuật (DB, UI).
  • Infrastructure Layer chịu trách nhiệm triển khai kỹ thuật.
  • Application Layer (service layer) làm trung gian, điều phối logic.

Thực hiện nguyên tắc Dependency Inversion (DIP):
👉 High-level modules (business logic) không phụ thuộc low-level modules (infrastructure).

Ports & Adapters:

  • Ports: Các interfaces định nghĩa ở core (business logic).
  • Adapters: Các lớp cụ thể triển khai interfaces trong infrastructure.

Ví dụ Ports & Adapters:

// Port (business logic)
interface IMessaging {
    void Publish(Message payload);
    void Subscribe(Message type, Action callback);
}

// Adapter (infrastructure)
class SQSBus: IMessaging { ... }

Khi nào sử dụng?

  • Phù hợp triển khai Domain Model vì giúp logic nghiệp vụ không phụ thuộc kỹ thuật.

📐 4. Command-Query Responsibility Segregation (CQRS)

CQRS chia tách logic xử lý dữ liệu (command) và truy vấn dữ liệu (query) riêng biệt:

  • Command Execution Model: Thực thi các thay đổi, quản lý trạng thái chính xác.
  • Read Models: Các projections chỉ dùng để đọc và truy vấn dữ liệu.

📌 Các kiểu Projection:

  • Synchronous: Dựa trên checkpoint từ DB để cập nhật.
  • Asynchronous: Dựa trên message bus để cập nhật dữ liệu.

📌 Lưu ý:

  • Command vẫn có thể trả về thông tin, miễn là từ model nhất quán (strongly consistent).
  • CQRS phù hợp với các hệ thống yêu cầu nhiều loại lưu trữ khác nhau (polyglot persistence).

🔍 5. Scope của các Architectural Patterns

Các mẫu kiến trúc này không nên áp dụng cứng nhắc cho toàn hệ thống hay toàn bộ bounded context:

  • Có thể áp dụng vertical slicing (chia theo subdomains).
  • Không nhất thiết mọi subdomain dùng cùng một mẫu kiến trúc.
  • Tránh trở thành Big Ball of Mud (hỗn loạn khó quản lý).

📌 6. Tổng kết các mẫu kiến trúc:

Mẫu kiến trúcĐặc điểm chínhPhù hợp khi nào?
LayeredPhụ thuộc từ trên xuống dướiActive Record, Transaction Script
Ports & AdaptersCore business logic độc lập ở trung tâmDomain Model
CQRSTách biệt command và query modelsEvent sourcing, polyglot modeling

📋 Câu hỏi củng cố kiến thức

  1. Pattern nào phù hợp Active Record?
    • ✅ Layered Architecture
  2. Pattern nào tách biệt logic nghiệp vụ khỏi hạ tầng?
    • ✅ Ports & Adapters, CQRS
  3. Adapter cho message bus nên đặt ở đâu?
    • ✅ Infrastructure layer
  4. Câu nào đúng về CQRS?
    • ✅ Command có thể trả về dữ liệu từ strongly consistent model.
    • ✅ Có thể dùng đồng thời synchronous và asynchronous projections.

Một bình luận cho “DDD: Phần 8 – Các pattern kiến trúc trong Domain-Driven Design”

  1. […] Xem lại phần 8 tại: DDD: Phần 8 – Các pattern kiến trúc trong Domain-Driven Design […]

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