2013-01-01 26 views
3

我想在當前的項目中應用DDD的校長。我會試着用一個希望有意義的例子來問這個(冗長的)問題。將DTO界面傳遞到域工廠?

創建新成員時,我的表示層調用應用程序層中定義的MemberService.CreateMember(MemberDTO memberDTO)。

我的表現層有這樣的事情:

MemberDTO member = new MemberDTO(); //Defined in Application Layer 

member.Username = username; 
member.Password = password; 
//...etc 

我的應用層調用領域層下面的工廠方法來創建成員:

public static Member MemberFactory.CreateMember(string memberDTO.Username, string memberDTO.Password...) 
{ 
    var member = new Member(); //Domain.Model.Member 

    member.Id = GenerateIdentity(); 

    member.Username = memberDTO.Username; 

    //... etc 

    return member; 
} 

會員被傳遞迴MemberService(應用層)將其保存(存儲庫位於基礎結構層)並將其映射到MemberDTO(使用AutoMapper)並將其傳回到表示層。

因此,我的表示層是在MemberDTO中設置值,然後我的域圖層(通過工廠)正在採取單獨的參數並設置成員的值。沒有工廠,它會很簡單,但我在這裏生成Id。創建一個域服務而不是生成該Id是否是錯誤的?例如,更改從該MemberService.CreateMember(MemberDTO memberDTO)方法:

public MemberDTO CreateMember(MemberDTO memberDTO) 
{ 
    var member = MemberFactory.CreateMember(memberDTO.Username, memberDTO.Password); 
    //Domain.Model.Member 

    SaveMember(member); 

    //Pass DTO to presentation layer 
    return Mapper.Map<Member, MemberDTO>(member); 
} 

要這樣:

public MemberDTO CreateMember(MemberDTO memberDTO) 
{ 
    var member = new Member(); //Domain.Model.Member 

    Mapper.Map<MemberDTO, Member>(memberDTO); 

    //Add this method into a Domain Service to generate the ID and any other defaults 
    Domain.MemberService.Initialise(member); 

    SaveMember(member); 

    //Pass DTO to presentation layer 
    return Mapper.Map<Member, MemberDTO>(member); 
} 

對不起,囉嗦的問題,儘管答案可能是一個簡單的是或沒有!

回答

1

我不確定這個問題與DDD的關係,因爲我沒有看到需要額外複雜度的複雜域。但是,在另一個結構中定義身份的生成可能有一個爭論,但這主要是爲了實現單一責任(SRP)。

我建議您創建一個名爲IGenerateMemberIdentity的接口。然後,您可以分離出這種複雜性並對其進行測試。我認爲你的直覺認爲這個功能不屬於工廠,這是正確的。如果這是一個複雜的例程,它似乎不是關於對象創建。

但是,對於我來說,Initialize靜態方法看起來像是一種代碼異味。最好在提供該功能的接口後注入依賴項。

要專門回答你的問題。不,我不認爲這是錯的。事實上,爲了保持純粹的業務功能而建模的複雜性是DDD最擅長的。

+0

謝謝@dtryon,我簡化了這個例子的域名。唯一的事情就是在'To this:'部分,我不再使用Factory來創建成員的實例。這就是爲什麼我認爲像'Initialise'方法也設置默認值,即。 'Member.IsVerified = false;'或者我應該只在會員的構造函數中設置它們? –

+0

