首页  >  文章  >  后端开发  >  创建专注的领域应用程序。 Symfony 方法(第 1 部分)

创建专注的领域应用程序。 Symfony 方法(第 1 部分)

Patricia Arquette
Patricia Arquette原创
2024-11-03 10:52:29662浏览

介绍

这是我决定创建的系列文章的第一篇,旨在解释我如何组织我的 symfony 应用程序以及如何尝试编写尽可能面向领域的代码。
下面,您可以找到我将在所有系列部分中使用的流程图。在每篇文章中,我将重点关注该图的具体部分,并尝试分析所涉及的流程并检测哪些部分属于我们的领域以及如何使用外部层与其他部分解耦。

Creating focused domain applications. A Symfony approach (Part 1)

分解数据处理

在第一部分中,我们将重点关注数据提取和验证过程。对于数据提取过程,我们假设请求数据的格式为 JSON。

提取数据

基于我们知道请求数据位于 JSON 请求负载中的事实,提取​​请求负载(在本例中,提取意味着从 JSON 负载中获取数组)就像使用 php json_decode 函数一样简单。

class ApiController extends AbstractController
{
    #[Route('/api/entity', name: 'api_v1_post_entity', methods: ['POST'])]
    public function saveAction(Request $request,): JsonResponse
    {
        $requestData = json_decode($request->getContent(), true);
        // validate data
    }
}

验证数据

为了验证数据,我们需要三个元素:

  • 用于定义验证规则。
  • 用于将请求的数据转换为有效的对象。
  • 用于根据这些规则验证提取的数据。

定义验证规则

对于第一个,我们将创建一个 DTO(数据传输对象)来表示传入的数据,我们将使用 Symfony 验证约束属性来指定如何验证此数据。

readonly class UserInputDTO {
    public function __construct(
        #[NotBlank(message: 'Email cannot be empty')]
        #[Email(message: 'Email must be a valid email')]
        public string $email,
        #[NotBlank(message: 'First name cannot be empty')]
        public string $firstname,
        #[NotBlank(message: 'Last name cannot be empty')]
        public string $lastname,
        #[NotBlank(message: 'Date of birth name cannot be empty')]
        #[Date(message: 'Date of birth must be a valid date')]
        public string $dob
    ){}
}

如您所见,我们在最近创建的 DTO 中定义了输入数据验证规则。这些规则如下:

  • 电子邮件:不能为空且必须是有效的电子邮件
  • 名字:不能为空
  • 姓氏: 不能为空
  • dob(出生日期):不能为空且必须是有效日期。

将请求数据非规范化到 DTO 中

对于第二个,我们将使用 Symfony 标准化器组件,通过它我们能够将请求传入数据映射到我们的 DTO 中。

class ApiController extends AbstractController
{
    #[Route('/api/entity', name: 'api_v1_post_entity', methods: ['POST'])]
    public function saveAction(Request $request, SerializerInterface $serializer): JsonResponse
    {
        $requestData = json_decode($request->getContent(), true);
        $userInputDTO = $serializer->denormalize($requestData, UserInputDTO::class);
    }
}

如上所示,denormalize 方法完成这些工作,并且只需一行代码,我们就可以用传入的数据填充 DTO。

验证 DTO

最后,为了验证数据,我们将依赖 Symfony 验证器服务,该服务将接收我们最近非规范化的 DTO 实例(它将携带传入数据),并根据 DTO 规则验证数据。

class ApiController extends AbstractController
{
    #[Route('/api/entity', name: 'api_v1_post_entity', methods: ['POST'])]
    public function saveAction(Request $request,): JsonResponse
    {
        $requestData = json_decode($request->getContent(), true);
        // validate data
    }
}

识别域

到目前为止,我们已经将提取验证数据的过程分为四个部分:

  • 从 JSON 请求负载中提取数组。
  • 创建一个 DTO 来表示传入数据及其验证规则。
  • 将数组非规范化为包含验证规则的 DTO。
  • 验证 DTO。

现在的问题是:这些部分中哪些属于我们的领域?
为了回答这个问题,我们来分析一下涉及的流程:

