2012-09-10 97 views
2

當涉及多個聚合時,我懷疑域應該如何執行業務規則。域驅動設計:服務和聚合中的域規則

假設我有帳戶和外部帳戶聚集:

public class Account { 
    public String getId() {...} 
    public void add (Double amount) {} 
} 

public class ExternalAccount { 
    public String getId() {...} 
    public void add (Double amount) {} 
} 

這個服務:

public class TransferService implements TransferServiceInterface { 
    public void transfer (String AccountId, String ExternalAccountId, Double amount) { 
     Account fromAccount = accRepository.get(AccountId); 
     ExternalAccount toAccount = extAccRepository.get(ExternalAccountId); 

     transferIsValid(fromAccount, toAccount, amount); 

     fromAccount.add(-amount); 
     toAccount.add(amount); 
    } 
} 

transferIsValid將拋出一個異常,如果轉讓不符合域規則的規定。

我怎樣才能防止這種模式的用戶從沒有使用服務,並執行這樣的事情:

Account fromAccount = accRepository.get(AccountId); 
    ExternalAccount toAccount = extAccRepository.get(ExternalAccountId); 

    fromAccount.add(-amount); 
    toAccount.add(amount); 

用戶沒有使用這項服務並沒有使用transferIsValid(... )來檢查完整性。我相信我的設計中存在一個錯誤,因爲用戶不應該能夠做一些無效的事情。我怎樣才能防止它?我的設計中的錯誤在哪裏?

+1

這是什麼意思「用戶」在這裏,用戶如何調用上面列出的代碼而不調用服務? –

+0

當我提到「用戶」時,我指的是使用該域的開發人員。開發人員可能決定不使用該服務,並直接使用聚合。除了代碼重複之外,不保證開發人員會執行業務規則。 – Pleonc

+0

爲什麼開發者可能決定在他們自己創建這些服務時不使用這些服務? –

回答

4

首先:不要用Add()退出。 DDD是關於域名的。當您與產品所有者交談時,我認爲您不會說So when I add a negative amount of money to account A, the equal amount will be added to account B。添加Widthdraw方法。


請記住。編碼時不涉及用戶。程序員是。所有程序員都可以搞砸代碼。

關於服務:您無法通過代碼來阻止該服務。除非唯一有效的提款方式是將其轉入另一賬戶。在這種情況下,您可以更改Widthdraw()方法以將另一個帳戶作爲參數。

除此之外,只需將文檔添加到Widthdraw方法中,並說如果涉及兩個帳戶應該使用該服務。因此,任何DDD開發人員都應該知道該服務應該被使用,因爲這是我們在DDD中做的事情(我做過你的&,以及下一個有DDD經驗的開發人員)。

1

業務邏輯應該在域對象,因此,而不是把業務邏輯TransferService,更好的方法,我想,以避免業務邏輯泄露到服務是創建新的實體,稱爲AccountTransfer與含有AccountFromAccountTo ,像(抱歉,我使用C#在這裏):

public class AccountTransfer 
{ 
    Account From { get; set; } 
    Account To { get; set; } 

    // More properties   

    private bool IsValid(ammount) 
    {} 

    public void DoTransfer(int amount) 
    { 
     is (IsValid(ammount)) 
     { 
      From.Withdraw(amount); 
      To.Add(amount); 
     } 
    } 
} 

您可能需要更多的信息,對象AccountTransfer,如:

  1. 何時轉賬
  2. 什麼樣的轉賬:通過visa,paypal轉賬。
  3. 要將此類填充到數據庫中,您需要存儲轉帳歷史記錄以便稍後跟蹤它們。

通過這種方式,您還可以將IsValid方法作爲私有方法放入AccountTransfer中。

+0

這與使用該服務並沒有什麼區別。它仍然不能執行(開發者仍然可以直接使用帳戶)。而'AccountTransfer'並不是一個真正的域模型。它不代表一個實體。根據DDD定義,這只是一個具有奇怪名稱的服務。 – jgauffin

+0

@jgauffin:爲什麼你認爲AccountTransfer不是真正的域模型?它還包含其他屬性,例如何時傳輸,什麼樣的傳輸......以及將此實體填充到數據庫中。這也使服務和AccountTransfer –

+0

@CuongLe「業務邏輯應該在域對象」 - 如果你的意思是一個實體,不一定。似乎不適合任何實體或跨越多個實體的域邏輯可放置在域服務中。而明智的DDD仍然算作常規域邏輯。 – guillaume31