上一篇文章我們有提到 domain model 以後,接下來我們可以來說說 DDD Trilemma,這個東西就是主要在說 domain model 設計上的 Trilemma,然後最早提到這個問題的應該是從這篇文章來的。
Domain model purity vs. domain model completeness (DDD Trilemma)
接下來就來談談這個東西 ~ 因為這個有個東西很容易會讓domain model 變歪呢
。
首先三難困境的三種特性 :
然後三難困境就是在說 :
任何一種實作方法,都只能達成以上兩種,不可能三種都有。
我們先假設一個需求情境 :
假設我們有個需求是
用戶註冊,然後需要檢查服務中有沒有已經存在的 email 了
。
然後根據文中會有三種寫法:
圖片來源: Domain model purity vs. domain model completeness (DDD Trilemma)
這種就是會先將所有的 email 拿出來,然後丟到 domain model 中來做業務判斷。然後根據文中它符合了以下兩點 :
// Service
class UserService {
constructor(private repository: UserRepository) {}
async registerUser(newEmail: string) {
const existEmails = repository.getEmails();
const user = new User();
user.register(newEmail, existEmails);
this.repository.save(user);
}
}
// Domain Model
class User {
constructor() {}
register(newEmail: string, existEmails: string[]) {
if(existEmails.include(newEmail)){
throw new Error('the email is existent')
}
this.email = newEmail;
}
}
// Service
class UserService {
constructor(private repository: UserRepository) {}
async registerUser(newEmail: string) {
const user = new User();
await user.register(newEmail, this.repository);
}
}
// Domain Model
class User {
constructor() {}
async register(newEmail: string, repository: UserRepository) {
const existingUser = await repository.getByEmail(newEmail);
if (existingUser) {
throw new Error('the email has existent');
}
this.email = newEmail;
await repository.save(this);
}
}
// Service
class UserService {
constructor(private repository: UserRepository) {}
registerUser(newEmail: string) {
const existingUser = repository.getByEmail(newEmail);
if (existingUser) {
throw new Error('the email has existent')
}
const user = new User();
user.register(newEmail);
repository.save(user);
}
}
// Domain Model
class User {
constructor() {}
register(newEmail: string) {
this.email = newEmail;
}
}
根據上面的範例與下面這張圖應該可以得出,我們只要使用 domain model 那就不可能三個特性都全部達成,那這樣的話,那一個種比較好呢 ?
圖片來源: Domain model purity vs. domain model completeness (DDD Trilemma)
文中作者是說 :
I strongly recommend that you choose domain model purity over domain model completeness.and go with the third approach: splitting the decision-making process between the domain layer and controllers.
就是我們第三種方案的實作 :
理由如下:
整體來說我很讚成作者給的建議,並且目前在開發上我們也都是用第三種模式來開發,主要的原因如下 :
軟體工程中
性能與維護
這個是個兩難與 Tradeoff,這篇文章很明顯的表達了這件事情。
對了 ~ 但我有個情況下,會在 domain model 在呼叫 out-of-process 的東西,因為我發現 domain model 裡的所有欄位變化,都很依賴那個,也就是沒有呼叫,那我 domain model 事實上沒什麼用,所有東西都是從外部丟進來的。
別忘了 domain model 是為了解決業務複雜變化的工具,如果有個 domain model 真很吃 out-of-process 的情況下,我覺得 ok ~ 我記得那時是在設計第三方金流服務的 payment domain model,我那時最初也是 purity 的寫法,但寫完後發現,我要這個 domain model 要幹啥呢 ? 所有變化與欄位都需要的東西都要從外部金流帶入呢 ~