Home » Design » Domain-Driven Design » Domain Driven Design cho mọi người

Domain Driven Design cho mọi người

Lời giới thiệu

ddd book
Chào các bạn! Sau hơn 1 năm làm việc với DDD, tôi thấy DDD thật là một tư duy mới về cách xây dựng phần mềm. Nhận thấy phương pháp thiết kế này còn khá mới mẻ ở Việt nam, nên tôi xin chia sẻ tổng hợp cơ bản về DDD dành người quan tâm. Bạn có thể là developer, SQA, managers, PO.. miễn là bạn liên quan đến phát triển xây dựng hệ thống phức tạp thì DDD đều có thể cho bạn những gợi ý hữu ích.
Do kiến thức còn hạn hẹp cũng như nhiều góc nhìn chủ quan có thể thiếu sót, mong nhận được góp ý của các bạn để bài viết được bổ xung hoàn thiện hơn.


1. DDD là gì


1.1 Khái niệm


Domain-driven design (DDD) là một cách tiếp cận cho phát triển các phần mềm phức tạp; sự phức tạp ở đây là do nghiệp vụ của lĩnh vực kinh doanh (domain business). Cách tiếp cận của DDD là kết nối chặt chẽ việc cài đặt phần mềm với sự tiến hoá của mô hình kinh doanh:
– Dự án đặt sự chú trọng nhiều cho phần nghiệp vụ chính (core domain) và các logic nghiệp vụ liên quan.
– Mô hình hoá của nghiệp vụ là trọng tâm, nền tảng cho mọi cài đặt phần mềm.
– Tăng cường cộng tác giữa nhóm kỹ thuật( developers) và các chuyên gia nghiệp vụ (domain expert) để xây dựng một mô hình tốt giúp xác định và giải quyết hiệu quả bài toán. wiki

DDD được tác giả Eric Evan đưa ra sau kinh nghiệm khoảng 20 năm tham gia phát triển phần mềm, xây dựng những ứng dụng lớn cho giới tài chính, ngân hàng, bảo hiểm, giao vận quốc tế.. Dựa trên kinh nghiệm thực tiễn, những best practices tích luỹ được, ông đề xuất một phương pháp tiếp cận mới để xây dựng những phần mềm có nghiệp vụ phức tạp mà nhóm phát triển sẽ gặp rất nhiều khó khăn để làm chủ, quản lý rủi ro, phát triển và duy tu lâu dài. Phương pháp của ông được tổng hợp trong cuốn sách xanh nổi tiếng ” Domain Driven design – tackling complexity in the heart of software” từ 2003. Và Phương pháp đã nhanh chóng nhận được sự tán dương của cộng đồng, ngày càng được phát triển và áp dụng.

1.2 Use case


Tại Septeni-Technology: từ nhu cầu phát triển những sản phẩm toàn cầu, chất lượng cao, dễ bảo trì, mở rộng đòi hỏi thống nhất một phương pháp luận phát triển dự án trong công ty, DDD đã được lựa chọn bởi CTO từ Nhật bản và yêu cầu trở thành một trong những nền tảng phát triển của Septeni Technology. Tất cả nhân viên của Septeni Technology đều cần có kiến thức về DDD dù ở các cấp độ khác nhau; DDD cũng hoàn toàn tương thích với SCRUM và yêu cầu cả đội dự án gồm developer, SQA, PO đều cần hiểu.
Là một cách tiếp cận hiện đại, với việc tổng hợp kiến thức từ một architect nhiều năm kinh nghiệm, DDD cung cấp nhiều best practices, design patterns hữu ích. Với từng cá nhân thực hành DDD giúp bạn nâng cấp kiến thức về phân tích thiết kế, tổ chức mã nguồn, hiểu sâu sắc và nghiệp vụ, giữ kết nối tốt với team. Do đó kể cả bạn không có điều kiện áp dụng nó trong công việc hiện tại tôi vẫn đề xuất với bạn nên đọc nếu bạn đang làm trong lĩnh vực phần mềm.

2. Nền tảng cơ bản của DDD


2.1 Ngôn ngữ chung


