Xàm xí về Domain Driven Design - Part 3: Strategic Design - SubDomain - Bounded Context
- 17 minsỞ các bài trước chúng ta đã phần nào thảo luận về What and Why Domain Driven Design trong việc phát triển các Enterprise System. Trong bài viết này chúng ta sẻ cùng thảo luận về công cụ mà cá nhân mình thấy là quan trọng và thiết yếu nhất của DDD - Strategic Design. Strategic Design thực sự powerful khi phát triển các dự án lớn, phức tạp. Tuy nhiên nếu áp dụng tốt Strategic Design chúng ta cũng sẻ thu được những lợi ích đáng kể đối với các dự án nhỏ dù mặc dù chúng ta không sử dụng bất kì ideas/concepts còn lại trong DDD. Chúng ta sẻ cùng tìm hiểu về approach để xử lí một hệ thống phức tạp, để chia nhỏ hệ thống. Bên cạnh đó phần nào làm rõ các khái niệm như Ubiquitous Language, Bounded Context, Subdomain,….
Như chúng ta đều biết, chia để trị là nghệ thuật của quá trình phát triển phần mềm. Cho dù không có hiểu biết về DDD thì gặp phãi vấn đề lớn chúng ta cũng nghĩ đến việc chia hệ ra thành nhiều phần, tụi mình cũng đã làm như vậy để rồi tiếp cận với Microservices Architecture một cách quán tính. Việc chia hệ thống ra thành các Sub-System với ít kiến thức về Domain Driven Design dẫn đến một hiện trạng là các software/domain concerns không được tách biệt một cách rõ ràng, phân chia trách nhiệm và mối quan hệ giữa chúng cũng diễn ra nhùng nhằn, rối ren. Kiến thức về Strategic Design sẻ phần nào tháo gỡ một số hiện trạng đó.
Với DDD approach chúng ta tiếp cận vấn đề với hai bước chính:
- Trả lời câu hỏi What?
- Trả lời câu hỏi How?
“What?”: Chúng ta sẻ giải quyết domain gì? Gồm các vấn đề nào? Các vấn đề đó đại diện cho khái niệm Problem Space.
“How?”: Khi xác định được vấn đề lớn và chia thành các vấn đề nhỏ rồi, chúng ta tập trung tháo gỡ từng vấn đề. Chúng ta sẻ giải quyết các vấn đề đó như thế nào? Triển khai hiện thực hoá chúng ra sao? Tất cả chúng đại diện cho khái niệm Solution Space.
1. Problem Space và Sub Domain:
Từ một khía cạnh nào đó chúng ta có thể định nghĩa Domain là các problems mà hệ thống của chúng ta sẻ giải quyết, do đó có thể gọi tập hợp các problems đó là Problem Space.
Để làm cho một Domain trở nên cụ thể và dễ phân tích đánh giá hơn chúng ta thường chia nhỏ Problem Space của Domain đó ra thành các problem nhỏ hơn, chúng có thể được hiểu là các Sub Domain. Sub Domain được chia thành ba loại sau:
-
Core Domain: Core Domain là những gì mang đến giá trị cốt lõi cho business của bạn, là những gì thể hiện xứ mệnh tổ chức của bạn. Core Domain phãi chứa những gì làm cho tổ chức của bạn trở nên khác biệt so với các tổ chức khác trong cùng lĩnh vực, một công ty không thể nào trụ được và thành công nếu không có gì đặc biệt so với các công ty khác.
-
Supporting Sub Domain: Đôi lúc một hệ thống cần phãi giải quyết một số vấn đề chuyên biệt để hổ trợ cho các core business của hệ thống, các vấn đề đó là cần thiết nhưng không phãi là cốt lõi của hệ thống, nó được gọi là Supporting Sub Domain. Một ví dụ như phần Suggestion của TIKI, nó không là phần quan trọng nhất nhưng là phần hổ trợ cực mạnh mẻ cho Core business của Tiki. Nó được gọi là Supporting Sub Domain.
-
Generic Sub Domain: Là một Sub Domain chung chung hoạt động nhiều nơi trong hệ thống, không chứa bất cứ business gì của hệ thống nhưng vẫn cần thiết để hệ thống có thể hoạt động.
Để hiểu hơn về việc chia tách thành các Subdomains, chúng ta hãy nghĩ về việc design một hệ thống E-Commerce. Về cơ bản, một E-Commerce application cần giải quyết các vấn đề sau:
- User/Account: Cho phép người dùng đăng nhập vào hệ thống.
- Role/Permission: Xác thực người dùng.
- Product Catalog: Cho phép người mua duyệt qua để tìm kiếm sản phảm thích hợp.
- Product Catalog Administration: Cho phép admin của hệ thống quản lí danh mục sản phẩm, ngành hàng, report,…
- Shopping Cart: Cho phép người mua quản lí dõ hàng của họ,…
- Shopping Cart Administration: Cho phép admin của hệ thống quản lí, theo dõi được các đơn hàng, report,…
- Inventory Administration: Cho phép admin của hệ thống quản lí kho bải
- Payment: Giúp người dùng thanh toán đơn hàng.
- Rating: Cho phép người dùng đánh giá sản phẩm thông qua đó có nguồn dữ liệu cho việc suggestion, xếp hạng,…
- Forecasting: Báo cáo và dự đoán xu hướng mua sắm.
- Notification: Gửi thông báo cho người dùng khi cần.
Rõ ràng đó là những vấn đề cơ bản của một E-Commerce application cần giải quyết.
Cái nhìn cá nhân của mình thấy rằng Core Domain của một E-Commerce application cần giải quyết là giúp người mua và người bán hoạt động được dựa trên các chức năng như Product Catalog, Product Listing, Shopping Cart.
Bên cạnh đó có thêm các thành phần Supporting Domain như Rating, Suggestion, Forecasting về cơ bản nó không quan trọng bằng các Core Domain tuy nhiên lại bổ trợ cực kì mạnh mẻ cho Core Business của hệ thống. Hệ thống suggestion hoạt động thông minh mang lại trải nghiệm mua sắm tốt cho người dùng. Hệ thống dự báo hoạt động thông minh có thể giúp các nhà cung cấp nhận thức đánh giá được thị trường, đưa ra các quyết định kinh doanh đúng đắn hơn, giảm thiểu rủi ro trong kinh doanh. Do đó các Supporting Sub Domain đó gián tiếp mang lại thành công cho core business.
Ngoài ra còn có một số Generic Sub Domain ví dụ như user Identity, Authorization/Authentication, Notification là những thứ tuy không mang lại giá trị business cho ứng dụng nhưng lại rất cần thiết cho hệ thống.
Một điều đáng chú ý là một Sub Domain có thể thuộc hai loại Sub Domain khác nhau, nếu một marketplace đặt trải nghiệm người mua hàng lên hàng đầu, muốn xây dựng một hệ thống suggestion system nhằm tạo ra sự khác biệt, lợi thế đối với các tổ chức khác thì Suggestion cũng sẻ có thể coi Core Sub Domain.
Như vậy chúng ta có thể thấy rằng Problem Space cuối cùng cũng là các phần của Domain cần được phát triển để tạo nên core Domain. Đồng nghĩa với việc đánh giá, phân tích Problem Space của một domain lớn sẻ liên quan đến việc phân tích các Sub Domain đã hiện hữu hay chưa và sự hiện hữu của các Sub Domain đó có thực sự cần thiết đối với hệ thống hay không. Sub Domain cho phép chúng ta có cái nhìn vừa bao quát vừa chi tiết các thành phần vấn đề khác nhau của Domain lớn, do đó Sub Domains là một công cụ rất hữu ích trong việc đánh giá, phân tích Problem Space.
Chúng ta đã phần nào rõ ràng các khái niệm về Problem Space và SubDomain. Vậy làm sao để tiếp cận một dự án phức tạp và lèo lái dự án đi đúng hướng? Chúng ta phãi trả lời các câu hỏi sau:
- Core Domain của chúng ta là gì?
- Tầm nhìn mục tiêu chiến lược về Core Domain đó là gì?
- Những thành phần nào nên được coi là một phần của Core Domain?
- Các Supporting Subdomains và Generic Subdomains nào là cần thiết đối với hệ thống?
- Trong từng khu vực của hệ thống sẻ có những nhân tố nào(engineer, domain expert…)?
- Mỗi nhân tố đó sẻ đóng góp ra sao?
Một khi đã xác định rõ ràng được tầm nhìn và mục tiêu chiến lược của Core Domain và các thành phần cần thiết để hỗ trợ, bổ trợ cho Core Domain, chúng ta sẻ có được chiến lượt tốt nhằm tập trung ưu tiên nguồn lực, sức mạnh, tầm nhìn, bí quyết để phát triển Core Domain. Đồng thời đảm bảo rằng tất cả các bên liên quan trong hệ thống có được sự liên kết chặc chẻ và cam kết hợp tác dựa trên một tầm nhìn chung. Không quá lang mang bởi những thành phần không quan trọng nhưng lại chiếm nhiều effort. Các team ở supporting domain cần nhận thức được mình đang làm gì, biết mình đang phục vụ core domain như thế nào, đóng góp ra sao…..
2. Solution Space và Bounded Context:
Chúng ta đã xác định Problem Space với các Sub Domain được chia nhỏ, ứng với từng Sub Domain chúng ta sẻ có một hoặc nhiều Solution/Bounded Context để giải quyết nó. Tập hợp chúng gọi là Solution Space.
Mình tạm định nghĩa có phần chưa hoàn chỉnh rằng Solution Space là nơi các vấn đề được giải quyết trong thực tế và Bounded Context là mỗi câu trả lời cho mỗi problem trong Problem Space, là một giải pháp cụ thể, một quan điểm hiện thực hóa, được sử dụng để hiện thực hóa một giải pháp.
Bên cạnh đó chúng ta cũng có thể hình dung rằng mỗi một Bounded Context xác định cách nhìn nhận về một phần của domain business. Một thành phần business có thể có mặt ở nhiều nơi trong một hệ thống tuy nhiên tại mỗi khu vực, mỗi context của hệ thống có thể nhìn nhận thành phần đó một cách khác nhau đó cũng chính là nguyên nhân vì sao họ gọi đó là Bounded Context.
Theo lẻ thông thường, chúng ta luôn có một quan hệ 1-1 lí tưởng cho một câu hỏi và một câu trả lời, do đó để đơn giản hoá chúng ta nên cố gắng chia các Subdomain và Bounded Context sao cho chúng luôn có sự liên kết 1-1 giữa một Subdomain và một Bounded Context.
Tuy nhiên trong thực tế ta nên linh động trong việc define các Subdomain và Bounded Context cho hợp lí. Một hệ thống phức tạp có rất ít Domain nhỏ và đơn giản, khi mà chỉ có một Ubiquitous Language và một model duy nhất biểu diễn mọi thứ về một Sub Domain, đồng thời luôn có một liên kết đẹp đẻ 1-1 giữa Sub Domain và Bounded Context như vậy.
Các business rule thường chống chéo, conflict, rối rắm rất nhiều. Cũng là một thuật ngữ nhưng có thể mang nhiều nghĩa khác nhau ở các context khác nhau, và cũng có thể hai từ khác nhau như lại cùng mang một ý nghĩa ở các context khác nhau.
Thông thường sẻ có nhiều solution cùng phối hợp để giải quyết một problem trong Problem Space tuỳ thuộc vào cách bạn nhìn nhận nó. Mỗi một Bounded Context là một trong nhiều solution để giải quyết một problem, do đó một Sub Domain có thể có nhiều Bounded Context. Bên cạnh đó cũng có những trường hợp vì mối quan hệ giữa các Context quá chặt chẻ hay một nguyên nhân nào đó khiến chúng ta phãi giải quyết hai problem trong cùng một Bounded Context, chẳng hạn như việc quản lí user account và role/permission cùng được đặt trong trong một context là Identity And Access
hay việc cung cấp product catalog cho client đồng thời hổ trợ admin quản lí product catalog được đặt trong Product Catalog
context.
Một khi toàn bộ Domain của một của tổ chức đã được chia ra thành các SubDomain, các Bounded Contexts rồi thì chúng ta chỉ cần tập trung vào một khu vực cụ thể thay vì toàn bộ business domain. Với Problem Space đã xác định ở trên chúng ta có thể cơ bản xắp xếp chúng vào các Bounded Context như sau:
Lưu ý rằng Problem Space là sự kết hợp giữa Core Domain, các Supporting Domain và Generic Domain. Khi bạn đã hiểu rõ về Problem Space, bạn sẻ đưa ra được Solution Space. Solution Space sẽ bị ảnh hưởng mạnh mẽ bởi hệ thống, công nghệ hiện có và những hệ thống mới sẻ được tạo ra. Ở đây chúng ta cần phải phân tích về các Bounded Context, do đó hãy cân nhắc những câu hỏi quan trọng sau:
- Hệ thống hiện tại đang có những gì?
- Những thứ đó có thể được tái sử dụng hay không?
- Những gì cần được thêm vào để thoả mãn mục tiêu cuối cùng?
- Làm thế nào tất cả những thứ có sẳn và những thứ mới được thêm vào có thể kết nối với nhau, tích hợp với nhau?
- Xác xuất mà mỗi quyết định chiến lượt đó sẻ thành công, thất bại, làm delay quá trình phát triển như thế nào?
- Chúng ta phãi quản lí hệ thống các thuật ngữ của các Ubiquitous Languages giữa các context như thế nào? Quản lí sự khác nhau về mặt ngữ nghĩa các mỗi khái niệm/thuật ngữ, sự chồng chéo, xung đột ngữ nghĩa giữa các bounded context khác nhau ra sao?
- Quyết định độ lớn của một Bounded Context như thế nào? Trong một Bounded Context nên chứa bao nhiêu Modules, Aggregates, loại Event, Services?
- Trong một Boundex Context nào sẻ chứa các concepts giải quyết Core Domain? Các Tactical Parterns nào sẻ được sử dụng cho việc model chúng?
3. Size của Bounded Context:
Xác định độ lớn của một Bounded Context như thế nào là hợp lí? Trong một Bounded Context nên chứa bao nhiêu Modules, Aggregates, loại Event, Services?
Thực sự không có một câu trả lời cho con số chính xác cho câu hỏi trên. Một bouded context cần đủ lớn để thể hiện đầy đủ một Ubiquitous Language trong chính nó.
Các khái niệm không thực sự liên quan đến Core Domain thì nên được cân nhắc mức độ quan trọng mới được đưa vào Ubiquitous Language. Nếu một khái niệm không có trong Ubiquitous Language của bạn, thì không nên đưa nó vào model ngay từ đầu. Nếu một hoặc nhiều khái niệm ngoại lai chen vào hãy loại bỏ chúng ra, chúng có thể thuộc về một Supporting hoặc Generic Subdomain hoặc không thuộc model nào cả.
Hãy cẩn thận để không nhầm lẫn các yếu tố là thành phần thực sự của Core Domain. Model của bạn phải thể hiện sự phong phú của Ubiquitous Language trong context, không để xót thứ gì thiết yếu cũng như không có quá nhiều thứ nhỏ nhặt rối rắm khiến chúng ta khó hình dung được thứ gì là cốt lõi cần tập trung, thứ gì là nhỏ nhặt không đáng đề cao. Các công cụ như Context Map(sẻ giới thiệu sau) có thể giúp chúng ta đánh giá những tiêu chí đó một cách tốt hơn.
Bên cạnh đó cũng sẻ có một số nguyên nhân dẫn đến việc chúng ta tạo ra một Bounded Context với một kích thước sai lầm. Những điều gì có thể dẫn chúng ta tạo ra một Bounded Context có kích thước sai?
- Chúng ta có thể sai lầm khi chấp nhận các ảnh hưởng đến từ kiến trúc thay vì để Ubiquitous Language dẫn lối chúng ta. Trong quá trình phát triển phần mềm, các engineer có xu hướng chuyển một Bounded Context thành một Fake Context dưới góc nhìn của họ, ứng với mục tiêu phát triển của họ, Fake Context đó đôi lúc bị phụ thuộc vào một platform, framework hoặc một vài thành phần infrastructure giúp ích cho việc implement, vô hình chung khiến các engineer không thể tập trung nhìn nhận các Bounded Context như với mục đích ban đầu, họ cân nhắc các ranh giới kĩ thuật thay cho ranh giới ngôn ngữ mà các Bounded Context bám víu vào, làm méo mó tầm nhìn đến từng Bounded Context và gây ra quyết định sai.
- Một sai lầm nữa là thay vì dùng Bounded Context để phân chia trách nhiệm thì các manager dùng việc chia Bounded Context để chia task, quản lí task cho Engineer Team, họ có xu hướng coi các Bounded Context là Fake Context của họ nhằm áp dụng các mục tiêu quản lí. Vì một số chiến lược về mặt quản lí mà họ có thể thêm bớt nhiều nhu cầu, thành phần dẫn đến việc không còn nhìn nhận đúng bản chất, kích thước cũng như méo mó tầm nhìn đến từng Bounded Context.
4. Conclusion:
Với cá nhân mình đánh giá một số kiến thức về Strategic Design kể trên thực sự rất thiết yếu cho việc design cũng như phát triển Enterprise Application:
- Mang lại chiến lược phát triển rõ ràng: Vượt phạm vi dự án(Scope Creep) là vấn đề nhan nhản trong các dự án phần mềm. Không chỉ làm tốn nhiều chi phí mà còn gây khó chịu cho đội ngủ phát triển, là nguyên nhân làm các developer mất đi sự vui vẻ hạnh phúc khi làm việc. Việc xác định rõ ràng các SubDomain, các Bounded Context và ranh giới giữa chúng giúp chia nhỏ dự án nhằm tập trung vào những phần nhỏ theo thứ tự ưu tiên từ đó hạn chế tối đa ảnh hưởng xấu từ Scope Creep.
- Nên chia nhỏ các model và đặt vào từng context cụ thể thay vì design một model lớn chứa mọi thứ. Điều đó giúp chúng ta có thể tập trung đào sâu và xác định đúng bản chất của mỗi vấn đề tại mỗi khía cạnh thay vì quá ôm đồm mọi thứ dễ gây ra quá tải.
- Strategic Design giúp bạn có cái nhìn bao quát đến từng giá trị mà hệ thống bạn mang lại. Đánh độ ưu tiên cho từng SubDomain mà ưu tiên lớn nhất cho Core Domain nhằm tối thiểu hoá effort nhưng mang lại tổng giá trị tối đa.