在3层体系结构中使用实体框架实现有界上下文

Kam*_*ran 2 domain-driven-design entity-framework n-tier-architecture bounded-contexts

我观看了Julie Lerman关于在企业应用程序中使用EF的视频.现在我正在开发一个使用"Bounded Contexts"的网站以及她在该系列中教过的其他内容.

问题是我不知道如何在我的"业务层"中使用有界上下文(BC).为了更清楚:BL应该如何知道它应该使用哪个特定的BC.

假设UI从业务层请求产品列表.在BL我有一个返回产品列表的方法:GetAll().此方法不知道UI的哪个部分(站点管理员,主持人或公共用户)已请求产品列表.由于每个用户/场景都有自己的有界上下文,因此需要使用该相关上下文来提取列表.BL如何选择合适的BC?

此外,我不希望UI层与数据层交互.

如何才能做到这一点?

g18*_*18c 6

如果业务层是指一个定义了所有业务规则的地方,那么这就是一个有界的上下文.

有界上下文从某个角度查看您的系统,以便可以以分区方式实现业务规则(目标是通过拆分到更小的块更容易处理整个问题).

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

福勒 - 有限的背景

前端

因此,假设您有一个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");
    }
}
Run Code Online (Sandbox Code Playgroud)

域名申请(用例)

接下来,您将拥有一个实现用例的域应用程序入口点(这将是一个命令或查询处理程序).

您可以直接调用此代码并使代码在前端应用程序中运行,或者您可以在其前面提供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);
    }
}
Run Code Online (Sandbox Code Playgroud)

领域模型

他们自己的实体在域模型中实现自己的业务逻辑.您可能还拥有逻辑域服务,这些服务自然不适合单个实体.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

基础设施

您将拥有实现代码的基础结构,以从后备存储中获取和检索实体.对你来说这是实体框架和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

我提到上面的内容,因为我开始使用EF开始我的DDD之旅以及你遇到的完全相同的问题.