1

我觀看了Julie Lerman關於在企業應用程序中使用EF的視頻。現在我正在使用「有限上下文」和她在該系列中教過的其他材料開發一個網站。在3層體系結構中使用實體框架實現有界上下文

問題是我不知道如何在我的「業務層」中使用有界上下文(BC)。爲了更清楚:BL應該怎麼知道它應該使用哪個特定的BC。

假設UI請求來自業務層的產品列表。在BL中,我有一種方法返回產品列表:GetAll()。此方法不知道UI(站點管理員,主持人或公共用戶)的哪個部分請求了產品列表。由於每個用戶/場景都有其自己的有界上下文,因此需要使用該相關上下文來拉取列表。 BL應該如何選擇合適的BC?

此外,我不希望UI層與數據層進行交互。

這怎麼辦?

+1

限界上下文也(也是最重要的)反映在域層,而不是僅僅「已使用」,所以給定的域對象本質上知道哪個BC很參見http://martinfowler.com/bliki/BoundedContext.html – guillaume31 2015-02-18 08:20:24

+0

如果域對象是各種BC的成員,該怎麼辦? – Kamran 2015-02-18 14:27:21

+0

這不應該發生。它應該在自己的BC中,或者在兩個BC中分成兩個相同名稱的類,然後可以在兩個BC之間來回映射。這是不列顛哥倫比亞省的全部重點 - 消除語義上相撞的東西。 – guillaume31 2015-02-18 15:01:30

回答

5

如果按業務層指您定義所有業務規則的地方,那麼這是一個有界的上下文。

一個有界的上下文從某個角度看待你的系統,這樣業務規則就可以以分區的方式實現(目標是通過分割成更小的塊來處理整個問題)。

http://martinfowler.com/bliki/BoundedContext.html

fowler - bounded contexts

前端

所以假設你有一個ASP MVC前端,這個控制器是會打電話給你的使用情況/用戶故事所呈現的東西來自要通過標準已知接口調用的域。

public class UserController : Controller 
{ 
    ICommandHandler<ChangeNameCommand> handler; 

    public UserController(ICommandHandler<ChangeNameCommand> handler) 
    { 
     this.handler = handler; 
    } 

    public ActionResult ChangeUserName(string id, string name) 
    { 
     try 
     { 
      var command = new ChangeNameCommand(id,name); 
      var data = handler.handle(command); 
     } 
     catch(Exception e) 
     { 
      // add error logging and display info 
      ViewBag.Error = e.Message; 
     } 

     // everything went OK, let the user know 
     return View("Index"); 
    } 
} 

領域中的應用(用例)

接下來,你就必須實現用例的域應用程序入口點(這將是一個命令或查詢處理)。

您可以直接調用此並在過程中運行代碼與你的前端應用,或者你可能在它呈現域應用服務前有一個或的WebAPI WCF服務。這並不重要,系統不信任取決於系統要求(從基礎架構的角度來看,通常更簡單,如果不需要,也不要分發)。

然後,域應用程序層編排用戶故事 - 它會重新創建存儲庫,獲取實體,對它們執行操作,然後回寫到存儲庫。這裏的代碼不應該很複雜或者包含邏輯。

public class NewUserHandler : ICommandHandler<ChangeNameCommand> 
{ 
    private readonly IRepository repository; 

    public NewUserHandler(IRepository repository) 
    { 
     this.repository = repository; 
    } 

    public void Handle(ChangeUserName command) 
    { 
     var userId = new UserId(command.UserId); 
     var user = this.repository.GetById<User>(userId); 
     user.ChangeName(command.NewName); 
     this.repository.Save(newUser); 
    } 
} 

域模型

的實體他們的自我實現域模型自己的業務邏輯。您也可能擁有邏輯領域的服務,而這種邏輯並不能很好地適應單個實體。

public class User 
{ 
    protected string Name; 
    protected DateTime NameLastChangedOn; 

    public ChangeName(string newName) 
    { 
     // not the best of business rules, just an example... 
     if((DateTime.UtcNow - NameLastChangedOn).Days < 30) 
     { 
      throw new DomainException("Cannot change name more than once every 30 days"); 
     } 

     this.Name = newName; 
     this.NameLastChangedOn = DateTime.UtcNow; 
    } 
} 

基礎設施

您將有代表執行代碼來獲取和檢索您的備份存儲實體的基礎設施。對你來說,這是實體框架和DbContext(我上面的示例代碼不使用EF,但可以替代)。

回答你的問題 - 哪些界上下文應該前端應用程序調用?

不讓複雜或長的答案,但我包括上述代碼來設置背景,並希望更容易理解,因爲我認爲你使用的術語有點混淆。

隨着你開始執行多個命令和查詢處理程序上面的代碼,這限界上下文被稱爲從前端應用程序依賴於用戶希望執行哪些特定的用戶故事。

用戶素材通常會聚集到不同的有界上下文中,因此您只需選擇命令或查詢來實現所需功能的有界上下文 - 不必擔心使其更復雜。

讓你試圖解決規定的映射,不要害怕,這個映射會洞察到你正在尋找解決改善問題可能改變的問題。

旁註

作爲一個側面說明,以提及的事情,我發現有用的(我開始了我與EF DDD之旅)......與實體框架有一些經常需要ORM的概念,如定義的映射關係,實體之間的導航屬性以及級聯刪除和更新會發生什麼。對我而言,這開始影響我設計實體的方式,而不是決定如何設計實體的問題。您可能會發現這個有趣的:http://mehdi.me/ambient-dbcontext-in-ef6/

您可能也想看看http://geteventstore.com和事件採購其帶走ORM映射的任何麻煩(但附帶額外的複雜性,並獲得可接受的性能所需要的解決方法)。什麼是最好的使用取決於情況,但它總是很好的知道所有的選擇。我也使用SimpleInjector連接我的類並注入到MVC控制器(作爲預編譯的命令或查詢處理程序),更多信息在這裏:https://cuttingedge.it/blogs/steven/pivot/entry.php?id=91

使用IoC容器是個人喜好,而不是一成不變的。

這本書也是真棒:https://vaughnvernon.co/?page_id=168

我提到了上面,我開始我的旅程DDD與EF和你有相同的問題。