1

首先讓我描述一個點亮的位域。我們有一個網站,客戶可以下訂單。爲了下訂單,客戶必須提供一些數據。這個過程分爲幾個步驟。在每一步中,客戶端只提供部分數據。當客戶完成最後一步時 - 訂單所需的所有數據已準備就緒。DDD:實體在技術上,但看起來像價值對象?

所以我們有一個實體StepsProgression。裏面有一個價值對象數組「步驟」。他們不存儲任何東西,所以它們很簡單,非常適合作爲價值對象。但爲了堅持所有步驟中的用戶數據,在StepsProgression也有一個對象StepsData

然後麻煩來了。 StepsData將有setter,來設置用戶數據。所以它必須是一個實體。但從領域的角度來看,它不是一個實體。這是一個價值對象,因爲我不關心它的身份。我可以用相同的數據替換它,它是可以的。

你在這種情況下能推薦什麼?

編輯1

關於域名再次

我們確實有一個訂票系統。我們詢問了領域專家,我們確實有不同的步驟(或階段)來填寫一些特定的數據,以便爲用戶預訂訂購商品。所以Step's和StepsProgressions的概念是可以的。它不與UI結合。例如,在UI方面,我們同時填充兩個步驟的數據。

回答

1

一個值對象可以有getter和setter。看起來像你的情況StepsData描述實體的(StepsProgression)狀態,使其成爲值對象候選者。您可以在值對象本身中擁有值對象屬性。價值對象是自包含的,使得從根本上更容易處理。對於DDD純粹主義者來說,價值對象是不變的,無副作用並且易於測試。

+1

請注意setter:VO是不可變的,因此setter必須返回一個新對象 –

+0

謝謝! StepsData確實是類似於狀態的東西,因爲當我們向它灌注某些東西時,我們正在改變狀態。所以看起來我可以讓StepsData成爲一個帶setter的值對象,它會返回一個新的值對象,我將用它來替換舊的對象? – Clickbeetle

1

通過閱讀您的問題/描述,從我的罕見知識看來,我認爲您正在建立某種網上商店,預訂系統或類似的東西。

這個假設請仔細分析你的域名。問問你自己的問題,什麼你的域名是真的,如果StepsProgressionStepStepData真的是「域關注」這樣一個排序系統......?

就我個人而言,我覺得這些只是UI工作流程的抽象,並不反映任何領域特定的概念 - 純粹的應用技術視角。

在這種情況下,它們既不是實體也不是價值對象,因爲它們甚至不是域模型的一部分。

我建議要回白板和第一開始建模領域模型只包含域的特定對象(+等等),而無需用戶界面或使用記案件太多:

- Order (Entity) 
- OrderNumber (Value Object) 
- Customer (Entity) 
- PaymentType (Entity) 
- OrderTotal (Value Object) 
- … 

通過將它們組合到合適的聚集(事務邊界),將它們與存儲庫一起保存並使用域服務處理它們,您應該能夠創建一個「豐富」的域模型。

應用程序服務將使用您的現有域模型來編排您的應用程序的用例(即從用戶收集並保留相應數據的正確數據塊)。

後來一些規模較小的重構,以領域模型可能需要但請記住,用戶界面,應用程序或基礎設施的具體問題應遵循的域模型,而不是「泄漏」到它。

也許我的問題完全錯了:在這種情況下,造成不便。但是,正如我所看到的那樣,對整個域名和相關模式的一般性反思/質疑似乎很有幫助。

+0

感謝您的回覆!我們確實在談論訂票系統。預訂數據填寫過程分爲多個步驟,是我們的核心領域。這是我們比競爭對手的優勢。步驟模型與UI非常分離(因爲在UI中許多步驟都統一併位於一個頁面中)。所以你的建議和一般建議一樣好,但我們已經有單獨的Order模型和單獨的Steps模型。 – Clickbeetle

0

首重檢查StepsProgression域:如果我們問我們的進程步驟的領域專家,我認爲我們得到了一個可以接受的答案。然後我們可以說這是我們域的一部分。

