Giới thiệu Domain-Driven Design

Photo by Clemens van Lay on Unsplash

Bài viết này là phần thứ nhất nằm trong blog series khai phá Domain-Driven Design. Bạn có thể bấm vào đây để xem danh sách toàn bộ các bài viết.

Trong phần này:

  • Sơ lược về DDD
  • Loạn bàn về Domain trong DDD
  • Chém gió về Model
  • Sự cần thiết của bộ ngôn ngữ thống nhất
  • Tản mạn chuyện design
  • Lời kết

Sơ lược về DDD

Không phải là một thứ gì đó cao siêu, phần mềm là phương tiện/công cụ được tạo ra nhằm mục đích cuối cùng là giúp người dùng giải quyết vấn đề đơn giản hoặc phức tạp trong cuộc sống hàng ngày.

Cũng giống như xây nhà thì cần kiến trúc sư, làm phần mềm cần một SA (software architect) — người sẽ thiết kế kiến trúc logic của phần mềm để chuyển thể yêu cầu nghiệp vụ thực tế thành mã nguồn phần mềm. Dân trong ngành thường hiểu ngầm với nhau, SA đôi lúc làm việc như một nghệ sĩ — không có công thức hay lý thuyết tuyệt đối nào có thể áp dụng trong quá trình làm phần mềm, bạn chỉ có thể học hỏi những nguyên lý và kỹ thuật để từ đó áp dụng chúng và tự chiêm nghiệm trong quá trình phát triển phần mềm nhằm đúc kết những trải nghiệm riêng cho bản thân mình.

Từ cùng yêu cầu của bài toán thực tế, gần như mỗi SA sẽ có một con đường khác nhau để tạo ra phần mềm nhằm giải quyết bài toán đó. Trong gần 20 năm qua, ngành công nghiệp phần mềm đã đúc rút ra rất nhiều phương pháp design software (thiết kế kiến trúc phần mềm) với những ưu/nhược điểm riêng. DDD là một trong những phương pháp như vậy. Tuy đã ra đời từ khá lâu, nhưng trong vài năm trở lại đây DDD mới thực sự đạt được sự kết tinh một cách rành mạch và toàn diện cả trên phương diện lý thuyết lẫn thực hành bởi cộng đồng những SA lão làng trên thế giới (nổi tiếng nhất phải kể đến Eric Evans và Martin Fowler), từ đó giúp mở ra cánh cửa cho tầng lớp SA bình dân (như mình chẳng hạn 😃) tiếp cận DDD một cách dễ dàng hơn.

Loạn bàn về Domain trong DDD

Như đã đề cập ở trên, phần mềm là phương tiện nhằm giải quyết các vấn đề thực tế trong cuộc sống, là giải pháp nhằm tự động hóa các quy trình nghiệp vụ nào đó trong đời sống hàng ngày. Chính tất cả các vấn đề thực tếquy trình nghiệp vụ mà phần mềm được sinh ra để giải quyết nói trên là domain (lĩnh vực) của phần mềm.

Hầu như mọi nhân viên trong công ty software đều biết phần mềm được tạo ra từ những dòng code (mã nguồn), các coder (lập trình viên) thường dành thời gian quá nhiều cho những dòng code, nhiều đến nỗi họ sẽ coi phần mềm là tập hợp của những objects và methods mà quên mất đi domain — phần hồn của phần mềm.

Hãy thử nghĩ, liệu bạn có thiết kế/phát triển được một phần mềm banking tốt/phức tạp trong khi bản thân không hiểu gì về domain banking (kiến thức trong lĩnh vực ngân hàng)? Chắc chắn là không rồi. Vậy ai là người hiểu nhất về domain banking? Chắc chắn không phải là SA, coder hay tester mà người này chính là banker (nhân viên ngân hàng). Trong DDD, người này được gọi chung là domain expert, họ là người hiểu domain một cách tường tận — chi tiết, họ hiểu các vấn đề tiềm ẩn có thể phát sinh và các quy luật vận hành trong domain… Tóm lại, khi bắt đầu dự án phần mềm, team phát triển phải có sự cộng tác chặt chẽ với domain expert để hiểu thật rõ về domain mà phần mềm sẽ hỗ trợ. Sự cộng tác này phải được duy trì liên tục trong vòng đời phát triển phần mềm.

Software has to model the domain.

