Model-Driven Design

Domain driven design

Trong phần trước, chúng ta đã nói về kiến trúc phân lớp trong Domain Driven Design (là một pattern trong Model của DDD). Bài viết này sẽ giải thích rõ hơn các pattern quan trọng khác trong DDD mà giúp chúng ta có thể mô hình hoá các nghiệp vụ bằng các model.

Entity

Trong quá trình phát triển phần mềm, chúng ta có một nhóm các đối tượng có định danh riêng và chúng được giữ xuyên suốt trong quá trình chạy phần mềm. Đối với những đối tượng này thì các thuộc tính của chúng có giá trị như thế nào không quan trọng bằng việc chúng tồn tại liên tục và xuyên suốt trong hệ thống. Chúng ta thường gọi chúng là các Entity

Các ngôn ngữ lập trình thường lưu trữ các đối tượng vào bộ nhớ và chúng thường được gắn địa chỉ hoặc tham chiếu, do đó chúng sẽ không thay đổi trong một khoảng thời gian nhất định khi chương trình chạy, tuy nhiên không có gì đảm bảo nó sẽ luôn như vậy mãi. Thực tế hoàn toàn ngược lại, các đối tượng sẽ được đọc và ghi liên tục ra khỏi bộ nhớ. Chúng có thể được đóng gói, gửi qua mạng và được phía bên nhận cập nhập thông tin hoặc chúng bị huỷ hoàn toàn. Do đó các tham chiếu chỉ đại diện cho một trạng thái nhất định của hệ thống mà không thể định danh cho đối tượng. Đơn giản chúng ta có 2 đối tượng chứa thông tin về thời tiết thì khả năng hai đối tượng có cùng nhiệt độ là hoàn toàn có thể xảy ra, hai đối tượng này hoàn toàn có thể thay thế cho nhau, tuy nhiên chúng được tham chiếu khác nhau nên rõ ràng 2 đối tượng này không thể là Entity

Do đó, trong phần mềm để tạo ra một Entity thì chúng ta cần tạo một định danh duy nhất. Có nhiều cách để tạo ra định danh duy nhất như chúng ta có thể dùng ID được sinh tự đồng bởi phần mềm, hoặc có thể là code ví dụ như mỗi country có thể có mã code khác nhau, hoặc nó định danh duy nhất bởi tổng hợp các thuộc tính của Entity.

Trong một khách sạn, để phân biệt giữa các phòng với với nhau thì chúng ta thường đánh theo số phòng và tầng bao nhiêu giúp ta định danh được đây là phòng nào và trong phòng này thì đang có những thuộc tính nào. Tương như vậy, trong ngân hàng thì nhân viên ngân hàng đều có một số ID để định danh và giúp xác định đó là ai, sinh năm bao nhiêu, bao nhiêu tuổi. Mỗi nhân viên sẽ đều có ID khác nhau và không ai giống ai.

Các Entity là những đối tượng quan trọng trong domain model và khi mô hình hoá chúng ta cần cân nhắc ngay từ đầu và việc phân biệt xem đối tượng có phải Entity hay không cũng rất quan trọng.

Value Object

Trong Domain, có những lúc chúng ta cần có các thuộc tính của các phần tử trong Domain. Khi đó chúng ta không cần quan tâm đó là đối tượng nào mà chúng ta chỉ quan tâm đến các thuộc tính của chúng. Các đối tượng mà dùng để miêu tả các thông tin cố đinh trong domain mà không cần định danh thì ta gọi chúng là Value Object

Về cơ bản thì Value Object và Entity là giống nhau, chỉ khác nhau là Entity thì có định danh còn Value Object thì không. Đối tượng Entity là mutable trong khi Value Object là immutable tức là nó được tạo bởi constructor và sẽ không bao giờ thay đổi . Do đó, khi bạn muốn cập nhập thông tin cho đối tượng thì bạn phải tạo một đối tượng mới chứ không phải thay đổi giá trị của đối tượng cũ. Nhờ không có định danh nên Value Object có thể được tạo và huỷ dễ dàng. Cũng nhờ không có định danh nên Value Object có thể được dùng chung và nó có thể chứa các Value Object khác hoặc một Entity.