如果'Member.IsVerified'是一個布爾值,默認情況下它將被設置爲false。如果你有更復雜的創建,我會看看[Builder](http://www.dofactory.com/Patterns/PatternBuilder.aspx)這樣的模式。不過,我建議您分別關注與配置,默認值和身份生成相關的問題。在與成員打交道時,這些看起來都是獨立的問題。 –

+0

謝謝,正如我對Aaron所說的,在這種情況下,MemberFactory只是一個開銷,因爲我將DTO中的各個參數傳遞到工廠並手動將這些參數映射到域模型。 –

2

對於DDD,如果此標識不是域標識(即基於有界上下文定義用戶的標識),則它不屬於您的域模型。在我看到的大多數情況下,用戶名是用戶在有界上下文中的身份,並且爲了訪問數據庫,將生成任何生成的ID。我懷疑這可能是你的情況,如果我是對的,那麼生成的ID是一個基礎設施問題,應該由你的存儲庫實現完成。你的域名不應該有這個生成的ID的任何概念。

另一方面,假設用戶名是用戶在上下文中的標識,並且您想使用某個域公式生成該標識。在這種情況下,生成身份將是域服務方法,因爲它不屬於任何域對象的實例。然後將生成的ID將被傳遞到您的工廠,你會:

public MemberDTO CreateMember(MemberDTO memberDTO) 
{ 
    //Domain.Model.Member 
    var member = MemberFactory.CreateMember(MemberService.GenerateUserName(), memberDTO.Password); 

    SaveMember(member); //Repository method? 

    //Pass DTO to presentation layer 
    return Mapper.Map<Member, MemberDTO>(member); 
} 

總之,答案是否定的,那就是錯誤的創建一個域服務來生成ID假設ID是有界情境的實際身份。事實上,這是你應該做的。但是,如果ID不是有界上下文的用戶身份,那麼讓您的存儲庫擔心如何生成ID,訪問數據庫以及轉換爲/從您的域模型。

+0

感謝@Aaron,這是有道理的,我只是認爲MemberFactory只是一個開銷,因爲我將DTO中的各個參數傳入工廠,並手動編碼將這些參數映射到域模型。 –

+0

爲了生成身份證,我同意你的意見。但是,您應該保留MemberFactory用於封裝。無論構造成員的代碼有多複雜,它都允許您將成員構造函數設置爲私有的,以便必須使用MemberFactory來獲取成員的實例。這允許您在允許創建成員實例之前驗證輸入並可能運行其他檢查(即密碼規則)。 –

+0

有效點,在這個特定的工廠中,我傳遞了12個參數,而且沒有可以設置默認值的參數,所以我考慮將其傳遞給DTO的接口,但我不認爲這是允許的? –

1

簡短回答:是的,您可以將DTO對象傳遞給工廠。

長答案: 域對象工廠負責創建完全初始化的域對象。但是,只要您可以提供所需的信息,您就可以自由選擇任何形式的輸入。在這種情況下,在工廠中使用任何方法都是個人選擇的問題: - public Member CreateMember(string userName,string password,...){...} - public Member CreateMember(MemberDTO memberDTO){ ...}

從分層的角度來看,域對象工廠是域模型的一部分。因此它可能被領域模型層和應用層中的不同對象所使用和依賴。根據您所描述的,MemberService似乎屬於您的應用程序(服務)層。如果您將創建邏輯移入其中,您可能會發現困難的依賴關係問題。例如,如果預定域對象在確認時創建了一個成員域對象,則最終可能會使預訂對象依賴於MemberService,這不是一個好主意。這裏舉例說明:http://thinkinginobjects.com/2012/09/05/abstract-factory-in-domain-modelling

如果我們退後一步,從某種角度來看待問題,我想問一下MemberDTO和MemberService究竟爲您的應用程序提供了什麼服務。 DTO對象是否用作一組數據字段的包裝? MemberService是否將創建調用委託給工廠和存儲庫?如果答案是肯定的,那麼如果我們一起擺脫MemberDTO和MemberService並且讓演示文稿直接調用MemberFactory和MemberRepository,那麼代碼會簡單得多。然而,我在這裏做了很多假設,如果您的表示層和服務層在物理上是分開的,您可能會發現DTO和服務仍然可取。

+0

感謝您的回覆@ nwang0。該項目是一個企業級應用程序,所以我想保留MemberService(以及其他聚合的服務)。你提到'Public Member CreateMember(MemberDTO memberDTO){...}'但是因爲DTO是在應用層中定義的,所以在域層中引用它是不對的? (因爲這是工廠的地方) –