Domain Driven Design là gì?
Có nhẽ tất cả chúng ta đã quá thân thuộc với cách tiếp cận truyền thống khi xây dựng một ứng dụng. Đầu tiền tất cả chúng ta đọc spec và tìm hiểu các chức năng, sau đó tiến hành chia nhỏ các task. Trong phần lớn trường hợp, việc này nhằm mục tiêu estimate thời kì và lên kế hoạch thực hiện cho những task này. Vậy trình tự công việc sẽ là estimate thời kì, chia việc cho những thành viên trong team, thiết kế cơ sở tài liệu, cuối cùng là bắt tay và code. Đây là cách thiết kế hướng tài liệu hay còn gọi là Data Driven Design.
OK? Vậy vấn đề với cách tiếp cận là gì nhỉ? Lâu này tất cả chúng ta vẫn tiếp cận Theo phong cách này và vẫn thực hiện tốt đấy chứ? Lời đáp là Đúng và Sai. Đúng tại phần tất cả chúng ta vẫn thực hiện tốt trong việc chuyển nhượng bàn giao project, Sai tại phần tất cả chúng ta chưa thực hiện tốt trong việc bảo trì và mở rộng project.
Trong các ứng dụng tiêu biểu có rất nhiều phần code xử lý các task không liên quan đến logic nghiệp vụ như truy cập file, mạng hay database, các phần này thường được gọi là plumping code và được nhúng trực tiếp vào trong Business Object và nhiều Business Logic cũng được nhúng vào behavior của UI Widget hay script của database, điều này thường xẩy ra vì nó làm tất cả chúng ta phát triển ứng dụng một cách nhanh chóng và dễ dàng. Việc này dẫn đến phần lớn thời kì phát triển ứng dụng của developer là giành riêng cho việc viết các plumping code thay vì viết Business Logic thực sự, nó làm cho thiết kế của tất cả chúng ta bị mất đi tính hướng đối tượng người dùng trong thực tế.
Ngoài ra khi logic nghiệp vụ trộn lẫn với layer khác khiến cho việc đọc hiểu và suy nghĩ về logic của ứng dụng trở thành khó khăn hơn so với những người dân ngoài. Chỉ một thay đổi nhỏ tại tầng UI cũng luôn tồn tại thể dẫn tới việc thay đổi tầng logic và trái lại khi thay đổi một business rule của ứng dụng yên cầu tất cả chúng ta phải quan tâm đến từng rõ ràng và cụ thể nhỏ phía UI cũng như Database để đáp ứng được sự thay đổi này.
Trong những ứng dụng nhỏ thì vấn đề này tất cả chúng ta không nhìn thấy. Ở các ứng dụng cỡ vừa thì vấn đề này đã tồn tại và mở màn dẫn đến tình trạng phá vỡ các thiết kế chuẩn (Anti-pattern). Và so với các ứng dụng lớn thì nó trở thành vấn đề nghiêm trọng, cách tiếp cận trên sẽ không còn thể cho tất cả chúng ta đưa ra một thiết kế hướng đối tượng người dùng chuẩn xác. Giải pháp ở đây đây chính là Domain Driven Design (DDD).
Vậy DDD là gì? DDD không liên quan gì đến công nghệ hay framework là những thứ thuộc về tầng vật lý mà nó là một khái niệm thuộc về tầng logic khi tất cả chúng ta xây dựng một khối hệ thống phần mềm. Cụ thể hơn nó là một design pattern và hơn nữa đây là design pattern ở Lever kiến trúc của khối hệ thống (bạn cũng có thể tìm hiểu thêm trong cuốn Patterns of Enterprise Application Architecture của Martin Fowler), tất cả chúng ta cần rõ điều này để phân biệt với những design pattern nổi tiếng ở Lever class. Nó cung cấp một cấu trúc thực hiện (structure of practice) và các thuật ngữ (terminology) cho việc ra quyết định thiết kế nhằm tập trung và tăng tốc các dự án phần mềm trong các nghành nghề phức tạp.
DDD được giới thiệu lần trước tiên bởi Eric Evans, trong cuốn sách của ông với tựa đề: Domain-Driven Design – Tackling Complexity in the Heart of Software (Addison-Wesley Professional 2003). DDD là cách tiếp cận đến phát triển phần mềm được cho phép các team quản lý cấu trúc và bảo tri phần mềm trong những nghành nghề có độ phức tạp lớn.
Xây dựng tri thức chung về domain
Để tạo ta một phần mềm tốt, bạn cần phải hiểu về phần mềm đó. Bạn không thể làm ra khối hệ thống phần mềm nhà băng nếu trừ khi chúng ta có hiểu biết tương đối tốt về mảng nhà băng và những điều liên quan. Tức là, để làm phần mềm tốt, bạn cần phải hiểu nghành nghề nhà băng.
Liệu có thể làm được phần mềm nhà băng phức tạp dù không có hiểu biết nghiệp vụ tốt? Không thể. Không bao giờ. Ai hiểu về banking? Người thiết kế phần mềm? Không. Đồng chí này chỉ tới nhà băng để gửi tiền và rút tiền khi cần. Người phân tích phần mềm? Cũng không hẳn. Anh ta chỉ biết phân tích một chủ đề cụ thể khi anh ta có đầy đủ tất cả cấu phần. Lập trình viên? Quên chuyện đó đi. Vậy là ai? Viên chức nhà băng, hiển nhiên. Hiểu nhất về khối hệ thống nhà băng là những người dân ở trong đó, những Chuyên Viên của họ. Họ hiểu mọi thứ rõ ràng và cụ thể, cái hay-dở, mọi vấn đề có thể và mọi quy định. Đây là nơi tất cả chúng ta thường xuất phát: Ngành nghề (domain).
Giả sự bạn cần phải xây dựng một khối hệ thống phần mềm quản lý bệnh viện. Rõ ràng bạn cần phải thao tác với hàng ngũ thầy thuốc, y tá (đây chính là các Chuyên Viên trong nghành nghề này – domain expert) để xây dựng tri thức về domain. Bạn và họ nói chuyện, trao đổi tri thức, đặt vướng mắc và trả lời. Bạn cần phải làm rõ càng nhiều càng tốt về domain này. Bằng phương pháp đặt vướng mắc đúng, xử lý thông tin đúng cách, bạn và Chuyên Viên sẽ dần dần vẽ ra một domain, một mô hình domain (domain model). Bạn là kỹ sư phần mềm, kết phù hợp với domain expert cùng tạo nên một domain model và mô hình đó là nơi tri thức kinh nghiệm của tất cả hai bên được phối hợp và tổng hợp lại.
Hãy xem xét tiếp ví dụ sau. Giả sử bạn đang tham gia thiết kế một tòa nhà. Yêu cầu là:
- Xây dựng trên một diện tích S đất nhất mực
- Tòa nhà cao 6 tầng
- Mỗi tầng có 4 nhà tại
Vậy domain ở đây là gì? Là dự án Bất Động Sản xây dựng chăng? Cũng luôn tồn tại thể. Nhưng nếu như khách hàng xem dự án Bất Động Sản xây dựng là domain của bạn, thì có thể bạn đang bỏ qua một vài rõ ràng và cụ thể trong yêu cầu. Khu công trình xây dựng bạn đang thiết kế phải gồm có thiết kế nhà tại cho tất cả những người dân số sống. Vậy thuật ngữ “dự án Bất Động Sản xây dựng” có thể khiến tất cả chúng ta bỏ lỡ rõ ràng và cụ thể, thay vì đó ta có thể thu hẹp xuống thành “chung cư”. Lúc này nếu như khách hàng nói với những kỹ sư về việc thiết kế, rõ ràng thuật ngữ “chung cư” sẽ dễ hiểu hơn là “dự án Bất Động Sản xây dựng” thuần tuý. Bạn thấy đấy, chỉ một thay đổi nhỏ trong ngôn từ cũng luôn tồn tại thể tạo nên sự khác biệt. Trở lại ví dụ phần mềm bệnh viện ở trên, bạn và các thầy thuốc, y tá không thể nói cùng một tiếng nói được. Họ nói về từ ngữ kinh nghiệm, bạn nói bằng đối tượng người dùng, phương thức, quan hệ. Đó đây chính là lúc tất cả chúng ta cần một tiếng nói chung để cả hai bên có thể thao tác với nhau dễ dàng hơn.
Ubiquitous Language
Khái niệm rất đơn giản, bạn và domain expert phải cùng nói một tiếng nói chung để cùng hiểu đúng một vấn đề.
Ví dụ về Ubiquitous Language.
Sai: Tỉ lệ chiều dài, chiều rộng của một phòng ngủ cỡ nhỏ là 4:3. Đúng: Chiều dài phòng ngủ trẻ em là 6m, chiều rộng là 4.5m.
Rõ ràng những từ như phòng ngủ cỡ nhỏ, tỉ lệ thiên hướng kỹ thuật. Phòng ngủ trẻ em sẽ dễ hiểu hơn, và một số đo cụ thể rõ ràng có ý nghĩa và dễ hình dung hơn.
Kiến trúc phân lớp
Như đã nói ở trên, lúc các đoạn code liên quan đến nghiệp vụ được trộn lẫn giữa các tầng lại với nhau, nó trở thành vô cùng khó khăn cho việc đọc cũng như suy nghĩ về chúng. Các thay đổi ở giao diện người dùng cũng luôn tồn tại thể thực sự thay đổi cả logic nghiệp vụ. Để thay đổi logic nghiệp vụ có thể yêu cầu tới truy vết tỉ mỉ các đoạn mã của giao diện người dùng, CSDL, hoặc các thành phần khác của Khóa học. Mô hình phát triển hướng đối tượng người dùng trở thành phi thực tế. Do đó, hãy phân chia một Khóa học phức tạp thành các LỚP. Phát triển một thiết kế cho từng LỚP để chúng trở thành gắn kết và chỉ phụ thuộc vào các tầng phía dưới. Sau đây là giải pháp kiến trúc chung cho 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 tắc Seperation of Concerns. Các phần logic xử lý khác nhau sẽ tiến hành cô lập thoát 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 tới 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ẽ tiến hành 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 sinh hoạt 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 đa số đối tượng người dù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. Tất cả chúng ta có thể hình dung phần này gần giống với những 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 nghành nghề. Đây đây chính là trái tim của phần mềm. Trạng thái của đối tượng người dùng nghiệp vụ được giữ tại đây. Infrastructure Layer: Tầng này đóng vai trò như một thư viện tương trợ cho tất cả những tầng sót 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 người dùng nghiệp vụ, chứa các thư viện tương trợ cho tầng giao diện người dùng…
Đến đây thì tất cả 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.
Tất cả chúng ta có thể thấy là trong mô hình 3 lớp thì tầng trên sẽ phụ thuộc trực tiếp vào tầng dưới nên không thể truy cập tài liệu một cách trực tiếp từ tầng Presentation sang tầng Data Access Layer mà không thông qua tầng Business Layer. Còn mô hình DDD thì từ tầng User Interface nếu muốn lưu cái gì đó vào trong database chẳng hạn nó có thể gọi trực tiếp xuống tầng Infrastructure để làm được việc đó. Rõ ràng là trong kiến trúc DDD thì tính lose coupling được đảm bảo tốt hơn. Có thể hình dung một cách trực quan là mô hình 3 lớp giống như một ngôi nhà 3 tầng chỉ có cầu thang bộ, và việc vận chuyển giữa tầng một và tầng 3 cần đi qua sàn tầng 2, trong những khi mô hình DDD giống như ngôi nhà 4 tầng có lắp thêm thang máy, tất cả chúng ta có thể vận chuyển tới các tầng khác nhau một cách tự do hơn.
OK. Nội dung bài viết này mình tạm ngừng ở phần kiến trúc của DDD. Phần sau mình sẽ trình bày về Building Blocks, tương tự như những viên gạch để xây hình thành kiến trúc này.
Thank you for reading.
Tham khảo:
- Domain-Driven Design – Tackling Complexity in the Heart of Software – Eric Evans (Addison-Wesley Professional 2003)
- Patterns, Principles and Practices of Domain-Driven Design – 1st Edition – Scott Millett & Nick Tune – (Wrox 2015)
- https://www.infoq.com/minibooks/domain-driven-design-quickly
- http://www.codeproject.com/Articles/339725/Domain-Driven-Design-Clear-Your-Concepts-Before-Yo
- http://sotatek.com/domain-driven-design-la-cai-gi/
- https://github.com/vuhung/ddd-quickly-vietnamese