38
日日日日日日日日日日 Khái quát về Domain Driven Design I. Khái quát Tài liệu này tóm tắt lại các khái niệm và nội dung quan trọng từ cuốn sách Implememting Domain-Driven Design 日日日日日 日日日日 日日日 2015.9. 4 1 日日日日 日日 1

[Implementing Domain Driven Design] Summary Vietnamese.docx

Embed Size (px)

Citation preview

Page 1: [Implementing Domain Driven Design] Summary Vietnamese.docx

日通システム株式会社

Khái quát về Domain Driven Design

I. Khái quát

Tài liệu này tóm tắt lại các khái niệm và nội dung quan trọng từ cuốn sách

Implememting Domain-Driven Design

改訂年月

版 改訂内容 改訂者

2015.9.4 1 初版作成 北平

1

Page 2: [Implementing Domain Driven Design] Summary Vietnamese.docx

II. Bounded Context

1. Domain, Sub Domain, Bounded Context

Domain bao quát những vấn đề chúng ta phải giải quyết và toàn bộ những j liên

quan đến vấn đề đó.

Từ việc chúng ta module hóa cho đến việc coding để giải quyết những vấn đề đó

được gọi là domain model hay còn gọi là thiết kế hướng miền vấn đề

Việc chúng ta chỉ tập chung vào 1 phần cụ thể trong toàn bộ domain được đưa ra

được gọi là subdomain. Nếu có subdomain nào đặc biệt quan trọng về nghiệp vụ thì

người ta gọi là core domain

Hình thể hiện subdomain và bounded context

Context chứa trên 1 subdomain là bounded context

2

Page 3: [Implementing Domain Driven Design] Summary Vietnamese.docx

Có khả năng 1 domain được sử dụng trên 2 context, tuy nhiên, việc sử dụng chung 1

class lại không dễ dàng.

Ví dụ trong khái niệm [ book] được xử lý (sử dụng) trong nhiều context # nhau

nhưng với mỗi context thì lại có các thông tin, behavior khác nhau.

(1) phần mở đầu và tóm tắt nội dung

(2) tác giả và hợp đồng xuất bản

(3) quản lý quá trình chỉnh sửa và việc viết sách

(4) bìa sách khổ giấy và hình ảnh minh họa

(5) dịch sách sang các ngôn ngữ khác

(6) tạo sách giấy và sách điện tử

(7) tuyên truyền quảng cáo sách

(8) bán sách thông qua trung gian hay là bán trực tiếp tới khách hàng

(9) giao sách cho khách hang hoặc đại lý trung gian

Giả như chúng ta gộp tất cả các context này vào chung 1 class “Book” thì class

book này sẽ trở nên rất phức tạp. Ở các context khác nhau cho dù chúng ta gặp

những khái niệm giống nhau thì chúng ta nên thiết kế các class riêng cho từng

context

3

Page 4: [Implementing Domain Driven Design] Summary Vietnamese.docx

III. Architecture

1. Layered Architecture

2. Hexagonal Architecture

Là cấu trúc mà những xử lý nghiệp vụ được sử lý ở tầng Domain (domain layer)

mà không phải ở tầng nào khác.

Tuy tên là Hexagonal Architecture nhưng không có nghĩa là hình lục giác.

Người ta còn hay gọi là Onion Architecture hay Ports and Adaptors

4

Page 5: [Implementing Domain Driven Design] Summary Vietnamese.docx

Cho dù Có thêm client nào vào đi nữa thì cũng chỉ cần bổ sung adapter chuyên dụng

của client đó mà xử lý bên trong sẽ không bị ảnh hưởng.

IV. Entity

1. Individuality Tính đồng nhất

Khi chúng ta thiết kế các khái niệm domain nếu chúng ta phải theo dõi, hay lưu

các thông tin về các khái niệm đó thì ta sẽ thiết thế chúng như là các entity.

Ví dụ: Khái niệm Person thì bao gồm các thuộc tính như là Name, Age và … Khi

kết hôn thì tên sẽ thay đổi (văn hóa nhật) trong ngày sinh nhật thì tuổi sẽ tang lên

chẳng hạn. Cho dù là như thế nhưng vì chúng ta phải sử các Person với tư cách như

nhau nên chúng ta phải thiết kế thành các entity.

5

Page 6: [Implementing Domain Driven Design] Summary Vietnamese.docx