1.- 提取数据:这部分仅使用“json_decode”函数将传入的数据从 json 转换为数组。它不添加业务逻辑,因此这不属于该域。

2.- DTO:DTO 包含与输入数据关联的属性以及如何验证它们。这意味着 DTO 包含业务规则(验证规则),因此它属于域。

3.- 反规范化数据:这部分只是使用基础设施服务(框架组件)将数据反规范化为对象。这不包含业务规则,因此这不属于我们的域。

4.- 验证数据:与反规范化数据过程相同,验证数据过程也使用基础设施服务(框架组件)来验证传入数据。这不包含业务规则,因为它们是在 DTO 中定义的,因此它也不会是我们域的一部分。

分析完最后一点后,我们可以得出结论,只有 DTO 才是我们域的一部分。那么,我们如何处理其余的代码呢?

创建应用程序服务

就我个人而言,我喜欢将这种过程(提取、反规范化和验证数据)包含到应用程序层或服务层中。为什么?我们来介绍一下应用层。

应用层

简而言之,应用层负责编排和协调,将业务逻辑留给领域层。此外,它充当领域层和外部层(例如表示层(UI)和基础设施层)之间的中介。
从上面的定义开始,我们可以将 ExtractingDenormalizingValidating 流程包含到应用程序层的服务中,因为:

  • 它协调传入的数据处理。
  • 它使用基础设施服务(PHP JSON 函数、Symfony 规范化组件和 Symfony 验证组件)来处理传入数据。
  • 它通过将 DTO 传递到验证基础设施服务来应用域规则。

完美,我们将创建一个应用程序服务来管理此流程。我们要怎么做呢?我们如何管理责任?

分析责任

单一职责原则(SRP)规定每个类应该只负责应用程序行为的一部分。如果一个类有多重职责,那么理解、维护和修改就会变得更加困难。

这对我们有何影响?我们来分析一下。
到目前为止,我们知道我们的应用程序服务必须提取、反规范化并验证传入的数据。知道了这一点,就很容易提取出以下职责:

  • 提取数据
  • 反规范化数据
  • 验证数据

我们应该将这些职责分成 3 个不同的服务吗?我不这么认为。让我解释一下。

正如我们所见,每个职责都由基础设施功能或组件管理:

  • 提取责任:PHP json_decode 函数
  • 反规范化数据责任:Symfony 规范化组件
  • 验证数据责任:Symfony 验证组件

由于应用程序服务可以将这些职责委托给基础设施服务,因此我们可以创建更抽象的职责(处理数据)并将其分配给应用程序服务。

class ApiController extends AbstractController
{
    #[Route('/api/entity', name: 'api_v1_post_entity', methods: ['POST'])]
    public function saveAction(Request $request,): JsonResponse
    {
        $requestData = json_decode($request->getContent(), true);
        // validate data
    }
}

如上所示,DataProcessor 应用程序服务使用 json_decode 函数以及 Symfony 规范器和验证器服务来处理输入请求并返回新的且经过验证的 DTO。所以我们可以说 DataProcessor 服务:

  • 协调与输入数据处理相关的所有任务。
  • 通过域业务规则 (DTO) 与外界(基础设施服务)进行通信。

您可能已经注意到,当验证过程发现错误时,DataProcessor 服务会抛出 Symfony ValidationException 。在本系列的下一篇文章中,我们将学习如何应用业务规则来构建错误并最终将其呈现给客户。

我知道我们可以删除 DataProcessor 服务并使用 MapRequestPayload 作为服务应用程序层来提取、反规范化和验证数据,但是,考虑到本文的上下文,我认为这样更方便这样写。

结论

在第一篇文章中,我们重点关注从流程图中提取和验证数据过程。我们列出了此过程中涉及的任务,并且了解了如何检测哪些部分属于该域。
知道哪些部分属于域,我们编写了一个应用程序层服务,连接域规则的基础设施服务并协调提取和验证数据过程。
在下一篇文章中,我们将探讨如何定义业务规则来管理异常,我们还将创建一个域服务,负责将输入 DTO 转换为持久实体。

以上是创建专注的领域应用程序。 Symfony 方法(第 1 部分)的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn