【问题标题】:DDD with EF Core(DTO, Business, Etc)带有 EF Core 的 DDD(DTO、业务等)
【发布时间】:2019-07-09 08:34:55
【问题描述】:

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

  • 存储库模式
  • 工作单元

一些对我来说真的很模糊的事情:

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

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

【问题讨论】:

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


    【解决方案1】:

    我需要提前道歉,我可能没有给你足够的食谱,但这是我对你的问题的看法。

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

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

    当我们查看应用程序端时,我们经常使用应用程序服务来检索聚合状态,调用聚合根上的一个或多个方法并将新状态持久化回来。重要的是要意识到所有这一切都发生在一次交易中,因此工作单元模式间接地应用在这里。我之所以写间接只是因为聚合本身一个事务边界,而您的应用程序执行的一个命令工作单元。聚合持久化可以使用存储库模式来完成,这就是我们所说的实现细节

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

    数据验证,应该在哪里做?

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

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

    最后一个防御层是您的域模型。从前面的所有步骤中,您可以确定您已经拥有有效的值对象和有效的域实体可以使用。聚合保护其不变量,根据定义,不应将聚合置于无效状态。我通常不称之为验证

    事件?这应该在哪里触发?示例:当用户注册时,我想向他们发送电子邮件。

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

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

    我没有完全理解这个问题,但您的业务层您的领域模型。领域模型通常由实体、值对象和领域服务组成。您调用域模型要求它做某事,在这个阶段不涉及 DTO。

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

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

    什么应该进入控制器?

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

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

    【讨论】:

      【解决方案2】:

      数据验证:

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

      示例工作流程如下所示:

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

      活动

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

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

      DTO 处理:

      业务层应该接受 DTO 并返回域模型。验证发生在 DTO 到域模型的转换过程中。 DTO(甚至是普通关键字参数)是工厂方法的正确输入。

      如果正在构造的对象是新的,则从 DTO 到域模型的转换应该发生在工厂方法中。如果您正在处理更新场景,存储库将获取持久化对象,使用实体的构造函数将其重组为域实体,并在显式 update 方法中应用 DTO 更改,该方法也将运行验证。

      控制器责任:

      控制器应负责:

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

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

      【讨论】:

        猜你喜欢
        • 2017-01-19
        • 1970-01-01
        • 1970-01-01
        • 2020-01-21
        • 2020-11-19
        • 2019-06-21
        • 2020-11-27
        • 1970-01-01
        • 2021-12-30
        相关资源
        最近更新 更多