Trong lập trình thì chúng ta gặp rất nhiều Value Object ví dụ như trong Java chúng ta có kiểu DateTime, String, Int – chúng đều là các Value Object, hoặc chúng ta có một đối tượng quảng cáo Ads có một Value Object là status mà nó có các giá trị là ACTIVE, INACTIVE, DELETED.

Trong Domain, khi thiết kế thì chúng ta cần phải định nghĩa rõ ràng xem những đối tượng nào là Entity, và những đối tượng nào là Value Object. Nó hoàn toàn phụ thuộc vào thiết kế bài toán và nghiệp vụ của phần mềm. Ví dụ trong một đối tượng User thì chúng ta có thể coi address là một Value Object nhưng trong một Domain khác chúng ta cần quản lý địa chỉ từng khu vực một thì lục này chúng ta có thể coi Address là một Entity.

Aggregate

Aggregate là pattern để định nghĩa việc sở hữu đối tượng và phân cách giữa chúng. Một mô hình thường được tạo từ nhiều đối tượng trong Domain. Cho dù chúng ta có thiết kế cẩn thận thế nào thì cũng không tránh khỏi việc sẽ có nhiều mối quan hệ chằng chịt giữa các đối tượng, và tạo thành một lưới các quan hệ. Có thể có nhiều mối quan hệ khác nhau, với mỗi kiểu quan hệ giữa các mô hình cần có một cơ chế phần mềm để thực thi nó.

Chúng ta sẽ có nhiều kiểu mối quan hệ giữa các đối tượng. Đơn giản nhất là quan hệ một – một, phức tạp hơn nữa là quan hệ một – nhiều vì nó liên quan đến nhiều đối tượng một lúc hoặc tập hợp các đối tượng. Ngoài ra còn có mối quan hệ nhiều – nhiều thường là mối quan hệ qua lại. Điều này khiến cho mối quan hệ giữa các đối tượng tăng lên và việc quản lý các mối quan hệ rất khó khăn. Do đó, số quan hệ nên được tối giản càng nhỏ càng tốt.

Aggrgate giúp chúng ta nhóm các đối tượng, nhóm này có thể coi như một khối thống nhất với các thay đổi dữ liệu. Một Aggregate được phân tách với phần còn lại của hệ thống, ngăn cách giữa các đối tượng nội tại và bên ngoài.

Để dễ hiểu thì khi chúng ta cần mô hình hoá đối tượng ôtô. Một chiếc ôtô thì thành phần được tạo từ nhiêù Entity và Value Object như cánh cửa, đèn hoặc sơn xe… Chúng ta có thể coi cánh cửa và đèn xe là các Entity, sơn xe là Value Object. Aggregate giúp chúng ta nhóm các đối tượng này để tạo nên đối tượng ôtô, có thể hiểu nó giúp chúng ta xác định các thành phần của ôtô. Bên cạnh đó trong hệ thống của chúng ta có domain khác là khách hàng, thì Aggregate cũng giúp chúng ta xác định được các đối tượng và ràng buộc cấu thành một customer như thông tin về tên, địa chỉ, giới tính …Aggregate giúp chúng ta phân biệt rõ được 2 domain là ôtô và khách hàng, các nhóm đối tượng cấu tạo nên nó. Nhờ đó giúp đảm bảo được tính toàn vẹn dữ liệu và đơn giản hoá các ràng buộc và các mối quan hệ.

Trong Aggregate có một Entity được gọi là aggregrate root, nó giúp chúng ta đảm bảo tính toàn vẹn dữ liệu. Nếu một Domain bên ngoài muốn truy cập để lấy thông tin của Domain khác thì nó sẽ truy cập và lấy thông tin thông qua Aggregate root. Mọi thay đổi cũng phải thông qua Entity root này và khi chúng ta xoá Entity root thì toàn bộ đối tượng bên trong nó cũng bị xoá vì không còn đối tượng tham chiếu nữa.

Factory