Khi tăng lên 1 tuổi thì có sử lý 2 person như những thứ giống nhau không?

2. Unique Identity Tính duy nhất

Để xác định duy nhất entity thì chúng ta cần thứ j để nhận viết, Trong trường

quyết định primary key của database thì chúng ta cũng nên suy nghĩ như thế

Có thể có bao nhiêu cách tạo key

① user quết định

Chúng ta lấy thuộc tính mà người dùng có thể nhập ví dụ (employeeID, name)

để làm key cho entity.

Trong phương pháp này thì chúng ta cần đưa ra các cơ chế để key không bị

trùng

② sử dụng chức năng của database đẻ sinh key

Chúng ta sử dụng trường key tự tăng của database làm key của entity.

Trong phương pháp này thì đầu tiên database phải sinh key, và nó cũng có

những hạn chế như là không thể đưa ra key cho entity ở trên phía application

③ tạo key ở trên app

Ví dụ như chúng ta sử dụng thuật toán UUID (Không thể trùng key) để tạo ra

key rồi cho làm key của entity.

Phương pháp này có ít hạn chế, và đơn giản

6

Page 7: [Implementing Domain Driven Design] Summary Vietnamese.docx

V. Value Object

Ở trong khái niệm domain thì cũng tồn tại nhiều thứ mà chúng ta nên thiết kế với

tư cách là value object. Ví dụ: tuổi, tiền, địa chỉ chẳng hạn. Không chỉ là Integer hay

String mà chúng ta nên chuyển chúng thành các đối tượng của Value object class.

Về việc quyết định xem khái niệm đó có nên trở thành entity hay value object hay

không thì trước hết chúng ta sẽ thảo luận về value object. Cũng như khi chúng ta xây

dựng các entity, chúng ta cố gắng thết kế tập hợp các value object chứ không phải

tập hợp các entity con.

Vì các đặc tính của value object như là tình bảo trì, unittest, quản lý, tạo instance

đều để dễ thực hiện hơn entity

1. đặc chưng của value object

① Đo lường 1 cái gì đó.Định lượng hóa, giải thích

Con người thì có tuổi, tuổi thì được định tính là từ khi sinh ra đến thời điểm

hiện tại xem trải qua bao nhiêu năm.

Con người thì có tên, tên thì giải thích cho việc người đó được gọi như thế nào

② có thể lưu giữ trạng thái hay không thay đổi(bất biến)

Với nhứng entity mà chúng ta phải theo giõi, lưu giữ giá trị thì trạng thái của

các bộ phận (tên, tuổi…) phải có thể thay đổi được. Để làm được điều đó chúng

ta đưa ra value object như là 1 phương pháp tạo mới khi thay dổi giá trị.

7

Page 8: [Implementing Domain Driven Design] Summary Vietnamese.docx

Age instance với value =25 đã bị loại bỏ

コード例

person.setAge(new Age(25));

...

person.setAge(new Age(26)); // Age の値を変更する場合は新しい Age

インスタンスを作る

Đương nhiên, chúng ta không được phép tạo ra method được update thuộc tính

Value trong Age class. Trong trường hợp muốn thay đổi thuộc tính giống như

method trim của kiểu String chúng ta phải tạo ra các method có thể trả ngược lại

instance mới.

Nếu chúng ta đưa ra được như thế thì chúng ta có thể tránh được việc tự tiện

update nội dung trước khi truyền object, hay là unit test dơn giản

③ tạo ra 1 giá trị có nghĩa bằng việc kết hợp nhiều khái niệm

Value object không hẳn là bị giới hạn là chỉ có 1 thuộc tính. Ví dụ: khái niệm

tiền thì chúng ta có thể thiết kế value object với cấu trúc gồm 2 thuộc tính là đơn

vị tiền tệ là số lượng

Amount và Currency. Chúng ta cũng có thể sử dụng class tự định nghĩa (value object)

8

Page 9: [Implementing Domain Driven Design] Summary Vietnamese.docx

hay primitive data type

④ So sánh 2 value object

Khi tồn tại 2 instance của age. Nếu những cái đó về mặt khái niệm là như nhau

thì chúng ta phải quyết đinh xem giá trị của chúng có bằng nhau không

Ví dụ: Chúng ta tạo method như dưới đây với value object Age

@Override