Để giúp phần mềm có khả năng thay đổi linh hoạt đáp ứng những yêu cầu nghiệp vụ mới trong tương lai, DDD đề xuất rằng phần mềm được làm ra phải phản ánh được đúng hiện trạng của domain tại thời điểm phát triển. Cụ thể hơn, phần mềm phải hợp nhất các core concept (khái niệm cốt lõi) của domain và phải mô phỏng được chính xác relationship (mối liên hệ) giữa các core concept này. Nói cách khác, mã nguồn phần mềm phải đạt đến ngưỡng trở thành một dạng tài liệu mô hình hóa kiến thức về domain.

Lấy ví dụ về việc team phát triển có thành viên mới tham gia dự án cho kịp deadline và bạn này chưa hề có kiến thức về domain của phần mềm trước khi tham gia. Nếu như bạn coder này có thể hiểu được nghiệp vụ chỉ bằng việc ngồi xem mã nguồn thì xin chúc mừng, phần mềm của bạn đã đạt đến cái ngưỡng mà DDD đề xuất.

Chém gió về Model

Okay, sau khi có kiến thức về domain (tất nhiên là lượng kiến thức vừa đủ để phát triển các tính năng cần thiết trên phần mềm chứ không fải toàn bộ kiến thức về domain) thì bước tiếp theo sẽ là gì? Chắc chắn bạn không thể typing những kiến thức này vào IDE để biến chúng thành code. Giờ là lúc bạn cần đến SA, người sẽ nhào nặn những kiến thức về domain nói trên một cách chọn lọccó tổ chức thành bản thiết kế trừu tượng hóa phù hợp cho việc coding. DDD gọi bản thiết kế này là model/model of domain (mô hình trừu tượng hóa của domain).

Trong giai đoạn khởi đầu dự án, model hầu như sẽ không phản ánh một cách toàn diện về domain, nhưng càng về sau model này sẽ ngày càng hoàn thiện, kết tinh, sàng lọc và rõ ràng. Xin lưu ý rằng: model này không phải là một diagram (biểu đồ) cụ thể nào đó (developer thường hay nhầm với UML sequence diagram, object diagram…), về bản chất domain model chính là idea mà diagram muốn truyền tải. Hay nói cách khác, team phát triển dùng diagram làm phương tiện để diễn đạt và trao đổi về model.

The model is our internal representation of the target domain, and it is very necessary throughout the design and the development process.

Về phần model, nó chính là phương tiện diễn đạt/phản ánh về domain. Đôi khi SA sẽ phải lược bỏ vài concept của domain trong model nhằm giữ cho model tinh gọn nhất có thể mà vẫn đảm bảo việc phát triển các tính năng của phần mềm. Model đóng vai trò quan trọng trong quá trình thiết kế và phát triển phần mềm. Lấy ví dụ về domain banking, lĩnh vực có rất nhiều kiến thức sâu rộng về tài chính. Banking software chắc chắn cần lưu trữ địa chỉ của khách hàng, nhưng chắc sẽ không quan tâm đến chiều cao/cân nặng của người này… Đây là phần khó nhất trong quá trình design software, SA sẽ cân đong đo đếm cho việc giữ cái gì và bỏ cái gì ra khỏi domain model vì trên thực tế điều này thường không hiển nhiên như ví dụ đơn giản nói trên.

Sự cần thiết của bộ ngôn ngữ thống nhất

Giả sử SA đã design ra model phù hợp cho domain, giờ là lúc SA mang model này ra để trao đổi, thảo luận, chia sẻ kiến thức & thông tin với team phát triển. DDD nhấn mạnh rằng, quá trình giao tiếp này phải diễn ra một cách chính xác — đầy đủ — rõ ràng. Đây là điều rất quan trọng vì bản thân mình đã từng tham gia những cuộc trao đổi về việc phát triển phần mềm với sales team mà sau khi ra khỏi phòng họp các coder thường nói rằng: “trước khi họp thì em hơi hiểu hiểu mình phải code gì, sau khi họp xong thì em hết hiểu… 😞”.

Có nhiều cách tiến hành việc giao tiếp: thông qua biểu đồ, thông qua tài liệu viết lách hoặc thông qua trao đổi trực tiếp. DDD cho rằng trao đổi trực tiếp là quan trọng trong việc chia sẻ và chuẩn hóa model, vì vậy DDD đề xuất tạo ra bộ ngôn ngữ chuyên dùng cho việc này, nó được gọi là ubiquitous language.

Tản mạn chuyện design

Sau khi team phát triển chốt được domain model, giờ là lúc chuyển sang giai đoạn tiếp theo của quá trình desgin & develop: giai đoạn “code design”. Xin dừng lại một chút để phân biệt hai khái niệm software design — code design:

