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和你有相同的问题。