public boolean equals(Object obj) {

if (obj == null) {

return false;

}

if (this.getClass() != obj.getClass()) {

return false;

}

final Age other = (Age) obj;

return this.value == other.value;

}

2. Persisting Value Objects

Trong tường hợp lưu value object vào database thì chúng ta có list sau

① lưu vào cùng bảng mà thằng entity được lưu

Là phương pháp mà khi entity chứa thằng value object được lưu vào các bảng

thì khi đó thằng value object cũng được lưu vào bảng đó.

NẾu không có vấn đề j đặc biệt thì sẽ như thế này

9

PERSON テーブル{ ID, FIRST_NAME, LAST_NAME, AGE }

Page 10: [Implementing Domain Driven Design] Summary Vietnamese.docx

② Lưu vào bẳng chuyên dụng của value object

Bảng mà value object sử dụng khác với bảng của entity chứa value object đó.

Vì cách này không phá vỡ sự chuẩn hóa database nên chúng ta có thể chọn cách

này cũng được

Tuy nhiên. Chúng ta cần phải chú y Để có thể lưu được value object vào bảng

chuyên dụng thì chúng ta cần thêm primary key và foreign key vào data model

nhưng cách đó chúng ta chỉ cố gắng gượng để có thể sử được chứ không phản ánh

được các hạn chế có thể có của domain model.

Domain model chỉ đơn giản là các khái niểm dựa trên nghiệp vụ và không

muốn có những thông tin mang tính kỹ thuật database.

Vì một người thì có nhiều số điệnt thoại (điện thoại di động, điện thoại bàn) nên chúng

ta sử dụng bảng Phone_Number

VI. Domain Service

Khi Business logic sử dụng thông tin từ nhiều tập hợp thì chúng ta tạo domain

service class để thực hiện. Domain service nhất thiết là để thực hiện các business

logic, khác với application service thực hiện việc quản lý transaction hay security.

Về aggregate và application service sẽ được giải thích ở trang sau

10

PERSON テーブル{ ID, NAME }

PHONE_NUMBER テーブル { PERSON_ID, SUMMARY, NUMBER }

Page 11: [Implementing Domain Driven Design] Summary Vietnamese.docx

VII. Domain Event

Khi tồn tại các quy tắc trong business như là “Nếu việc đó sảy ra….” Hay “khi

cái… phát sinh” bằng việc sử dụng domain event chúng ta có thể thiết kế một cách

đơn giản

1. Publish/Subscribe Pattern

Phương pháp cơ bản nhất của domain event là dựa Publish/Subscribe pattern (hay

Observer pattern)

Khi kết thúc sử lý của method doLast thì sẽpublish HogenEvent

public class Hoge {

private List<Consumer<HogeEvent>> subscribers = new List<>();

public void doTask() {

...

if (...) {

this.publish(new HogeEvent(...));

}

}

public void subscribe(Consumer<HogeEvent> subscriber) {

this.subscribers.add(subscriber);

}

private void publish(HogeEvent event) {

for (Consumer<HogeEvent> subscriber in this.subscribers) {

subscriber.accept(event);

}

}

}

11

Page 12: [Implementing Domain Driven Design] Summary Vietnamese.docx

Fuga class thì đăng ký HogeEvent

public class Fuga {

public Fuga(Hoge hoge) {

hoge.subscribe(event -> this.handle(event));

}

private void handle(HogeEvent event) {

// HogeEvent を受け取って処理

}

}

Ta có thể thấy source code đã trở nên phức tạp nhưng nó cũng tạo ra những điểm

lợi riêng

① Publisher không cần phải biết hết về Subcriber

Vì Hoge class chỉ cung cấp subscribe method tới các bộ phận bên ngoài nên

chúng không cần quan tâm xem ai đã gọi subcribe.ngoài Fuga subcrible thì cho

dù subcribe có tăng lên hay không thì nó cũng không cần thay đổi sử lý mà vẫn

hoàn thành được

② Subcriber không cần phải biết hết về Publisher

Hogen class cung cấp method doTask() nếu thỏa mãn điều kiện if. Tóm lại là chỉ

cung phát sinh HogeEvent khi thỏa mãn điều kiện ngoài ra thì Fuga class không gọi

đến Hoge Class. Fuga class chỉ cần chờ cho đến khi phát sinh event.

