2010-01-19 115 views
5

比方說,我們有一個類型爲Order的聚合根實體,它與客戶和訂單行相關。當我考慮一個訂單實體時,將其概念化爲沒有Id的定義更自然。沒有Id的訂單似乎更好地表示爲訂單請求而非訂單。應該如何表示將聚合根添加到存儲庫?

到訂單添加到資料庫,我平時見人實例沒有編號的順序,然後讓庫完成對象:

class OrderRepository 
{ 
    void Add(Order order) 
    { 
     // Insert order into db and populate Id of new order 
    } 
} 

我喜歡這種做法是要添加將實例排序到OrderRepository。這很有道理。但是,訂單實例沒有Id,並且在存儲庫的使用者範圍內,對於我而言訂單沒有Id是沒有意義的。我可以將OrderRequest定義爲一個訂單實例並將其添加到存儲庫,但感覺就像從橙色中獲取蘋果,然後將其添加到橙子列表中。

另外,我也看到了這種做法:

class OrderRepository 
{ 
    Order AddOrder(Customer customer) 
     // It might be better to call this CreateOrder 
    { 
     // Insert record into db and return a new instance of Order 
    } 
} 

我喜歡這種方法是一種順序是不確定沒有ID。存儲庫可以創建數據庫記錄並在創建和返回訂單實例之前收集所有必填字段。這裏有什麼味道是事實上,你從來沒有實際上將一個訂單的實例添加到存儲庫。

無論哪種方式的作品,所以我的問題是:我必須生活在這兩種解釋之一,還是有最佳做法來建模插入?

我發現這個答案是相似的,但對於價值對象: how should i add an object into a collection maintained by aggregate root。當涉及一個值對象時,不會有任何混淆,但是我的問題涉及到一個來自外部源的實體(自動生成的數據庫ID)。

+0

我認爲沒有身份證的訂單根本不是訂單實體,這是您的第一個初步想法來自哪裏。爲了解決這個問題,你必須記住你的對象的狀態。將'狀態'看作是一段時間的停頓。即,網頁請求。在創建一個訂單的過程中,是的,它最初沒有一個Id,但你尚未完成它的狀態 - 你將它添加到存儲庫中,從而完成Order()的持久化狀態,現在是完成。 – eduncan911 2010-01-19 21:04:40

+0

我並不同意你的觀點,但是如果你聲稱一個沒有和Id的訂單是一個短暫停頓的實體,在這個暫停期間它將沒有可辨別的身份。我認爲,至少在語義上,這是與實體定義的矛盾。 – 2010-01-19 21:17:26

+0

身份不像你想象的那樣枯燥無味。根據我的信念,我們的身份是我們獨特的靈魂,從我們的概念出現。但對政府來說,我們的身份可以通過多種方式建立:出生證明,護照等等。有一個時間點兒童沒有政府的官方身份,但政府仍然認爲它是一個實體,儘管需要身份證明。把訂單想象成一個寶貴的小寶貝,並將其作爲政府的存儲庫(無論如何,整個系統都更加用戶友好!):) – tuespetre 2014-05-23 04:14:18

回答

5

我想先排除第二種方法。這不僅違背了直覺,還違反了幾項優秀的設計原則,如Command-Query SeparationPrinciple of Least Surprise

其餘選項取決於域邏輯。如果域從邏輯上講,沒有ID的順序是無意義的,該ID是訂單的要求不變,我們必須使之型號:

public class Order 
{ 
    private readonly int id; 

    public Order(int id) 
    { 
     // consider a Guard Clause here if you have constraints on the ID 
     this.id = id; 
    } 
} 

注意,通過標記id場爲readonly,我們一直將一個不變的。我們無法改變它給定的Order實例。這完全符合Domain-Driven Design實體模式。

您可以通過在構造函數中放入一個Guard子句來進一步強制執行域邏輯,以防止ID爲負數或零。

現在,您可能想知道如何使用自動生成的數據庫ID。那麼,它不會。

沒有好方法確保提供的ID尚未使用。

這使得你有兩個選擇:

  • 更改ID爲GUID。這允許任何呼叫者爲新的訂單提供唯一的ID。但是,這需要您使用Guids作爲數據庫密鑰。
  • 更改API以便創建新訂單不需要Order對象,而是按照您的建議使用OrderRequest - OrderRequest可以與Order類幾乎相同,再減ID。

在許多情況下,創建新訂單是一種業務操作,無論如何需要特定的建模,所以我認爲沒有任何問題做出區分。雖然Order和OrderRequest在語義上可能非常相似,但它們甚至不必在類型層次結構中相關。

我甚至可能會走這麼遠說,他們不應相關,因爲OrderRequest是值對象而秩序是一個實體

如果採取這種方法,AddOrder方法必須返回一個Order實例(或者至少是ID),否則我們無法知道剛創建的訂單的ID。這導致我們回到CQS違規行爲,這就是爲什麼我傾向於更喜歡Guids的實體ID

+0

很好的答案!在我提出這個問題之前,我傾向於將API更改爲接受OrderRequest,並且我同意您應該將它作爲未包含在Order層次結構中的值對象。 但是,現在我真的很喜歡你的Guid建議。我可以定義一個Order構造函數,它知道如何將自己實例化爲唯一的,然後將其添加到存儲庫。聽起來很完美。然而,我很不安,因爲這種解決方案似乎超出了一般實踐。你有沒有遇到過很多你喜歡使用這個解決方案但不被允許的項目? – 2010-01-19 21:38:03

+0

你可能會遇到那些認爲這是一個壞主意的人有幾個原因。其中一些主要與Cargo Cult的應用架構方法有關,但阻力仍然可能是真實的。一般來說,使用Guids作爲鍵(而不是整數)會有一個(小)perf的開銷,但是到目前爲止,還沒有任何東西阻止我。反對Guids的最重要的論據是如果ID在應用程序之外使用,因爲它們真的很難在電話中大聲朗讀(作爲一個例子)。使用Guids解決了一些問題,但不是全部。我更喜歡Guids,但我也願意接受其他替代方案:) – 2010-01-19 22:23:32

+0

如果Order和OrderRequest是分開的,它將成爲維護地獄,不是嗎?我的意思是,由於他們完全相互反映,除了Id領域,他們將不得不一直共同進化。或者我錯過了什麼? – vorou 2013-03-01 08:06:47

相關問題