Yêu cầu đầu tiên của cách tiếp cận DDD là Ngôn ngữ Chung (UL). DDD nhấn mạnh sự cộng tác, không bó hẹp giữa các lập trình viên với nhau, do đó những người liên quan phải có khả năng trao đổi để cùng hiểu một vấn đề nghiệp vụ.
Thực tế chỉ ra rằng các từ vựng “chuyên nghành” rất dễ bị hiểu sai với người ngoài nghành, thậm chí cùng một thuật ngữ khác nhau theo các vai trò khác nhau, định nghĩa cũng có thể khác nhau.
** Ví dụ ** thực tế trong dự án gần đây, nhóm tôi có nhận được yêu cầu về quản lý các dự án cho khách hàng. Và khi thảo luận team tưởng tượng rằng dự án sẽ có các thuộc tính bắt đầu, kết thúc, giá thành, nhân viên, rồi để hoàn thành sẽ có nhiều nhiệm vụ gán cho các thành viên trong đội. Nhóm tưởng tượng đến những trello, jira, dotproject. Nhưng sau này khi nhận được sample từ khách hàng mới nhận ra Project chỉ tương đương với một bản ghi exel tương ứng với một code từ hoá đơn gán cho một người phụ trách.
Vậy làm thế nào để có ngôn ngữ chung để ai tham gia dự án cũng thống nhất một cách hiểu, có thể chia sẻ với nhân viên kinh doanh hay nhân viên kỹ thuật. Evan gợi ý rằng nó là tài liệu để mọi người cùng hiểu.
Ví dụ khi phân tích về một hệ thống quảng cáo , qua trao đổi, nhà phân tích thấy được một số yêu cầu và đưa ra một giản đồ:
sample-ad-system-diagram
Cộng thêm đó là một từ điển thuật ngữ dự án, giải thích chi tiết khái niệm các phần tử trong giản đồ. Với tài liệu đơn giản này, ta có thể dễ dàng chia sẻ, confirm hệ thống với các chuyên viên nghiệp vụ như là nhân viên kinh doanh quảng cáo, nhà quản lý, hay thảo luận giữa nhóm dự án, để cùng nhau khai phá ra các vấn đề cốt lõi, sâu sắc hơn .
Điều này có vẻ cũng không mới trong phân tích dự án, nhưng với DDD đây là yêu cầu xuyên suốt vòng đời sản phẩm, DDD yêu cầu mô hình phân tích(analysis) và mô hình cài đặt (coding) là một, phản ánh lẫn nhau.

2.2 Thiết kế hướng mô hình (model driven design)


ddd-building-block Hãy nhớ, DDD yêu cầu sự thông suốt trong xây dựng và cài đặt mô hình . Những nhà phân tích, kiến trúc sư hệ thống khi làm việc với các bên liên quan, các chuyên gia nghiệp vụ(Domain Experts), sẽ dựng lên mô hình, trao đổi và nhận được sự đồng thuận với các Domain Experts. Sau đó họ cần truyền đạt và đảm bảo các nhà phát triển, lập trình cũng thông suốt mô hình dựng lên, đến lượt mình các lập trình viên khi cài đặt cũng phải thể hiện được mô hình qua code, và nếu ai đó phát hiện bất kì điểm bất hợp lý gì, sửa đổi gì cần thiết với mô hình trong quá trình làm việc, đều cần thông báo và nhận được sự đồng thuận của nhóm phát triển, hay lớn hơn là được review và đồng thuận từ DE. Và DDD cung cấp các cấu thành nền tảng( building blocks) cho việc xây dựng mô hình mà mọi người cùng hiểu đó. Nó là “từ vựng” để thiết kế mô hình theo DDD.

2.2.1 Các cấu thành cơ bản để xây dựng mô hình trong DDD


1. Entity( Thực thể ) dùng để biểu thị các khái niệm mà sự tồn tại của nó liên tục xuyên suốt, dù các thuộc tính có thay đổi.

Ví dụ với một hệ thống quản lý nhân sự, đối tượng nhân viên Employee có các thuộc tính như name, age, address, position; thì theo thời gian thì các thuộc tính này đều có thể thay đổi, được cập nhật, tuy nhiên hệ thống vẫn cần nhận diện 1 nhân viên vẫn là nhân viên đó dù đã cập nhật tuổi, vị trí hay địa chỉ cư trú, hay cả tên cho anh ta trong hoạt động lưu vết cho 1 cá nhân. Vậy Employee cần được xác định là 1 entity.

  • Để đảm bảo điều này, các lập trình viên sẽ dùng một ID để xác định cho Entity, ID này là duy nhất, xuyên suốt vòng đời của một đối tượng là Entity.
  • Xác định một đối tượng trong mô hình là entity sẽ có các hệ quả quan trọng liên quan đến vòng đời và tương tác của entity đó như: Về cài đặt việc so sánh hai đối tượng entity không được so sánh dựa trên các thuộc tính của nó mà dựa trên ID; Entity có thể thay đổi thuộc tính theo thời gian (mutual) nên không dùng nó để trao đổi thông tin giữa các xử lý; với entity thì cần chú tâm vào cách hàng xử lý nó (behavior) hơn là dữ liệu.

