带有EF核心的DDD(DTO,业务等)

时间:2019-07-09 08:34:55

标签: asp.net entity-framework rest design-patterns domain-driven-design

我正在尝试进入设计模式和REST API,对于一个项目,我将Entity Framework Core与:

  • 存储库模式
  • 工作单位

对我来说确实有些模糊:

  • 数据验证,应该在哪里完成?
  • 活动?应该在哪里触发?示例:注册用户后,我想向他们发送电子邮件。
  • 业务层应该采用DTO还是Domain模型?
  • 从DTO到Domain模型的转换应该去哪里?
  • 应该向控制器添加什么?

我知道这些事情也取决于开发人员,但是实现此目的的最佳/“原始”方法是什么。

2 个答案:

答案 0 :(得分:1)

数据验证:

所有验证都应作为域层的一部分执行,通常应作为Entity类中工厂方法的一部分执行。如果更适合您,则可以使用单独的工厂方法或类。要记住的关键是:实体/聚合对象应该始终有效。它们不能处于无效状态,需要您使用is_valid方法检查其有效性。

示例工作流程如下:

  • 应用程序服务初始化存储库
  • 应用程序服务调用工厂方法来组装实体
  • Factory方法运行验证并确保传递的数据有效
  • 工厂方法返回构造的有效对象
  • Application Service将对象传递到要持久存储的存储库

事件

在执行业务逻辑之后,应立即将事件作为域层的一部分触发。

为防止事件过早地分派(例如,在持久存储数据之前),理想情况下,您应该在业务逻辑中引发事件,但仅在成功的持久性事务之后才将其作为工作单元的一部分进行分派。

DTO处理:

业务层应接受DTO并返回Domain模型。在将DTO转换为域模型的过程中进行验证。 DTO(甚至纯关键字参数)是工厂方法的正确输入。

如果要构造的对象是新对象,则应在Factory方法中进行从DTO到Domain模型的转换。如果您要处理更新方案,则存储库将获取持久对象,使用实体的构造函数将其重构为域实体,并在显式update方法中应用DTO更改,该方法还将运行验证。 / p>

控制者职责:

控制器应负责:

  • 解构请求对象
  • 组装参数(甚至与会话相关的项目,例如当前登录的用户)
  • 初始化DTO
  • 调用适当的应用程序服务来执行

这些概念适用于任何DDD设计,包括EF Core。

答案 1 :(得分:1)

我需要提前道歉,因为我可能不会给您足够的食谱,但这是我对您的问题的看法。

您提到了设计模式,但是您也用domain-driven design标签标记了您的问题,所以我假设的意思是在项目中使用DDD战术模式。我想强调一点,DDD既不是一组设计模式,也不是设计模式或体系结构样式,而是设计系统时考虑到业务的方式。

在DDD中,最有用的模式之一是“聚合”模式。聚合是一组形成一致性边界的实体,假设该实体集合的所有业务规则都聚集在一个地方,则该实体可以容纳足够的信息以自行做出任何业务决策。

从事物的应用程序角度来看,我们经常使用应用程序服务来检索聚合状态,在聚合根上调用一个或多个方法,并将新状态持久化。重要的是要意识到所有这些都是一次完成,一笔交易,因此工作单元模式在这里间接应用。我之所以间接写 ,仅仅是因为聚合本身的事务边界,而应用程序执行 的一个命令就是工作单元。聚合持久性可以使用存储库模式来完成,这就是我们所说的实施细节

现在,我将尝试解构您的具体观点。

  

数据验证,应该在哪里进行?

我倾向于将 validation 看作是确保您的应用程序从外界收到的命令在某种程度上是有效的。这涉及控制必需的命令字段不为空并且包含正确的值类型,就像您不能为数字字段发送字母,而电子邮件字段确实包含看起来像有效的电子邮件一样。您可以实施多层验证,使最明显的验证层更接近客户端,以便您的用户获得快速反馈。这些 easy 检查可以在UI方面进行,也可以在边缘进行检查(在您的情况下为REST API)。

当命令传递给应用程序服务时,它需要确保它获得正确的域实体(聚合)状态,因此至少您要尝试从数据库中获取该实体,以检查该实体是否存在。您还可以使用值对象(例如Email或Address),并从作为命令属性传递的原始类型构造它们。值对象是将某些业务规则放入其中的理想场所,并且通过尝试构建它们可以自然进行验证。

最后一个防御层是您的域模型。通过前面的所有步骤,您可以非常确定自己已经具有有效的值对象和有效的域实体。聚合保护其不变量,并且根据定义,不可能将聚合置于无效状态。我通常不称其为 validation

  

活动?应该在哪里触发?示例:注册用户后,我想向他们发送电子邮件。

如果您正在谈论域事件,并且不使用事件源,则必须确保将域事件在同一事务中发布到您的交付介质中。当您的应用程序发布域事件但无法持久保存修改后的实体状态时,应避免这种可能性,反之亦然。当发生此类故障时,您的整个系统将处于不一致状态。

  

业务层应该采用DTO还是Domain模型?

我还没有完全解决问题,但是您的业务层您的域模型。领域模型通常由实体,价值对象和领域服务组成。您致电域模型,要求它做某事,此阶段不涉及DTO。

  

从DTO到Domain模型的转换应该去哪里?

在这种情况下,我完全避免使用DTO术语,因为您的实体状态可以被视为DTO以及您的API模型和事件。如果您谈论的是API合同,那么这是一个命令,并且不会“转换”,您的API会从API中获取命令并调用您的域模型。

  

控制器应该放入什么?

API控制器只是您应用程序的边缘。该API负责传输(HTTP或其他),序列化,身份验证和一些授权问题以及异常处理。其主要目的是确保API请求看起来合法,并将其传递给应用程序服务。

如果您正在寻找一个使用WebAPI,EF并实现一些战术DDD模式的示例应用程序,则可能需要在Packt repo https://github.com/PacktPublishing/Hands-On-Domain-Driven-Design-with-.NET-Core/tree/master/Chapter09/ef-core

上查看工作示例。