Làm giống như thế thì chúng ta có thể làm giảm sự phụ thuộc giữa 2 class

12

Page 13: [Implementing Domain Driven Design] Summary Vietnamese.docx

2. Modeling Events

① Tên class event

Giống như ví dụ sau đây chúng ta đặt tên event mà nó thể hiện dc là event đó

làm j

Khi thực viện command (method): BacklogItem#commitTo(Sprint sprint)

Khi đó sẽ phát sinh event với tên: BacklogItemCommitted

② Thông tin trong event class

Chúng ta cần phải lấy những thông tin j trong event? Về việc đó thì chúng ta

cần phải bàn riêng cho các event khác nhau. Có thể là những thông tin đặc trưng

của event đó, hay những thông tin đặc trưng của event đó với những event khác,

Cũng có thể là những thuộc tính như giá trị trước khi update, sau khi update.

③ tính bất biến của event

Vì những thông tin của event thì không cần phải đổi đi đổi lại (switch) nên

chúng ta sẽ thiết kế chúng thành các constant class để sau này không thể thay đổi

dc.

3. Handling Events

Việc đăng ký subscriber được xử lý bởi application service và domain service. Biểu đồ

sequence diagram bên dưới mô tả luồng xử lý Publish/Subscribe domain event, sử

dụng class chung để publish domain event, được gọi là DomainEventPublisher.

13

Page 14: [Implementing Domain Driven Design] Summary Vietnamese.docx

Nội dung của method handleEvent cuối cùng là khi tạo ra Subscriber, thì sẽ mô tả vào

application service.

Chuỗi xử lý này sẽ được thực thi một cách đồng bộ (synchronous). Sau đó, việc điều

khiển transaction sẽ do application service đảm nhiệm. Do đó, chúng ta không nên mô

tả xử lý kiểu như thay đổi trạng thái của những aggregate khác trong handleEvent.

Điều này sẽ vi phạm quy tắc không thay đổi nhiều aggregate trong một transaction

đơn lẻ.

Nếu muốn thay đổi 1 aggregate khác sau khi đã nhận được event, nhất định phải đảm

bảo được tính toàn vẹn của kết quả bằng cách sử dụng những cơ chế bất đồng bộ

(asynchronous).

Trong trường hợp tính toàn vẹn của kết quả được tuân thủ, sự phụ thuộc vào cơ chế

được sử dụng, ví dụ như từ khi thay đổi aggregate thứ nhất cho đến khi tính toàn vẹn

của việc thay đổi aggregate thứ 2 được đảm bảo, có thể gây ra trễ khoảng một vài mili

giây. Do đó, tùy thuộc vào mục tiêu của mỗi công việc cụ thể, trễ đến một mức độ nào

đó được cho phép, cần có sự cân nhắc và đưa ra quyết định cụ thể trong từng trường

hợp riêng.

VIII. Aggregate

Aggregate là ranh giới của tính thống nhất của transaction (Transaction Consistency là

14

Page 15: [Implementing Domain Driven Design] Summary Vietnamese.docx

ACID) trong domain model. Nếu có một tập hợp một hoặc nhiều entity (Object Value)

luôn đảm bảo được tính thống nhất của transaction, thì có thể thiết kế và coi chúng

như một aggregate.

1. Aggragate rất lớn

Đây là domain model của “Scrum-based project management tool”. Product là

aggregate root. Trong aggregate này gồm có 3 entity là BacklogItem, Release và

Sprint.

Aggregate là ranh giới của tính thống nhất của transaction. Khái quát lại trong thiết kế

này có những vấn đề sau:

(a) Hai người dùng không thể cập nhật đồng thời vào những BacklogItem khác nhau.

(b)Hai người dùng không thể cập nhật đồng thời vào mỗi BacklogItem và Sprint.

(c) Hai người dùng …

Như vậy, tất cả các entity thuộc trong một Product đều bị lock đối với các entity khác.

Điều này khiến cho application trở nên rất bất tiện.

2. Aggragate review

Duyệt lại yêu cầu nghiệp vụ, theo như kết quả sau khi kiểm tra lại ranh giới của tính

thống nhất của transaction, Product, BacklogItem, Release, Sprint có thể được chia

thành những aggregate khác nhau.

Nhưng thậm chí sau khi đã phân tách aggregate, điều kiện bắt buộc phải gắn kết

15