2. Value Object Vâng dịch thô thì nó là loại đối tượng chứa giá trị gì đó ạ, nên Value object chỉ mô tả đặc điểm, thuộc tính của gì đó.

Trong ví dụ trên thì name, age, address, hay position đều là khái niệm thuộc loại Value Object; Nếu giá trị của đối tượng này thay đổi thì nó là đối tượng mới, và nếu giá trị 2 value object như nhau thì có thể dùng thay thế cho nhau. Nghĩa là ở dạng đối tượng này trong dự án chúng ta chỉ quan tâm đến giá trị của nó mà thôi.

Với việc phân định là Value Object hay Entity, DDD đưa ra những gợi ý hữu ích cho thực hành như với Value Object thì seft Validate, còn với Entity thì nên dùng Specification patterns..

3. Service (Dịch vụ) Khi mô hình hóa bài toán thực tế ta cần biểu diễn thực tiễn qua các khái niệm, nhưng Value Object hay Entity thì không đủ, ví dụ để biểu diễn các operations, business policy, process. Ở đây chúng nên được biểu diễn là các service.

Thiết kế một service thì nên là stateless, nghĩa là service sau khi phục vụ xong client thì không nên lưu trữ lịch sử giao dịch phục vụ cho kết quả lần tới, xong thì thôi. Một Service trong DDD là một cấu thành quan trọng của model nên cũng cần được làm rõ trong UL. Một vấn đề lưu ý nữa là phân chia nhiệm vụ cho service ở các tầng khác nhau thì khác nhau, ví như service ở infra có thể lo những dịch vụ hạ tầng về liên lạc, thông báo lỗi, truy xuất cơ sở dữ liệu.. không chứa thông tin về nghiệp vụ. Service ở domain phải mang thông tin về xử lý nghiệp vụ, còn Service ở application có thể kết hợp gọi service ở domain và kết hợp xử lý lỗi, cảnh báo lỗi từ Infra cung cấp, để hoàn thành một business use cases.

4. Module Một model lớn có thể chia thành các module, giống như một cuốn sách thì có nhiều chương. Tên module cần nằm trong UL.

Với việc sử dụng Entity, Value Object, Service, Module như những thành phần chính kiến tạo nên model, ta thấy rằng Model driven design được nhắc đến trong DDD khá gần với Object Oriented Design, nhưng không giới hạn.

Trong ứng dụng phần mềm, các đối tượng domain object liên tục được tạo ra, biến đổi, lưu lại, nên có vòng đời (Life cycle). DDD cung cấp các khái niệm:

5. Aggregate hình tượng của aggregate được biểu diễn như một chùm nho, nhiều quả nho chung trên 1 cuống nho nối với thân cây nho. Aggregate đảm bảo tính nhất quán của mọi thay đổi đối với phần tử trong nó. Về cấu tạo Aggregate có một đối tượng root là một entity là đối tượng duy nhất tham chiếu ra bên ngoài. VD trực quan về Aggregate thì Car là aggrate của tire (bánh xe), wheel(vô lăng)..

6. Factory khi việc khởi tạo một Entity hay Value Object phức tạp, bạn hãy ủy nhiệm cho một Factory. Giống như việc tạo ra 1 chiếc xe phụ thuộc vào hàng trăm linh kiện thì bạn cứ ủy quyền cho nhà máy sản xuất rằng làm cho tôi một chiếc xe mui trần, màu xanh, 4 chỗ thay vì nhập về hàng trăm linh kiện và tự lắp ráp.

7. Repository là kho chứa cho bạn lấy ra hay lưu lại các aggregate. Repository là khái niệm thuộc tầng domain không quan tâm đến kỹ thuật, phương tiện lưu trữ(memory hay db..). Cụ thể việc lưu trữ đến đâu do tầng Infra đảm nhiệm, có thể là một ORM để lưu vào Database. Hình minh họa cho Repository là bà thủ thư, bạn đến thư viện nhận và trả sách qua bà thủ thư.

3. Kiến trúc ứng dụng dùng DDD


