2015-11-08 60 views
5

我目前正在重構ASP.NET MVC項目以使用onion arcitecture,因爲它似乎適合未來開發的需要。在洋蔥架構中放置視圖模型/ DTO

我已成立了,我想我需要使用層和我的解決方案,現在看起來是這樣的:

enter image description here

所以,基本上我已經瞭解,ClientName.Core項目應該不會有什麼參考其他項目。 ClientName.Infrastructure應該有對ClientName.Core的引用。 ClientName.Core上的Interfaces文件夾定義了ClientName.Infrastructure項目中的服務,並且我的DbContext和域實體是分開的,因此只有實體位於核心項目中。

我把頭靠在牆上的地方是,ClientName.Infrastructure項目不應該將域實體返回到調用它的任何客戶端。這將在覈心項目和任何「違反」洋蔥原則的用戶界面之間創建一個參考。正如我所讀到的,解決這個問題的方法是讓基礎架構服務返回DTO。但是,如果我要返回的類別是PersonService類中的PersonDto,則需要ClientName.Core項目知道PersonDto對象,因爲這是該接口所在的位置。

所以問題是:我究竟在哪裏放置用於UI /客戶端的DTO/ViewModel /其他模型?我是否創建了一個單獨的類庫來保存這些模型,並讓UI,基礎結構和核心項目都引用它?

任何幫助/提示,​​我有點困惑提前這個;-)

由於是極大的讚賞。

編輯

基於Euphorics答案,我寫了示例代碼這裏只是爲了檢查,如果我有一些跟進的問題得到它的權利和可能。

所以基本上,我ClientName.Core層,我有我的實體包含業務邏輯,即PersonFirm實體看起來是這樣的:

(Exists in ClientName.Core/Entities) 
public class Person 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public Firm Firm { get; set; } 
} 

(Exists in ClientName.Core/Entities) 
Public class Firm 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public IEnumerable<Person> Employees { get; set; } 

    public IEnumerable<Person> GetEmployeesByName(string name) 
    { 
     return Employees.Where(x => x.Name.Equals(name)); 
    } 

    public void AddEmployee(Person employee) 
    { 
     Employees.Add(employee); 
    } 

    public void CreateFirm(Firm firm) 
    { 
     // what should happen here? Using entity framework, I have no reference to the DbContext here... 
    } 
} 

(Exists in ClientName.Infrastructure/Services) 
public class FirmService : IFirmService 
{ 
    private readonly IDbContext _context; 

    public FirmService(IDbContext context) 
    { 
     _context = context; 
    } 

    public Firm GetById(int firmId) 
    { 
     return _context.Firms.Find(firmId); 
    } 

    // So basically, this should not call any domain business logic? 
    public void CreateFirm(CreateFirmFormViewModel formViewModel) 
    { 
     Firm firm = new Firm() 
     { 
      Name = formViewModel.Name; 
     } 

     _context.Firms.Add(firm); 
     _context.SaveChanges(); 
    } 

    public IEnumerable<Person> GetEmployeesByName(int firmId, string name) 
    { 
     Firm firm = _context.Firms.Find(firmId); 
     return firm.GetEmployeesByName(name); 
    } 
} 

我是正確,每一個讀查詢應該是直接在實體上定義(可能作爲實體擴展,因爲我使用實體框架),並且任何創建/更新/刪除只會發生在基礎設施層的服務中?

讀取(即IEnumerable<Person> GetEmployeesByName(int firmId, string name)方法)方法是否也在FirmService接口/類上?

再次感謝:-)

回答

2

首先,你是不是從PersonService返回Person,但是從IPersonService。這是巨大的概念差異。 PersonService僅實現IPersonService定義的內容。它並不關心它使用的接口或對象。

簡而言之,Core項目應該包含業務實體中的所有業務邏輯。如果你沒有任何實際的邏輯,那只是簡單的DTO。然後,Infrastructure將負責從數據庫中保存/加載這些實體。如果它創建了它自己的模型,或者它重用了由核心定義的模型,那麼它就是Infrastructure的實現細節。同時,UI(或Web)項目將與Core項目中的實體一起工作,但它不會在乎持續性如何發生。它只是想「做生意」。

這裏的關鍵是核心(或業務邏輯)是自動測試而不涉及UI或數據庫代碼。只要你能夠實現這一點,那麼一些DTO在哪裏並不重要。

編輯:

這開始變得艱難。您遇到了相互衝突的設計要求。一方面,您擁有清晰的域名設計。另一方面,你希望你的模型可以使用EF持久化。有一點很清楚:你要妥協和扭曲你的領域模型,以便它可以在EF中使用。

現在爲具體問題。第一個是公司的創建。在這裏,你有領域所理解的「新公司」之間的衝突。例如。確保新公司具有有效的名稱。另一方面,您想創建實例並使EF知道它。我會這樣做的方式是從Firm類中刪除CreateFirm,並在IFirmService中更改CreateFirm,以便它僅接受創建新Firm(例如,只是名稱)所需的參數並返回新創建的公司。此外,存儲庫不應該調用域的想法是錯誤的。例如,存儲庫可能會調用域來驗證它創建的公司是否有效,如果不是,則不要將其添加到EF中。

我看到的第二個問題是保持Employees集合在公司中。雖然這對於域很好,但對持久性來說卻是災難性的(除非你認爲在這種情況下延遲加載沒問題)。再次。如何滿足「公司有員工集合」的領域要求和將實體堅持到數據庫中的能力沒有簡單的方法。第一種選擇是不要將這個集合並移動到IFirmService,在那裏它可以正確實施。第三種選擇是最極端的。這就是爲了使Firm成爲抽象類,同時使所有「get」成爲抽象方法,在基礎結構項目中創建它的實現並使用具體實例所具有的EF上下文實現get方法。此外,只有在基礎結構中創建了Firm的具體實例時纔可能實現,這正是我上面提到的。

我個人喜歡第三個選項,因爲它保持了方法的美觀和內聚。但一些英超純粹主義者可能會不同意我的看法。

+0

嗨@Euphoric,謝謝你的回答。我剛剛用一些簡單的代碼和一些後續問題更新了我的問題,只是爲了確保我理解正確。 –