2

我正在使用 ASP.NET MVC 开发一个基于 Web 的应用程序。我正在尝试拥有丰富的域模型而不是瘦/贫血模型。

我已经按照 Onion 架构对我的解决方案进行了建模。不同的项目如下: 在此处输入图像描述

  • {}.Domain.Core - 包含域对象和接口,例如在基础设施层实现的 IDbContext
  • {}.Database - 是数据库项目
  • {].Infrastructure - 包含日志记录、数据访问等的实现。
  • {}.Web - 视图和控制器

**** 数据访问是使用 dapper 完成的,IDbContext 是围绕 2 个简单命令、查询接口的包装器。我已将每个查询隔离为单独的类。

为了便于讨论,我只取了应用程序的一小部分。

我有一个版本化的文档库,其中包含文档以及标签、权限等其他元数据

我的文档对象的简化模型如下所示

在此处输入图像描述

我希望在域对象中定义操作,因为每个操作都涉及业务逻辑。让我把“删除”作为一个操作。需要执行的操作

  • 验证用户是否有删除权限
  • 检查是否没有会受到此删除影响的关联
  • 检查是否没有正在进行的工作流
  • 在事务中从数据库中删除实际项目

如上例所示,我需要数据库上下文来完成此操作。我目前考虑的方式是建模是否让域对象具有 IDbContext,它可以执行公开的查询。

在此处输入图像描述

在我的控制器类中,我调用域对象并执行操作。

我不确定在域对象中传递 IDbContext 是否可以?如果不是,有什么更好的建模方法?

我不相信有一个单独的服务层,因为 1) 在大多数情况下,控制器充当服务层的第一层 2) 服务层只是将相同的方法从域复制到另一个类

让我知道如何改进这个设计。

4

1 回答 1

3

注入IDbContext类似的东西会破坏域模型的主要原则,它应该只负责业务逻辑,同时检索和存储你的域实体是基础设施层的责任。是的,你通过接口注入它,隐藏了实际的实现,但它让你的域模型知道一些存储。

此外,删除 a 所需的上述步骤Document也不完全属于 Document 对象。让我们考虑用户权限的第一步和以下情况:

  • 应允许具有管理员角色的用户删除任何文档
  • 应该允许文档所有者删除文档

对于第一种情况,用户和要删除的文档之间可能没有连接。管理员用户只被允许做任何事情。这就像一个经典的例子,有两个银行账户和一个转账操作,涉及两个账户,但不是他们的责任。这是域服务到位的时候。请不要将它们与服务层服务混淆。领域服务是领域模型的一部分,只负责业务逻辑。

所以如果我是你,我会用DeleteDocument方法创建一个新的域服务。这应该执行上述接受UserDocument作为参数的前三个步骤。第四步应该由您的存储库完成。不知道你说的是什么意思

我没有看到添加存储库有太多价值

但从领域模型的角度来看,你已经有了一个,它是IDbContext. 我假设您的意思是为每个实体实现存储库或单独的存储库的某种模式。从长远来看,控制器中的伪代码应如下所示:

var user = bdContext<User>.SelectById(userId);
var document = bdContext<Document>.SelectById(docId);
var docService = new DocumentService();
docService.DeleteDocument(document, user);  //  Throw exception here if deletion is not allowed
bdContext<Document>.Delete(document);

如果您希望在应用程序的许多地方都需要此逻辑,则可以将其包装在服务层服务中。

如果您想了解有关领域建模的更多信息,我建议您阅读 Eric Evans关于 DDD 的书。这将详细讨论实体、值对象、域服务等的含义。

对评论的回答: 并非如此,域服务是域的一部分,因此实现和接口也是域的一部分。两个或多个对象必须相互交互的事实不足以创建域服务。让我们以航班预订系统为例。您有一个Flight具有不同属性的实体,例如DepartureCity, ArrivalCityFlight实体还应参考席位列表。Seat也可以是一个单独的实体,具有Class(商业、经济等)、Type(小岛、行、中间)等属性。因此预订座位需要与不同的实体进行交互,例如FlightSeat但我们不需要域服务在这里。与自然一样Seat如果不将属性视为 a 的子对象,则该属性毫无意义Flight。您甚至不太可能有SeatFlight上下文之外查询实体的案例。所以保留 aSeatFlight这里实体的责任,可以将保留逻辑放置到Flight类中。请注意,这只是一个示例,用于尝试解释当我们需要创建域服务时,可以完全以另一种方式对真实系统进行建模。因此,只需尝试按照以下三个基本步骤来决定您是否需要域服务:

  1. 服务执行的操作是指一个自然不属于实体或值对象的领域概念。
  2. 执行的操作是指域中的其他对象。
  3. 操作是无状态的。

我正在从控制器访问 dbcontext,它是应用程序/服务层而不是域/业务层。域模型只处理业务逻辑,它不应该知道任何持久性逻辑,从上面的示例中您可以看到 DocumentService 没有对 dbcontext 的引用。

于 2014-10-18T23:26:30.680 回答