Domain-Driven Design

DDD

Posted by Box XV on July 2, 2024. 7 min read.

Mục lục

Trong thiết kế kiến trúc Microservice, có thể bạn sẽ được nghe nhiều về thuật ngữ Domain Driven Design, một phương pháp thiết kế phần mềm theo hướng business domain. Phương pháp này được giới thiệu bởi Eric Evan, viết thành một cuốn sách dày cộp bán trên Amazon.

Vì sao lại đẻ ra DDD (Domain Driven Design)

Giả sử một anh dev được giao nhiệm vụ phát triển một ứng dụng bán hàng online. Thanh niên coder thì sẽ áp dụng mô hình quen thuộc MVC hay MVVM

MVC

Nhìn source code thì kiểu thế này

MVC

OK, cool, tuy nhiên đem cấu trúc này cho một BA (Business Analysis) thì anh ta lại không hiểu controller, view là cái giề. Anh ấy chỉ biết về nghiệp vụ bán hàng, order online, thanh toán thì đi theo flow abc, xyz. Còn chú coder thì biết mỗi Java, .NET design pattern. Kiểu nhưng 1 anh tàu nói tiếng Chinese với 1 anh US. Như vậy phải có 1 phương pháp nào đó để anh dev và anh BA có thể ngồi được với nhau sử dụng 1 ngôn ngữ chung để cả 2 có thể hiểu, và Domain Driven Design ra đời.

DDD là gì

Domain-Driven Design (DDD) là một phương pháp tiếp cận thiết kế phần mềm tập trung vào việc mô hình hóa vấn đề kinh doanh (domain) phức tạp và diễn đạt nó qua mã nguồn. .NET cung cấp những công cụ mạnh mẽ để hỗ trợ việc áp dụng DDD.

DDD không phải là công nghệ gì cả, nó là một design pattern, một phương pháp thiết kế phần mềm, nhìn trên góc độ architect. Trong đó ứng dụng sẽ được chia thành 4 layer như ở dưới

DDD

Kiến trúc phân lớp

Phân chia một chương trình phức tạp thành các LỚP. Phát triển một thiết kế cho mỗi LỚP để chúng trở nên gắn kết và chỉ phụ thuộc vào các tầng bên dưới. Dưới đây là giải pháp kiến trúc chung cho DDD.

DDD

Ở đây mô hình DDD vẫn giữ lại những ưu điểm của mô hình kiến trúc phân lớp (Layered Archiecture) để đảm bảo nguyên lý Seperation of Concerns. Các phần logic xử lý khác nhau sẽ được cô lập ra khỏi các phần khác làm tăng tính Lose Coupling của ứng dụng và tính dễ đọc và dễ bảo trì cũng như ứng dụng khi có thay đổi logic của từng layer thì không ảnh hướng đến các layer khác.

  • User Interface: Chịu trách nhiệm trình bày thông tin tới người sử dụng và thông dịch lệnh của người dùng. Có thể hiểu là các sự kiện xảy ra trên giao diện khi được trigger sẽ được dịch thành lệnh và xử lý ở các tầng dưới.

  • Applicatioin Layer: Tầng này được thiết kế khá mỏng (ít xử lý logic) phối hợp các hoạt động của ứng dụng. Nó không chứa logic nghiệp vụ. Nó không lưu giữ trạng thái của các đối tượng nghiệp vụ nhưng nó có thể giữ trạng thái một tiến trình của ứng dụng. Chúng ta có thể hình dung phần này gần giống với các Controller trong mô hình MVC chỉ làm nhiệm vụ chuyển tiếp các task đến nơi cần xử lý.

  • Domain Layer: Tầng này chứa thông tin về các lĩnh vực. Đây chính là trái tim của phần mềm. Trạng thái của đối tượng nghiệp vụ được giữ tại đây.

  • Infrastructure Layer: Tầng này đóng vai trò như một thư viện hỗ trợ cho tất cả các tầng còn lại. Nó cung cấp thông tin liên lạc giữa các lớp, cung cấp chức năng lưu trữ các đối tượng nghiệp vụ, chứa các thư viện hỗ trợ cho tầng giao diện người dùng…