Page 16: [Implementing Domain Driven Design] Summary Vietnamese.docx

BacklogItem, Release và Sprint vào Product vẫn không thay đổi. Do đó, ta chuyển

sang hướng thiết kế lấy ProductId làm key như sau:

Theo như sơ đồ trên, khi tham chiếu từ aggregate này tới aggregate khác, chúng ta kết

nối gián tiếp bằng cách sử dụng định danh (Identifier).

3. Transactional Consistency

Để tìm ra aggregate thì cần thiết phải hiểu được một cách chính xác phạm vi của tính

thống nhất của transaction mà model phải đảm bảo. Ở đây, quan trọng không phải là

tính thống nhất của kết quả (Eventual Consistency) mà cần phải chú ý đến tính thống

nhất của transaction (Transactional Consistency).

Tuy nhiên, thật khó để xác định chính xác điều này ngay từ đầu. Sau khi đã xác định

được “Aggregate này không quá lớn”, tạm thời có thể coi nó là một trong những

aggregate, sau đó sẽ kiểm tra lại sau.

4. 結果整合性 (Eventual Consistency)

Những quy tắc nghiệp vụ như thay đổi một lần nhiều aggregate, được giải quyết bằng

tính toàn vẹn của kết quả, đã sử dụng xử lý batch theo chu kỳ và xử lý event bất đồng

bộ.

Bằng cách như vậy, phạm vi của transaction được mở rộng, ngăn chặn sự sụt giảm

performance của application.

16

Page 17: [Implementing Domain Driven Design] Summary Vietnamese.docx

5. Cấu hình value object

Những phần của aggregate cố gắng sử dụng nhiều Value Object hơn là sử dụng Entity.

6. Nguyên tắc Tell, Don't Ask

Về mặt sử dụng object aggregate, vẫn có thể hoàn thành thiết kế mà không cần truy

vấn trạng thái nội tại của aggregate.

Ví dụ về một đoạn code tệ

AnyAggregate agg = this.repository.find(aggregateId);

if (agg.getAnyObject().isAny()) {

agg.doTaskA();

} else {

agg.doTaskB();

}

Nếu kết thúc như design ở trên, một business logic sẽ được viết lặp lại ở nhiều nơi. Nó

có thể gây ra bug và việc thay đổi những logic này cũng trở nên khó khăn hơn.

Thiết kế kiểu thay đổi xử lý để nhìn được nội trạng thái của aggregate cần được

tránh, nên làm thiết kế chỉ chấp nhận những câu lệnh đơn giản.

17

Page 18: [Implementing Domain Driven Design] Summary Vietnamese.docx

Ví dụ về một đoạn code sạch

AnyAggregate agg = this.repository.find(aggregateId);

agg.doTask();

...

// AnyAggregate クラスの doTask

void doTask() {

if (this.getAnyObject().isAny()) {

this.doTaskA();

} else {

this.doTaskB();

}

}

Nếu làm như vậy, một ngày nào đó khi muốn thay đổi những logic này, chỉ cần thay

đổi 1 phần của class AnyAggregate.

IX. Factory

Nếu có những aggregate rất phức tạp, xử lý của constructor của aggregate trở nên rất

dài, khi đó hãy sử dụng factory. Factory không chỉ là class chuyên dụng mà nó còn có

factory method để tạo ra aggregate khác trong aggregate root, và nó cũng có thể đảm

nhận vai trò như một factory trong domain service.

1. Factory Method on Aggregate Root

Giả sử có một model trong nhận thức như biểu đồ sau.

18

Page 19: [Implementing Domain Driven Design] Summary Vietnamese.docx

Dựa trên model ý tưởng, aggregate được thiết kế một cách phù hợp, tạo kết nối bằng

định danh, cuối cùng được biểu đồ như sau:

Trên model ý tưởng, BacklogItem được đặt ở bên dưới Product. Trong trường hợp này,

triển khai factory method để tạo ra aggregate khác trong aggregate root.

Lập kế hoạch BacklogItem mới trong Product

19

Page 20: [Implementing Domain Driven Design] Summary Vietnamese.docx

