antonyan/ddd-mappers-project

DDD 项目骨架

v2.1 2018-12-18 15:35 UTC

This package is auto-updated.

Last update: 2024-09-27 04:07:19 UTC


README

该项目旨在帮助开发者使用 PHP 编程语言实现 DDD 方法。其中包含大量的理论知识和一些示例。

DDD-mappers-project 包含了您创建短期高质量(高可维护性)的商业产品所需的所有内容。

概述

我们的团队不想强调技术细节,因此我们尽可能使用知名解决方案,例如在可能的情况下使用 Symfony 组件。

此外,我们还创建了许多不同且有用的功能,这些功能可以显著加速开发。

快速入门

您只需要运行命令 composer create-project antonyan/ddd-mappers-project nameOfYourProject 设置数据库 查看数据库交互 -> 连接

请求

为了验证请求数据,我们创建了可以在表示服务(控制器)中指定的注解。

示例

/** 
     * @Validation(name="phone", type="string", required=true, maxLength=50)
     * @Validation(name="email", type="string", required=true, maxLength=100)
     * @Validation(name="firstName", type="string", required=true, maxLength=100)
     * @Validation(name="lastName", type="string", required=true, maxLength=100)
     * @Validation(name="status", type="string", required=true, maxLength=30)
     * @Validation(name="roles", type="array", required=true)
     *
     * @param Request $request
     * @return CreateEntityJsonResponse
     */
    public function create(Request $request): CreateEntityJsonResponse
    {
        return new CreateEntityJsonResponse($this->getUserService()->create($request->request->all())->toArray());
    }

在这种情况下,我们的验证器检查所有指定的字段,并从请求中删除任何额外的字段。

可用的验证类型

'array', 'bool', 'float', 'double', 'int', 'integer', 'numeric', 'string'

路由

请求配置可以通过路径 /src/app/config/restRoutes.php 查找。项目在底层使用 Symfony\Component\Routing\RouteCollection,但我们进行了一些改进,使得添加路由变得容易。

如果您需要为特定情况创建路由,可以使用 addGET、addPOST、addDELETE 或 addPUT。如果您正在创建以数据为中心的模块,可以使用 addCRUD,项目将创建所有 URL 并将其映射到表示服务(控制器)。

$routesCollectionBuilder = new RouteCollectionBuilder();

$routesCollectionBuilder->addCRUD('/restaurants', Restaurant::class);

$routesCollectionBuilder->addGET('you/url', 'YouService', 'youMethod');

return $routesCollectionBuilder->build();

上下文

该项目的主要思想是允许开发者创建由上下文和模块组成的架构。创建上下文的最简单方法是使用 vendor/bin/generate context -n ContextName 命令生成它。它将在 /srs/contexts/ 中创建,并包含配置、Services 文件夹和 Contract。

配置

文件夹包含两个文件 config.php 和 container.php。在 config.php 中,您可以指定所有可以更改但应在 Service 中可用的数据(例如令牌的存活时间、尝试次数等)。

容器用于指定依赖项(依赖注入)。在此文件中,您应注册所有模块服务。

示例: $containerBuilder->register('UserService', Contexts\User\UserModule\Services\UserService::class);

服务

在此文件夹中,您可以看到上下文服务,它协调与模块级别的所有服务的交互。它可以是一个简单的调用方法

    public function create(array $data): UserAggregate
    {
        return $this->getUserAggregateService()->create($data);
    } 

或者它可以从一个服务接收一些信息并将其发送到另一个服务。

合同

这是上下文合同,上下文之外的服务只能通过它与模块进行交互。

示例:要使用 UserService,您应在表示服务(控制器)中指定

    private function getUserService(): UserContract
    {
        return $this->container()->get('UserService');
    }

模块

如果您想快速创建模块,您应该在上下文的根目录中创建 Model /src/contexts/SomeContext

示例

<?php

namespace Contexts\Test;

use Infrastructure\Models\ArraySerializable;

class SomeModel implements ArraySerializable
{
    public const ID = 'id';
    public const PHONE = 'phone';
    public const EMAIL= 'email';

    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $phone;

    /**
     * @var string
     */
    private $email;

    /**
     * SomeModel constructor.
     * @param int $id
     * @param string $phone
     * @param string $email
     */
    public function __construct(
        int $id,
        string $phone,
        string $email
    )
    {
        $this->id = $id;
        $this->phone = $phone;
        $this->email = $email;
    }

    /**
     * @return array
     */
    public function toArray(): array
    {
        return [
            self::ID => $this->id,
            self::EMAIL => $this->email,
            self::PHONE => $this->phone,
        ];
    }
}

然后生成模块 vendor/bin/generate module -n Contexts\\Test\\SomeModel。您应该指定创建的模型的命名空间。

在上下文中将创建模块(例如 SomeModelModule),它将包含以下文件夹

SomeModelModule
|-config
|-Factories
|-Mappers
|-Models
|-Services

配置

这个文件夹几乎与在上下文的情况相同,但配置包含为模型映射的数据库

示例

'SomeModelDbTranslator' => [
        'table' => 'someModels',
        'columns' => [
			'id' => 'someModels.id',
			'phone' => 'someModels.phone',
			'email' => 'someModels.email',
        ],
        'create' => 'id',
        'update' => ['id'],
    ],

SomeModelDbTranslator是SomeModel的元数据类。

  • table - 它是从数据库中的表名。
  • columns - 类Model属性与数据库字段之间的映射。
  • create - 你应该在指定Model标识符的名称。
  • update - 你应该在指定Model更新标识符名称。

对于一般情况(如果你只需要CRUD),Module的所有数据都将自动生成,你不需要关心任何事情。

但你可以重写或创建自己的解决方案。

工厂

在这里,你可以看到创建Model的简单工厂。

映射器

在这里,你可以看到Model的映射器。仅用于类型化。

服务

它是无状态的类,用于业务逻辑实现。在简单的以数据为中心的应用中,它可能看起来像从Mapper到ContextService的代理。

主要思想

除了快速实现领域设计外,该项目还有一个严格分离依赖项的想法。每个服务都有自己的容器,如果它是模块服务,则位于同一模块的config文件夹中;如果是上下文服务,则位于上下文中。

在此基础上,为了访问基础设施类(HttpClient,MySqlClient),BaseService自动将其合并到每个服务的容器中。

我们推荐的做法是遵循以下规则
  • 表示服务只能使用上下文服务;
  • 上下文服务可以使用自己上下文中的模块服务或其它上下文服务;
  • 模块服务只能使用自己模块中的映射器、工厂和模型。

基础设施容器

src/app/config/appContainer.php中,你可以附加自己的监听器或订阅者到事件,并覆盖特定的保留服务。

自定义错误处理

要自定义由异常监听器触发的错误处理,你可以覆盖保留的application.error.handler服务。
$containerBuilder->register('application.error.handler', \App\Services\Error::class);

基础设施事件

request - 在执行请求之前抛出。常量名称为Infrastructure\Application::EVENT_BEFORE_DISPATCH_REQUEST

Symfony内核事件

KernelEvents::EXCEPTION
...

事件监听器和订阅者

要附加订阅者或监听器
$containerBuilder->register('test_subscriber', TestSubscriber::class)->addTag('kernel.event_subscriber');

$containerBuilder->register('test_subscriber', TestSubscriber::class)->addTag('kernel.event_listener',["event" => "<event_to_listen>", "method" => "<your_method>"]);

应用注册表

Infrastructure\Models\ApplicationRegistry
应用注册表是一个全局容器,作为单例和注册模式开发。可以从扩展BaseService的服务中访问。