Các Entity và đối tượng tập hợp có thể lớn và phức tạp – quá phức tạp để chúng ta có thể dùng construtor của Entity root tạo nó do đó trong DDD chúng ta dùng Factory pattern. Nhưng tất nhiên trong thực tế, chúng ta có thể dùng Contructor trong trường hợp sau:

  • Việc khởi tạo không quá phức tạp
  • Việc tạo một đối tượng không liên qua đến việc tạo đối tượng khác và toàn bộ các thuộc tính cần thiết được truyền qua constructor

Factory là một đối tượng có trách nhiệm duy nhất là tạo các đối tượng khác. Trong DDD, Factoy thường được sử dụng để tạo các Aggregate. Factory cung cấp một interface thích hợp cho việc tạo các object phức tạp trong một ứng dụng. Khi ứng dụng phát triển, các quy tắc xung quanh việc tạo object có thể bị lỗi. Factory cung cấp một tiêu chuẩn để khởi tạo các object, nên việc tạo object mới sẽ không bị lỗi khó hiểu.

Factory giúp chúng ta đóng gói các đối tượng phức tạp hoặc Aggregate. Do đó, nó cần phải biết được các cấu trúc bên trong và mối quan hệ giữa các object, nhưng nó cũng giúp các truy cập bên ngoài tránh được những phức tạp này bằng cách cung cấp một interface mà phản ánh lại những yêu cầu phía client và một abstract giúp cho việc tạo object. Client sẽ không cần hiểu những nội tại bên trong object và chỉ cần dùng và tạo đơn giản thôi

Chúng ta có thể hiểu đơn giản về Factory giống như một nhà máy. Nếu chúng ta cần tạo ra một chiếc ôtô thì chúng ta không cần quan tâm làm thế nào để tạo được nó mà chỉ cần có nguyên liệu đầu vào và đầu ra của Factory là ôtô.

Repository

Trong thiết kế hướng lĩnh vực, đối tượng có vòng đời bắt đầu từ khi khởi tạo và kết thúc khi chúng bị xoá hoặc lưu trữ. Một constructor hoặc factory sẽ lo việc khởi tạo đối tượng. Toàn bộ mục đích của việc tạo ra đối tượng là sử dụng chúng.

Repository có nhiệm vụ chính là thêm một object, lấy các object bằng identifier hoặc các tiêu chí phức tạp và cuối cùng là xoá dữ liệu. Cũng có những trường hơp nó được dùng để tổng hợp các dữ liệu như là có bao nhiêu đối tượng trong hệ thống, tổng số sản phẩm đang có trong kho. Trong những trường hợp này, repository có thể cung cấp những phương thức tập hợp trực tiếp, nên chúng ta sẽ không mất thời gian để lấy dữ liệu các object.

Repository được thực hiện trong lớp Domain, bởi vì nó hoạt động với các đối tượng trong Domain. Nhưng trong lớp Domain, chúng ta không có bất kỳ ý niệm nào về database hoặc lưu trữ, nên Repository chỉ là một Interface. Chúng ta có thể lưu trữ các object của Domain trong database, trong một Document Database hay trong một hệ thống bên ngoài được kết nối bằng API hoặc bằng bất kỳ cách nào mà chúng ta có thể hình dung ra. Tất cả những hệ thống này đều là Infrastructure cho Domain, nên phần thực thi của Repository là nằm ở lớp Infrastructure. Đọc đến đoạn này, chúng ta sẽ cảm giác hơi đảo ngược một chút, vì trong kiến trúc thì lớp Domain phụ thuộc Infrastructure.

Để hiểu một cách đơn giản về repository thì ta có thể hình dung cách thức hoạt động như sau. Phía Application sẽ nhận request để lưu thông tin một khách hàng vào trong Database, thì nó sẽ tạo ra một đối tượng customer bằng cách dùng Factory, rồi sau đó dùng phương thức được đóng gói trong Repository để lưu đối tượng customer. Repository lúc này giống như một Interface trung gian giứa lớp application và infrastructure, phần thực hiện tương tác và lưu vào DB sẽ được thực thi ở trong phần Infrastructure.

You May Also Like

About the Author: Nguyen Dinh Thuc

Leave a Reply

Your email address will not be published.