Upload
anonymous-22j6wnz1r
View
247
Download
2
Embed Size (px)
Citation preview
日通システム株式会社
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
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
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
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
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
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
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
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
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 }
② 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 }
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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