antonyan / ddd-mappers-project
DDD 项目骨架
Requires
- php: >=7.1.0
- antonyan/ddd-mappers-infrastructure: 2.*
- zircote/swagger-php: ^3.0
Requires (Dev)
- codeception/codeception: 2.*
- ddd-project/generator: 1.*
- phpunit/phpunit: 6.*
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的服务中访问。