2

我看过 Julie Lerman 关于在企业应用程序中使用 EF 的视频。现在我正在开发一个使用“有界上下文”和她在该系列中教授的其他内容的网站。

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

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

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

如何才能做到这一点?

4

1 回答 1

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");
    }
}

领域应用程序(用例)

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

您可以直接调用它并让代码在前端应用程序的进程中运行,或者您可以在它前面有一个 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

我在使用 EF 开始我的 DDD 之旅时提到了上述内容,以及您遇到的完全相同的问题。

于 2015-02-19T00:31:34.263 回答