User Interface Layer: làm nhiệm vụ biểu diễn thông tin trực quan cho user và dịch các user command ( ở đây chúng ta có thể hiểu là các event xảy ra trên giao diện khi được trigger ( nhấn nút trên các UI input control ) là các sẽ được dịch thành các command xử lý ở các tầng dưới.

Application Layer: Tầng này được thiết kế khá mỏng ( thin ) với ít logic xử lý chỉ để làm nhiệm vụ coordinate các Activity của Application và không chứa Business Logic, nó không chứa state của các Business Object mà chỉ chứa state của Application Task Progress. Chúng ta có thể hình dung phần này gần giống với các Controller trong mô hình MVC chỉ làm nhiệm vụ forward các task đến nơi cần xử lý.

Domain Layer: Đây là trái tim của ứng dụng ( Business Software ), các state của Business Object đều nằm ở đây. Việc lưu trữ ( persistence ) các Business Object và các state của nó được chuyển giao cho tầng Infrastructure ở dưới. Trái tim của mô hình này chính là ở phần Domain Layer, các nghiệp vụ sẽ được mô tả tại đây, và cấu trúc source code cũng được tổ chức theo tên các nghiệp vụ chứ không để kiểu view, controller như truyền thốngChẳng hạn như Order App thì chia thành thư mục hướng nghiệp vụ như Buyer, Order… Như vậy anh BA nhìn vào có thể hiểu ngay là app này có chức năng gì

Infrastructure Layer: Đóng vai trò cung cấp thư viện ( supporting libraries ) cho các tầng khác. Nó cung cấp cơ chế giao tiếp ( communication ) giữa các Layer với nhau, cũng như cung cấp các chức năng khác như lưu trữ ( persistence ) các Business Object của tầng Domain.Cái tầng Infrastructure Layer dễ làm người đọc lầm lẫn sang hạ tầng phần cứng như máy chủ, database server, nhưng thật ra không phải, do chú tây viết sách thích tỏ ra nguy hiểm. Còn trong DDD, layer này có thể hiểu như tầng common library như logging, utility và phần data persistent layer của tầng Data Access trong mô hình thông thường, tức là vẫn chỉ là phần mềm

DDD

Đến đây thì chúng ta sẽ thấy kiến trúc của DDD tuy mới nhìn có vẻ lạ nhưng chỉ đơn giản là nó tùy biến lại mô hình kiến trúc 3 lớp (3-tier architecture) cho linh hoạt hơn. Tính linh hoạt này được tạo ra từ hệ quả của việc tái tổ chức lại các layer từ mô hình ba lớp, nó thể hiện ở data flow và control flow giữa 2 mô hình.

1. Xác định Ubiquitous Language:

Ngôn ngữ đồng nhất (Ubiquitous Language) là một tập hợp các từ ngữ được chia sẻ giữa nhóm phát triển và các bên liên quan để mô tả vấn đề kinh doanh. Nó giúp đảm bảo rằng mọi người đều hiểu đúng về domain và nó được diễn đạt qua mã nguồn.

2. Mô hình hóa Entities, Value Objects, và Aggregates:

Entities là các đối tượng có danh tính duy nhất. Value Objects là những đối tượng không có danh tính nhưng được định nghĩa qua các thuộc tính của nó. Aggregates là một tập hợp của các entities và value objects được tổ chức lại thành một nhóm.

3. Sử dụng Repositories và Services:

Repositories là những đối tượng giúp truy xuất dữ liệu từ nguồn lưu trữ, thường là cơ sở dữ liệu. Services là nơi đặt logic kinh doanh mà không thuộc về một entity cụ thể nào.

4. Sử dụng Domain Events:

Domain Events là những sự kiện xảy ra trong domain, thường liên quan đến việc thay đổi trạng thái của một entity hoặc aggregate. Chúng giúp bạn thiết kế các hệ thống phản ứng và không đồng bộ.

Ví dụ minh họa: Trong một ứng dụng quản lý bệnh viện, “Bệnh nhân”, “Bác sĩ”, và “Hóa đơn” có thể là các Entities, trong khi “Địa chỉ” có thể là một Value Object. Các hành động như “Tiếp nhận bệnh nhân”, “Chẩn đoán bệnh”, v.v. có thể được xử lý trong các Services. Khi một bệnh nhân được tiếp nhận, một “Bệnh nhân được tiếp nhận” (PatientAdmitted) Domain Event có thể được phát đi. Event này có thể kích hoạt một loạt các hành động khác như gửi thông báo tới bác sĩ chăm sóc, cập nhật thông tin bệnh nhân vào hệ thống, v.v. Tất cả những khái niệm này giúp mã nguồn phản ánh chính xác những gì đang diễn ra trong vấn đề kinh doanh thực tế, làm cho mã nguồn dễ đọc, dễ bảo dưỡng, và dễ mở rộng.

Clean Architecture

Kết luận

Microservice đang là một xu hướng lập trình chủ chốt cho mọi ứng dụng lớn và DDD chính là khởi đầu cho việc triển khai microservice. Mặc dù bài viết chưa thực sự tạo ra sức móc cũng như việc triển khai microservice bằng DDD tuy nhiên đó là một câu chuyện khác và nó cần một bài viết khác, và bài này không có liên quan. Đó có lẽ mà một mảng cao hơn mà mình sẽ hướng tới để viết tiêp. Tuy nhiên với bài viết này, hi vọng các bạn có cái nhìn sơ về DDD và các thành phần cốt lõi của nó. Vì chỉ là cốt lỗi nên chắc chắn còn nhiều thành phần khác chưa được đề cập tới. Một mặt cũng vì kiến thức của mình còn hạn chế, chưa có dịp làm việc trực tiếp với DDD nên qua đây cũng mong nhận được nhiều góp ý, trao đổi từ các bạn để có có thể nắm rõ hơn về DDD.

Cảm ơn các bạn đã dành thời gian đọc bài viết này. Chúc một ngày tốt lành.