Software design:

  • thiết kế kiến trúc tổng thể của hệ thống phần mềm (phần mềm gồm bao nhiêu module, các module liên kết với nhau thế nào…)
  • Đóng vai trò quan trọng hơn code design
  • Chi phí để sửa đổi sẽ đắt đỏ hơn code design

Code design:

  • thiết kế chi tiết cho việc coding module trong hệ thống phần mềm (thường liên quan đến việc áp dụng code design pattern khi viết mã nguồn module)
  • Cũng quan trọng trong việc bảo trì, nhưng không quan trọng bằng software design
  • Chi phí để sửa đổi ít đắt đỏ hơn so với software design

Có nhiều phương pháp làm software design, một trong số đó phải kể đến phương pháp truyền thống Waterfall. Mô hình này thường phù hợp để phát triển hệ thống phần mềm nhỏ, nó chia quá trình phát triển thành nhiều chặng: KH đưa ra business requirement (yêu cầu nghiệp vụ) cần đáp ứng, BA (business analyst) làm việc với KH để xác định rõ, chuẩn hóa và thống nhất business requirement để chuyển sang SA. SA từ business requirement đã chuẩn hóa, thiết kế ra model phù hợp rồi chuyển sang developer để tiến hành các bước tiếp theo. Đây là mô hình một chiều, nhược điểm lớn nhất của nó là không có sự feedback ngược từ cấp dưới lên cấp trên (hoặc có thì cũng mất nhiều thời gian để thay đổi), và rất khó để áp dụng trong bối cảnh yêu cầu nghiệp vụ và công nghệ thay đổi quá nhanh như hiện nay. Bên cạnh đó, yêu cầu thu thập và chuẩn hóa business requirement ngay từ giai đoạn đầu của dự án cũng là việc khó, gây tốn kém thời gian và nhân lực.

Một phương thức khác phải kể đến là Extreme Programming (XP — một trong những phương thức Agile). Nhận thấy khó khăn và tốn kém trong việc thu thập đầy đủ business requirement cũng như trong việc thiết kế một model hoàn hảo trong giai đoạn đầu của dự án, XP chủ trương giảm thiểu việc thiết kế để tập trung nguồn lực cho việc coding sao cho ra đời sản phẩm nhanh nhất. Từ sản phảm đó, KH dùng thử sẽ liên tục đưa ra feedback, team developer liên lục refactor (tái cấu trúc) lại phần mềm để đáp ứng yêu cầu KH. Phương thức này cũng có nhược điểm: việc liên lục refactor mà không dựa trên một nền tảng model được design cẩn thận sẽ dẫn đến khó khăn trong việc làm chủ và bảo trì mã nguồn sau này.

Good design will accelerate the development, while feedback coming from the development process will enhance the design.

Với việc áp dụng DDD, giai đoạn phát triển phần mềm (dù theo phương thức Agile hay Waterfall…) sẽ có được bổ trợ đáng kể để làm tăng khả năng mô hình hóa domain và tạo ra phần mềm có tính dễ dàng bảo trì và khả năng thay đổi linh hoạt trước những yêu cầu nghiệp vụ mới. Trong DDD, sự song hành của quá trình design và develop được nhất mạnh sẽ giúp tạo ra những phần mềm chất lượng. Design tốt sẽ giúp tăng tốc quá trình develop, trong khi feedback từ quá trình develop sẽ giúp cải tiến design.

Lời kết

Phần 1 xin được dừng tại đây, tới thời điểm này có vài điều cần ghi nhớ khi bắt đầu một dự án software với công cụ DDD

  • Kiến thức về domain là quan trọng và trong team phát triển nhất thiết phải có mặt domain expert
  • Trước khi bắt tay vào code, cả team cần thiết kế ra một model phù hợp với domain của phần mềm và duy trì sự phù hợp của model trong suốt vòng đời của dự án

Tất nhiên cái gì cũng có giá của nó, việc đầu tư vào DDD sẽ đòi hỏi thời gian và công sức của team phát triển ở giai đoạn đầu của dự án (sự tham gia của domain expert, SA có kỹ năng abstract và modeling ở mức độ chuyên nghiệp, dự án cho phép quá trình iteration để refinemodel…). DDD khuyến cáo không nên áp dụng phương pháp này với những dự án không quá phức tạp về mặt nghiệp vụ.

Backend Leader @ Pingcom, Runner