fusonic / ddd-extensions
用于与 Doctrine ORM 一起使用的 Symfony 领域驱动设计构建块。
Requires
- php: >=8.2
- beberlei/assert: ^3.2
Requires (Dev)
- doctrine/orm: ^3.2
- friendsofphp/php-cs-fixer: ^3.64
- infection/infection: ^0.29
- nelmio/api-doc-bundle: ^v4.29
- nikic/php-parser: ^5.1
- phpstan/phpstan: ^1.12
- phpstan/phpstan-deprecation-rules: ^1.2
- phpstan/phpstan-phpunit: ^1.4
- phpstan/phpstan-strict-rules: ^1.6
- phpstan/phpstan-symfony: ^1.4
- phpunit/phpunit: ^11.3
- symfony/cache: ^6.4 || ^7.0
- symfony/messenger: ^6.4 || ^7.0
- symfony/serializer: ^6.4 || ^7.0
- tomasvotruba/type-coverage: ^0.3
Suggests
- doctrine/orm: ^3.2
- nelmio/api-doc-bundle: ^4.29
- symfony/messenger: ^^6.4 || ^7.0
- symfony/serializer: ^6.4 || ^7.0
README
关于
此库为在 PHP 中实现领域驱动设计提供了一些基类。提供了用于与 Symfony 和 Doctrine 一起使用的有用工具,但您不必使用这些工具。
安装
使用 composer 从 Packagist 安装库。
composer require fusonic/ddd-extensions
配置
services: # This service dispatches the domain events raised on aggregate roots to the given message bus. Fusonic\DDDExtensions\Doctrine\LifecycleListener\DomainEventLifecycleListener: arguments: $bus: '@Symfony\Component\Messenger\MessageBusInterface' tags: # Note: The listener has to be tagged individually for each event - { name: doctrine.event_listener, event: 'postPersist', priority: 500 } - { name: doctrine.event_listener, event: 'postUpdate', priority: 500 } - { name: doctrine.event_listener, event: 'postRemove', priority: 500 } - { name: doctrine.event_listener, event: 'postFlush', priority: 500 } # Optionally configure a ModelDescriber if you are using NelmioApiDocBundle to display EntityId objects in the # generated documentation Fusonic\DDDExtensions\ModelDescriber\EntityIdDescriber: tags: - { name: nelmio_api_doc.model_describer, priority: 1000 } # Optionally configure a normalizer to automatically serialize/deserialize AbstractIntegerId objects Fusonic\DDDExtensions\Normalizer\EntityIdNormalizer: tags: - { name: serializer.normalizer }
使用和建议
例如,请参阅 测试中的示例。
值对象
值对象必须扩展 Fusonic\DDDExtensions\Domain\Model\ValueObject
。值对象必须是不可变的。所有需要的属性必须在初始化对象时设置。值对象不能有任何设置器属性。
比较值对象时,必须实现 equals
函数。
聚合根
聚合根是边界上下文的 入口点。扩展 Fusonic\DDDExtensions\Domain\Model\AggregateRoot
的领域对象是聚合根。只有聚合根可以直接在边界上下文之外创建/修改。所有子实体都通过聚合根进行修改/创建。
领域实体
领域实体必须实现 Fusonic\DDDExtensions\Domain\Model\EntityInterface
。
您不应该直接对领域实体(不是聚合根)进行操作。所有操作都应通过聚合根进行。
getId()
返回的 "id" 可以返回任何内容,但建议为每个领域实体创建一个专门的 "id" 类。例如,一个具有 UserId
类的 User
类。扩展 EntityId
的类必须实现一个 __toString
方法和一个 getValue()
方法,该方法将返回内部值。内部值的实现取决于您。对于 Doctrine,您可以使用整数并使用 Fusonic\DDDExtensions\Domain\Model\EntityIntegerId
类作为基类,请参阅 此示例。
为了保持一致的返回类型并避免在所有地方进行空值检查,您不能返回 null。如果您使用 AbstractIntegerId
基类,默认内部值将是 0
。为了检查这一点,实现了方便的 isDefined()
方法。
public function getId(): UserId { return new UserId($this->id); }
断言
领域层不依赖于任何验证服务。验证逻辑必须在模型内部。对于领域中的断言,有一个静态辅助类,其中包含常用的断言函数。请参阅: Fusonic\DDDExtensions\Domain\Validation\Assert
。
领域异常
领域异常只能从领域内部抛出。所有领域异常都必须实现 Fusonic\DDDExtensions\Domain\Exception\DomainExcetionInterface
。
领域事件
扩展 Fusonic\DomainDrivenDoctrin\Domain\Model\AggregateRoot
的领域对象可以引发事件。在类内部,您可以调用 $this->raise(...)
并传递一个实现 Fusonic\DDDExtensions\Domain\Event\DomainEventInterface
的事件。
所有引发的事件将在 Doctrine flush
被调用时分发。 Fusonic\DDDExtensions\Doctrine\LifecycleListener\DomainEventLifecycleListener
处理此操作。
ORM 映射
您不能使用PHP注解或属性来定义您的ORM映射。映射应该配置在领域之外。对于Doctrine,您可以使用XML或PHP映射。
作为嵌入对象的值对象
可以使用Doctrine嵌入对象来实现值对象映射,但仅限于一对一关系。嵌入对象的优势在于,您可以使用Doctrine查询语言轻松查询字段。
作为JSON的值对象
另一种映射值对象的方法是定义自定义的Doctrine类型。扩展Fusonic\DDDExtensions\Doctrine\Type\ValueObjectType
并实现convertToDatabaseValue
和convertToPHPValue
方法来定义值对象的映射。该类提供了四个辅助方法用于序列化:serialize
、deserialize
、serializeArray
和deserializeArray
,如果您想存储值对象的数组(一对多关系)。示例请参考这里和这里。在数据库中,对象将以json
格式存储。
实现自定义类型后,您需要注册它。
在Doctrine中,无法直接查询JSON数据,但可以通过扩展实现(示例)。