Các lập trình viên có thể quen với kiến trúc MVC. Nhưng trong sách xanh có nói, đó là những kiến trúc “ông nội” của DDD. Về mặt kiến trúc DDD đề ra việc phân chia làm 4 tầng logic như sau:
UI( Tầng giao diện) : chịu trách nhiệm cho hiển thị thông tin, nhận lệnh từ người dùng
Application( Tầng ứng dụng) : Phối hợp các xử lý. Lưu ý là không chứa logic nghiệp vụ ở đây
Domain (Tầng nghiệp vụ) : Phần này là trái tim của phần mềm, chứa các mô hình biểu diễn nghiệp vụ của hệ thống. Thể hiện logic của nghiệp vụ nhưng uỷ quyền việc cài đặt chi tiết cho Infra. Đây là tầng quan trọng nhất
Infrastructure( Tầng nền) : Cung cấp các gói hỗ trợ, liên lạc, cài đặt chi tiết, sử dụng các thư viện bên ngoài..
Phần này đặc biệt quan trọng cho các lập trình viên. Việc thiết lập và giữ vững các quy tắc giao tiếp giữa các lớp, phân chia trách nhiệm hợp lý là điều kiền cần thiết đảm bảo kiến trúc hệ thống.
Evan có giới thiệu khá chi tiết về mô hình 4 tầng trong sách của mình kèm ví dụ tham khảo link ở cuối bài. Ngoài ra hiện cộng đồng DDD cũng đề xuất kiến trúc hiện đại có thể dùng với DDD là Hexagonal Architecture hay Port and adapter. Hi vọng có dịp được chia sẽ sâu về kiến trúc.

Trên đây là một số kiến thức cơ sở về DDD để xây dựng mô hình. Phần tiếp theo của DDD cung cấp các hướng dẫn khi phát triển dự án, từng bước hoàn thiện mô hình, xây dựng những ứng dụng lớn. Ở đó DDD đưa ra những gợi ý thiết thực cho quá trình phát triển với các best practices về:
– Tái cấu trúc liên tục
– Duy trì tính toàn vẹn của mô hình

Xin được cùng thảo luận với các bạn trong bài viết tiếp theo.

4. Vài kinh nghiệm với DDD


4.1. Khó khăn khi học DDD


  • DDD là một hướng tiếp cận giải quyết cho những phần mềm lớn và phức tạp, vì thế nó áp dụng nhiều design patterns và các best practices. Việc làm chủ những khái niệm này là không dễ và đòi hỏi nhiều kinh nghiệm. Ngoài ra cả team đều phải hiểu và tuân theo các rule của DDD.
  • DDD đòi hỏi cao trong sự cộng tác cao của nhóm phát triển với các chuyên gia về nghiệp vụ. Nếu không phải là một chính sách, quyết tâm của công ty thì cũng gặp nhiều khó khăn để áp dụng.

4.2. DDD và SCRUM


Một điều tôi thấy tuyệt vời là sự tương thích hoàn toàn của DDD với các phương pháp, công cụ phát triển sản phẩm hiện đại. Tính agility của DDD. DDD và SCRUM nhấn mạnh đến sự tương tác, phản hồi, cải tiến liên tục. Khác với Warterfall, khi giả định của chúng ta từng bước hoàn thành từ trên xuống dưới. Nghĩa là yêu cầu đã đc phân tích đầy đủ và kĩ lưỡng rồi thiết kế hoàn chỉnh rồi lập trình theo thiết kế có sẳn, rồi kiểm thử để đảm bảo cài đặt như đặc tả.
Ở SCRUM, Ngay trong một Sprint ngắn, hay thậm chí ở Daily meeting khi cài đặt, nhà phát triển có thể nhanh chóng chia sẻ về một cập nhật cho mô hình, nhận sự phản hồi của nhóm , thông báo với PO.. DDD cũng hướng nhóm SCRUM đến mô hình business ngay từ đầu để mường tượng về sự tiến hoá trong tương lai, khi yêu cầu phát triển là chưa xác định. Việc của nhóm phát triển là liên tục cài đặt và cập nhật mô hình để đạt được sự thông suốt, đúng đắn, qua đó việc phát triển phần mềm không phải là công việc khô khan mà giúp thu lượm được nhiều những kiến thức thực tế kinh doanh.
Ngoài ra, DDD cũng nhắc đến CI như là công cụ cần thiết cho automation test, auto deployment, hỗ trợ tích cực cho sự tiến hoá của sản phẩm phần mềm.

5. Tài liệu tham khảo


  • Domain Driven Design: tackling complexity in the heart of software
  • DDD quickly
  • Công ty Septeni Technology cũng đã cùng nhau dịch bản DDD quickly sang tiếng Việt, đó cũng là môt tài liệu tốt để các bạn nhanh chóng nắm bắt các ý tưởng của DDD.
  • Ví dụ đi kèm với cuốn sách xanh nổi tiếng, các nhà phát triển rất cần nghiên cứu sâu http://dddsample.sourceforge.net/architecture.html
  • Tôi cũng chia sẻ về một cài đặt sample một CMS đơn giản trên Scala ở đây.