public classs Product {

...

public BacklogItem planBacklogItem(

BacklogItemId aNewBacklogItemId,

String aSummary,

String aCategory,

BacklogItemType aType,

StoryPoints aStoryPoints) {

BacklogItem backlogItem =

new BacklogItem(

this.tenantId(),

this.productId(),

aNewBacklogItemId,

aSummary,

aCategory,

aType,

BacklogItemStatus.PLANNED,

aStoryPoints);

DomainEventPublisher

.instance()

.publish(new ProductBacklogItemPlanned(

backlogItem.tenantId(),

backlogItem.productId(),

backlogItem.backlogItemId(),

backlogItem.summary(),

backlogItem.category(),

backlogItem.type(),

backlogItem.storyPoints()));

20

Page 21: [Implementing Domain Driven Design] Summary Vietnamese.docx

return backlogItem;

}

}

Có một điểm quan trọng trong đoạn code này là việc copy tenantId và productId vào

BacklogItem mới.

BacklogItem là một aggregate khác với Product, để khái niệm trên gắn vào Product thì

cần phải copy như vậy. Bằng cách triển khai với tư cách như factory method trong

aggregate, ta có thể thể hiện kết nối trên ý tưởng đó trong domain.

Nếu viết là tạo instance của BacklogItem bằng application service, điều này

(BacklogItem và Product có quan hệ với nhau) sẽ bị rò rỉ ra bên ngoài domain.

2. Factory on Service

Dựa trên thông tin được truyền từ boundary context khác, ví dụ để tạo một class trong

context của nó, một vài service class chịu trách nhiệm cho factory.

X. Repository

Repository đảm nhiệm việc persist aggregate (như là lưu vào cơ sở dữ liệu). Kiến thức

về mặt kỹ thuật của persistence (như là O/R Mapper) được che giấu vào bên trong

repository. Về cơ bản, aggregate và repository được tạo theo quan hệ 1-1.

Để thiết kế repository, có một vài cách.

21

Page 22: [Implementing Domain Driven Design] Summary Vietnamese.docx

1. Collection-Oriented Repositories

Chúng ta sẽ thiết kế các interface giống như kiểu collection (List, Map..) để thao

tác với repository. Ví dụ như chúng sẽ có các method find, add, remove…

Ropository hướng Colletion sẽ sử dụng như sau:

...

Calendar calendar = new Calendar(calendarId, "Project Calendar", ...);

calendarRepository.add(calendar);

...

Calendar calendarToRename = calendarRepository.find(calendarId);

calendarToRename.rename("CollabOvation Project Calendar");

...

Trong ví dụ này thì không có những method như là save. Trong trường hợp chúng

ta thiết kế hướng collection, vì calendarRepository chứa instance

(calendarToRename) nên khi chúng ta update bằng method rename thì không cần

phải thông báo lại cho calendarRepository

2. Persistence-Oritented Repositories

Nếu không thể sử dụng collection oriented repository thì chúng ta sẽ dung

persistence oriented repository

22

Page 23: [Implementing Domain Driven Design] Summary Vietnamese.docx

Persistence oriented repository sẽ thực hiện như sau

...

Product product = new Product(...);

productRepository.save(product);

...

Product product = productRepository.find(tenantId, productId);

product.reprioritizeFrom(backlogItemId, orderOfPriority);

productRepository.save(product);

...

3. Đóng gói, tạo ra repository

Chúng ta có 2 bước để tạo ra repository

① tạo repository interface

Chúng ta để các repository interface vào chung 1 package. Tóm lại là để vào

trong domain model

② Tạo ra class thao tác với repository

Để tao tác với repository thì chúng ta có nhiều yếu tố mang tính kỹ thuật như or

mapper hay database nên chúng ta nên tách biệt với domain model. Vì thế chúng

ta nên tạo các class thao tác với repository khác package với domain model

23

Page 24: [Implementing Domain Driven Design] Summary Vietnamese.docx

4. Managing Transactions

Về quản lý transaction sẽ được giải thích ở application service ở chương sau.

Nhưng những sử lỹ của nó có thể thực hiện giống như sau:

public class SomeApplicationServiceFacade {

....

public void doSomeUseCaseTask() {

Transaction transaction = null;

try {

transaction = this.session().beginTransaction();

// ここでリポジトリを呼び出したり、集約を変更したりする

transaction.commit();

} catch (Exception e) {

if (transaction != null) ();transaction.rollback

}

}

}

24

Page 25: [Implementing Domain Driven Design] Summary Vietnamese.docx

UI

Aggregate

Aggregate

Aggregate

Aggregate

