注入IDbContext
类似的东西会破坏域模型的主要原则,它应该只负责业务逻辑,同时检索和存储你的域实体是基础设施层的责任。是的,你通过接口注入它,隐藏了实际的实现,但它让你的域模型知道一些存储。
此外,删除 a 所需的上述步骤Document
也不完全属于 Document 对象。让我们考虑用户权限的第一步和以下情况:
- 应允许具有管理员角色的用户删除任何文档
- 应该允许文档所有者删除文档
对于第一种情况,用户和要删除的文档之间可能没有连接。管理员用户只被允许做任何事情。这就像一个经典的例子,有两个银行账户和一个转账操作,涉及两个账户,但不是他们的责任。这是域服务到位的时候。请不要将它们与服务层服务混淆。领域服务是领域模型的一部分,只负责业务逻辑。
所以如果我是你,我会用DeleteDocument
方法创建一个新的域服务。这应该执行上述接受User
和Document
作为参数的前三个步骤。第四步应该由您的存储库完成。不知道你说的是什么意思
我没有看到添加存储库有太多价值
但从领域模型的角度来看,你已经有了一个,它是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
, ArrivalCity
。Flight
实体还应参考席位列表。Seat
也可以是一个单独的实体,具有Class
(商业、经济等)、Type
(小岛、行、中间)等属性。因此预订座位需要与不同的实体进行交互,例如Flight
,Seat
但我们不需要域服务在这里。与自然一样Seat
如果不将属性视为 a 的子对象,则该属性毫无意义Flight
。您甚至不太可能有Seat
从Flight
上下文之外查询实体的案例。所以保留 aSeat
是Flight
这里实体的责任,可以将保留逻辑放置到Flight
类中。请注意,这只是一个示例,用于尝试解释当我们需要创建域服务时,可以完全以另一种方式对真实系统进行建模。因此,只需尝试按照以下三个基本步骤来决定您是否需要域服务:
- 服务执行的操作是指一个自然不属于实体或值对象的领域概念。
- 执行的操作是指域中的其他对象。
- 操作是无状态的。
我正在从控制器访问 dbcontext,它是应用程序/服务层而不是域/业务层。域模型只处理业务逻辑,它不应该知道任何持久性逻辑,从上面的示例中您可以看到 DocumentService 没有对 dbcontext 的引用。