接下來,你說的是這個實體還是VO?是的,它看起來像價值對象,實際上是一個價值對象,因爲你不關心整個數據集的身份,它沒有獨特的含義(如果我不明白domaing是錯誤的)。

假設你有一個像的操作UI的表單嚮導和每一步的投入是不同的,我想實現這樣的:

public class StepsProgression 
{ 
    private readonly string _userId; 
    public Step1 step1 { get; set; } 
    public Step2 step2 { get; set; } 
    public Step3 step3 { get; set; } 

    public StepsProgression(string userId) 
    { 
     _userId = userId; 
    } 
} 
// Immutable Step Object 
public class Step1 
{ 
    public string input1 { get; } 
    public string input2 { get; } 
    public string input3 { get; } 
} 

您實例StepsProgression中的第一步,所以我假定你只知道他們的步驟是。所以構造函數只設置用戶ID。然後,當用戶填寫第一步輸入和點擊下一頁然後創建一個新的(immutable)的步驟對象,並將其設置爲第一步。如果用戶點擊,從第一步改變任何東西,然後再次單擊接下來,然後你再創建一個新的第一步對象並將其分配給StepsProgression。

你能想到的更通用的實現(步驟),但由於輸入是步驟之間的所有不同,這種明確的方式爲您提供了編碼約定。

執行編輯: 據我所知,StepsProgressions包含動態一套你剛剛創建StepsProgression之前確定的步驟。然後,假設你知道stepTypes事先我會改變我的實現如下:

public class StepsProgression 
{ 
    private readonly string _userId; 
    private readonly IStepProgressionBuilder _builder = new StepProgressionBuilder(); 
    public IEnumerable<IStep> Steps { get; set; } 

    public StepsProgression(string userId, IEnumerable<string> stepTypes) 
    { 
     _userId = userId; 
     foreach (var step in stepTypes) // foreach smells here but you got the point 
     { 
      _builder.AddConcreteStep(step) // step = "Step1" for instance. 
     } 
     Steps = _builder.Build(); 
    } 
} 
// Immutable Step Object 
public class Step1 : IStep 
{ 
    public string input1 { get; } 
    public string input2 { get; } 
    public string input3 { get; } 
} 
// Builder Pattern with Fluent Methods 
public interface IStepProgressionBuilder 
{ 
    // stepType = "step1" for instance. You have concrete Step1 class, so you return it. 
    // Start with a switch, then you refactor. (May involve some reflection) 
    void AddConcreteStep(string stepType); 
    IEnumerable<IStep> Build(); 
} 

同樣,你可以使用一個通用的步驟類,但隨後你的建設者應該shomehow建立通過採取這一步驟應包含每個數據的步驟。如果你的步驟可能有超過3個屬性,這將變成一個代碼。這就是爲什麼我更喜歡有具體的步驟類,以便我可以將其構建爲一個完整的數據集。

+0

謝謝你的回覆。我們問領域專家和步驟是好的,但步驟不耦合到用戶界面。我喜歡你的例子,但是我想在創建StepsProgression的時候已經有了Steps的實例。因爲步驟級數可以有很多不同的步驟,所以一個StepsProgression可以完全不同於另一個。我需要一種方法來訪問一個StepsProgression中所有步驟的所有數據,這就是爲什麼需要StepsData。 – Clickbeetle

+0

據我所知,您的StepsProgression包含一個「動態」步驟集,可能因表單嚮導的不同而不同,您希望使用通用的StepsProgression類。如果我沒有錯,我按照您的需求編輯瞭解決方案。 – stratovarius

0

因此最後我選擇將StepsData作爲Value Object。它給了我在StepsProgression以外的寫保護的好處。

我可以通過StepsProgression以外的StepsData只讀,不用擔心,因爲如果外面的某個人在StepsData上調用setter,則會返回一個新對象。在StepsProgression之外,您不能重寫原始StepsData。所以即使有人打電話給setter,StepsProgression中的原始StepsData仍然不變。