Source code ở trên chỉ là hình dung về các sử lý còn trong thực tế thì thay vì

chúng ta phải viết đi viết lại những đoạn code như thế thì chúng ta sẽ sử dụng các

chức năng của framework nên mọi thứ sẽ trở nên đơn giản hơn

XI. Application

1. User Interface

Tại một thời điểm chúng ta chỉ có 1 aggregate để thay đổi, update nhưng lại có

nhiều aggregate về mặt hiển thị như hình sau

Để hiển thị được nhiều tập hợp (aggragate) đối với 1 màn hình thì chúng ta thực

hiện copy thông tin của aggragete đó vào 1 cái j đó ví dụ như DTO chẳng hạn sau đó

truyền lên màn hình.

① DTO (Data Transfer Object) Đối tượng truyền giữ liệu

DTO là object mà tập hợp toàn bộ thông tin cần thiết để hiển thị lên màn hình.

Trong trường hợp sử dụng DTO thì application service sẽ lấy các instance của

aggragate từ repository rồi copy thông tin từ instance đó sang DTO.Thao tác copy

đó sẽ được giao cho DTO assembly class (Không thao tác trực tiếp trên tầng

application service )

25

Page 26: [Implementing Domain Driven Design] Summary Vietnamese.docx

public class SomeDtoAssembler {

public SomeDto assemble(SomeAggregate agg) {

SomeDto dto = new SomeDto();

dto.setId(agg.getId());

dto.setName(agg.getName());

dto.setValue(agg.getChildObject().getSomeValue());

...

return dto;

}

}

Khuyết điểm của phương pháp này là DTO assembly class phải biết thông tin

bên trong của aggragate (sẽ tạo sự liên kết chặt chẽ giữa fomain và client hight

coupling)

② (Mediator) Trung gian

Để khắc phục yếu điểm của DTO assembly thì chúng ta sẽ sử dụng interface

trung gian ở aggragate.

Ở hình dưới đây thì chúng ta sẽ tạo ra DTO bằng cách sử dụng Mediator

(Trung gian) để có thể sử dụng 1 aggragate trên 2 màn hình (application service )

26

Page 27: [Implementing Domain Driven Design] Summary Vietnamese.docx

Chúng ta sẽ tạo ra các Mediator interface chuyên dụng cho từng aggragate và

cũng tạo ra các class thực thi cho từng màn hình

Chúng ta sẽ truyền interface của Mediator sang aggragate và aggragate sử dụng

Double Displatch để truyền từng thông tin 1 của nó đối với Mediator

27

Page 28: [Implementing Domain Driven Design] Summary Vietnamese.docx

public class BacklogItem ... {

....

public void provideBacklogItemInterest(BacklogItemInterest interest) {

interest.informTenantId(this.tenantId());

interest.informProductId(this.productId());

interest.informBacklogItemId(this.backlogItemId());

interest.informSummary(this.summary());

...

}

public void provideTasksInterest(TasksInterest interest) {

Set<Task> tasks = this.allTasks();

interest.informTaskCount(tasks.size());

for (Task task : tasks) {

...

}

}

...

}

Điều quan trọng là Mediator không liên quan tới client. Vì Mediator chỉ lấy các

thông tin của aggragate nên việc lưu trữ thông tin đó vào DTO như thế nào sẽ

được thực hiện ở class thực thi của mediator.

③ DPO (Domain Payload Object)

Vì DTO là việc copy instance của aggragate nên sẽ rất tốn bộ nhớ. Để giải

quyết vấn đề đó chúng ta sẽ sử dụng DPO.

Giống như DTO thì DPO cũng cung cấp interface (getter) nhưng nó sẽ chứa

instance của aggragate.Các getter sẽ lấy trực tiếp thông tin của aggragate rồi trả

về. Chúng ta có thể sử dụng wrapper class của aggragate để thực hiện việc đó.

Và nếu làm như thế thì DPO cũng gặp những vấn đề như DTO nên để giải

quyết vấn đề đó thì chúng ta cũng sẽ sử dụng các class trung gian (Mediator)

28

Page 29: [Implementing Domain Driven Design] Summary Vietnamese.docx

2. Application Service

Application service là client mà kết nối trực tiếp tới Domain model

Như được miêu tả thì ở tầng này sẽ phụ trách về quản lý section hay những vẫn đề

liên quan tới security.

29