发布于 2026-01-06 12 阅读
0

领域驱动设计以 PHP 和 Symfony 为开发 DDD 和 Symfony 结论

使用 PHP 和 Symfony 进行领域驱动设计

感性发展的重演

DDD 和 Symfony

结论

英文版请点击此处查看。

关于PHP和Symfony框架(版本 2.x 至 5.x)的试点开发的观察、考虑和建议

注意: pas l'habitude de rédiger en français sur les sujets 技术。英语来源、留置权和参考资料

感性发展的重演

DDD在 Symfony/PHP 社区中广受欢迎。我对 DDD 的具体传播动机感到不知足。

定义

DDD是一种范式、一种方法、一种方法、一种思考和观察的方式。

DDD 的价值在于其定义:“développement Piloté par le métier”。如果您继续开发并理解 qu'une seule 选择了 DDD:这就是 intitulé。如果您将注意力集中在专业知识、专业背景、专业术语、专业词汇、编码器 IDE 的专业知识中:将代码序列与 DDD 原则一致。

Le DDD 不是建筑逻辑或建筑风格。 “实体”、“值对象”或“聚合根”都是东方对象中的细节技术、独特性用途。

DDD 是开发者代码中的结晶技术的本质

混凝土

如果项目负责人、“利益相关者”或责任人在 DDD 内容中的时间超过了 80% 的代码。

PHP高级编程语言的实际用途是考虑到英语版本的极端应用。在 DDD 中,PHP 代码可以定义为“英语东方对象”中的一个概念或概念。

一个类似于我的代码?英语中的一些技巧说明:

$cashier->checkout($cart) // $caissier->encaisse($panier)
$cashier->charge($card) // $caissier->debite($carte)
$accountant->write($payment) // $comptable->ecrit($paiement)
$compliance->verify($payment) // $controlleurFinancier->verifie($paiement)
Enter fullscreen mode Exit fullscreen mode

一个类似于“领域”(métiers)的类? Là encore, à des Concepts métiers écrits dans un anglais pariculier:

namespace Legal\Company\France;

/**
 * French unique registration number for a specific establishments of a company
 * issued by INSEE
 * SIRET: "Système d'Identification du Répertoire des ETablissements"
 * 
 * @see https://www.economie.gouv.fr/entreprises/numeros-siren-siret
 */
Class Siret implements RegistrationNumber
{
    private Siren $siren;
    private Nic $nic;

    public function __construct(string $number)
    {
        try {
            $this->siren = new Siren(mb_substr($number, 0, 9));
            $this->nic = new Nic(mb_substr($number, 9));
        } catch (\Exception $exception) {
            throw new \DomainException('Invalid SIRET number');
        }
    }

    public function getNumber(): string
    {
        return $this->siren->getNumber() . $this->nic->getNumber()
    }
}
Enter fullscreen mode Exit fullscreen mode
namespace Legal\Company\France;

/**
 * Unique registration number for a french company
 * Issued by INSEE, composing the first part of SIRET
 * Système d'Identification du Répertoire des ENtreprises
 *
 * @see https://bpifrance-creation.fr/encyclopedie/formalites-creation-dune-entreprise/formalites-generalites/numeros-didentification
 */
class Siren
{
    private string $number;

    public function __construct(string $number)
    {
         if (preg_match('/^[0-9]{9}$/', $number) !== 1) {
            throw new \DomainException('Invalid SIREN number');
         }

         $this->number = $number;
    }

    public function getNumber(): string
    {
        return $this->number;
    }
}
Enter fullscreen mode Exit fullscreen mode

法国开发者们的家庭成员都拥有这些概念。复仇时,如果您想了解法国社会入学的功能,请简单地了解您的想法,以实现对自己的兴趣。

美德

代码是“ expressif,是“lisible”的。 Il traduit mot pour mot lesconcepts et le contexte métier.

代码不是“expressif”,而是“ immutable ”。我不认为设计模式是“最佳实践”。我简单地尊重法国企业的不成熟规则(SIRET、SIREN 和 NIC),这些数字是不可变的,而代码是不可变的......以及“值对象”的对象的明显性质,请注意。 Le fait qu'ils soient des VO n'est donc qu'une de l'implémentation des Métiers!

La cuillère n'existe pas

关注“最佳实践”、关注“设计模式”、关注“流畅界面”、“事件调度”、MVCSoCSRPSOARADCRUDDRY ...关注技术、先行实施、先行先行。

利用语言 (ici PHP) 进行建模,使工作变得更加简单。告诉一位初级专业人士的专家,在流程专业的代码中开发“明确”的明确和明确的信息。

如果您在命名空间中实现了 setters 和 getters 的实体,以及“值对象” \ValueObject\,那么您就可以了解 DDD 的本质。

我坚持:DDD 不是实现的一部分,DDD 是专业和代码中公共语言的一部分。

DDD 的价值的最终目的:
在机器执行时刻的价值代码基础。有关 DDD 代码的基础,对于非执行人员来说是非常重要的。这是一个知识中心、人类劳动遗产的中心。

DDD 和 Symfony

我在 2014 年完成了 DDD 自学,并在今年完成了强化讲座和实施,并以“高效”的方式使用了这些工具(PHP、Symfony 和 Doctrine)。埃文斯提到的汽车:

“不要对抗你的框架”。

领域驱动设计:解决软件核心的复杂性

项目和复杂性的功能,请适应实施。一般而言,我对新项目的热爱或对 DDD 的新开发教练的指导,我是这样的:

  • J'utilise & j'abuse du命令模式Symfony Messenger est parfait)

  • 我不使用CQRS模式

  • 我不使用 pa le 模式ES

  • J'utilise Behat & le BDD au niveau de me commandes/handlers, couverture de test intégrale

  • Je n'utilise que très 稀有 les test unitaires, 20-30% de couverture en général.

为什么没有 CQRS 或 d'ES?

Parce que ça coûte un rein! (查看ES 中软硬堆栈的后续文章。)
Ce sont des solutions ultra puissantes。 Mais à déployer uniquement lorsque cela fait sens (rapport coût & besoin)。

ES

我想在 php 开发过程中为家人提供事件采购、为创建者进行实验、在时间上维护或修改事件存储校正。 Souvent,是在 manque de “maturité” 模式、apprend、refacto 和 migre 上首次亮相的项目。 Mais sur un event store,aucune 迁移。历史反映了过去实施中的错误,这使维护逻辑的问题倍增。

CQRS

Le CQRS 在问题/大阶梯卷中具有强大的作用。在上下文类型中,CQRS 指定了“术语一致性”的复杂性,也称为最终一致性。我可以自由地设想具体情况。

我拥有 2 种模式、分离和同时的经验,在 DDD、Symfony、parfois Doctrine 和 SQL/MongoDB 项目中

La结构

  • src/Controller/Symfony 控制器
  • src/Command/Symfony CLI 命令

  • src/Action/:命令 + 命令处理程序

  • src/Domain/AR、实体、VO、存储库接口

  • src/*基础设施及其他方面。

Loin d'être ideal, le repertoire nommé /Domain/est plutôt pratique。在地址栏上使用名称特定的名称来替换“域”,例如:/Ecommerce/

关于“域”的多元性的争论

存在于子项目中的控制域的存在问题。这是关于存在Core Domain和分离问题的学校和学者的思想Supporting Domains。 De mon côté, c'est tranché, ces derniers sont Organisés sans 区别特别之处 dans /Domain/:

  • /Domain/Payment
  • /Domain/Compliance
  • /Domain/Delivery

Problème réglé,c'est cohérent(一致),ça gagne à tous les coups。

“Domain”的内容?

一个实用的src/Domain/概念和实现的整体。 Il ya certaines Class qui sont persistantes (en base de données), d'autres non.详细说明服务内容。

Comme src/Entityn'existe plus,la 配置 de Doctrine est modifiée 扫描器 le 曲目src/Domain/dans 合奏。这些注释用于定义基本映射、本地化,这对我来说是有价值的。

证据表明,在项目中,无法重新组织/Domain/相关专业

/Domain/Legal/Company/France/RegistrationNumber/Siret.php
/Domain/Legal/Company/France/RegistrationNumber/Siren.php
/Domain/Legal/Company/France/RegistrationNumber/Nic.php
Enter fullscreen mode Exit fullscreen mode

Commande qui conquiert

用法

使用系统命令模式来实现以下目的:

  • les命令处理程序在 les 六边形架构中写入 parfaitement

  • 命令处理程序在沙发“模式”操作上具有连贯性,是唯一的入口点。

  • 命令处理程序澄清了业务操作的边界事务。

  • 服务应用程序的命令处理程序正常化

  • 命令处理程序Soulagent 聚合根上的问题

J'abuse pas mal de ces命令处理程序汽车je les détourne parfois de leur 但总理:

拉布斯

  • 如果你有一些不确定性、不确定性、理解力,那么你可能会觉得很简单或很简单:我可以利用暂时的专业代码库存,在专业服务或专业对象中从事高级工作。

  • 我可以使用同步器来增强处理过程:可以使用SAGA恐怖电影的实现方式

  • 如果AR的实现非常微妙,那么在使用 Symfony/Doctrine 时会遇到困难,请在处理程序中编写逻辑部分(ça craint, vraiment à éviter)。

Cela doit sonner très faux aux oreilles des puristes, mea maxima culpa. J'ai fait mon deuil & je suis complètement dans l'acceptance car ça fonctionne très bien pour moi。

命名

Les commandes sont nommées d'après les processus métiers, les actions qu'elles representent.组织的具体情况如下:

src/Action/Checkout.php
src/Action/CheckoutHandler.php

src/Action/FraudulentPaymentDeclaration.php
src/Action/FraudulentPaymentDeclarationHandler.php

src/Action/Compliance/Registration.php
src/Action/Compliance/RegistrationHandler.php
src/Action/Compliance/FrenchEstablishmentRegistration.php
src/Action/Compliance/FrenchEstablishmentRegistrationHandler.php
Enter fullscreen mode Exit fullscreen mode

或许我应该用祈使句来表示命令:注册(Register)而不是注册(Register)。

——弗洛里安·克莱因

Un process métier, une action métier porte généralement un nom。这是最重要的。这是不可能的。

Un verbe est un indicatesur d'une potentielle faiblesse de compréhension du métier,例如:

  • MarkAsRead
  • ValidateStatus

注意,这是一个方向,当我到达时,我们将收到命令首次亮相的苏文特。

命令实施细节

Les commandes sont donc les Expressions d'une Intention (métier)。这些模型和实现都是简单的DTO,可以接受原语,促进六边形架构的应用程序端口/适配器。代表原始知识和概念的边界的命令在一般应用中:

namespace Action\Compliance;

use Domain\Geo\Address;
use Domain\Geo\Country;

class Registration
{
  private string $userId;
  private string $name;
  private string $addressLine1;
  private string $addressLine2;
  private string $addressZip;
  private string $addressLocality;
  private string $addressCountry;

  public function __construct(
    string $userId,
    string $name, 
    string $addressLine1, 
    string $addressLine2,
    string $addressZip,
    string $addressLocality,
    string $addressCountry
  ) {
    $this->userId = $userId
    // ... boring stuff
  }

  public function getUserId(): Uuid
  {
    return new Uuid($this->userId);
  }

  public function getName(): string
  {
    return $this->name;
  }

  public function getAddress(): Address
  {
    return new Address(
      $this->addressLine1,
      $this->addressLine2,
      $this->addressZip,
      $this->addressLocality,
      new Country($this->addressCountry)
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

验证 Symfony 的一般方法是为了避免“形式”错误并避免使用。 Là encore,来自注释。在报复过程中,行业规则的优势将在行业对象(领域)上实施和实施,违反了系统中的规则,导致了例外。

六边形建筑的实现方法如下:

  • Les Symfony/Form peuvent declarer le DTO commande comme model , la 著名数据类

  • Symfony/Console 的命令 (CLI) 初始化程序、DTO 命令和参数

  • Un Worker/Consumer RabbitMQ 在 DTO 命令中使用消息和转换器进行序列化

  • Un Symfony/EventListener peut Transformer Un Event en DTO 命令

  • Web Socket 等……

在复仇中,我的命令是从汉德勒那里传递出来的,这是对物体的本质的操纵(VO,实体)。 Un objet métier fournit des garanties fortes:

  • Un état systématiquement valide (d'après les règles métiers)

  • Un état quasi-systématiquement immutable(不可变)。

  • Une API 公共自动独特性变更和专业规范

操纵类型的物体是一种幸福,一种灵魂:认知能力是非常可靠的,与系统的信任相反,非常强大。

Quelques remarques :

  • AR 和实体的 ID 的特定类型(UserId、BookId,...)。维护教义。某些项目是复杂的,我会在使用时到达。

  • 评论 仲裁者 entre VO (object métier) 和原始人?
    如果这个概念是重要的,那么这个概念的具体规则就是:VO。西农,原始人已经足够了。 PHP 的恢复,Java 的恢复。

Les命令处理程序

命令处理程序的角色是 AR 协调员、实体、简单的协调员概念和概念。

Très souvent ils utilisent un repositorypour Hydrauer l'AR contacté par l'action, la commande métier.

Le handler appelle ensuite des méthodes sur les objets métiers (AR/Entité)。数据映射持久性周期处理程序实现原则。因此,某些处理程序可协调基础设施的其他细节。 Très souvent relatifs aux IO necessaires à la Composition d'un état métier Correct (satisfaisant) répondant au pré-requis d'une action métier souhaitée.

Symfony/Messenger 负责命令和处理程序的管理。通过 HTTP 控制器、Symfony/Commande 或 Worker RabitMQ 即可,它足以注入总线和调度命令

在流程过程(trop)长时,异步命令的传递者不需要执行任何必要的努力。Symfony/Messenger 配置的简单修改

  • la commande est envoyée via un RabbitMQ。
  • 处理程序连接到队列的“消费者”。

使用 CQRS,是同步命令执行的主要功能,使用Symfony/Messenger 的功能即可恢复处理程序的结果,并在序列化后获得漂亮的REST表现une 响应 HTTP。

有界上下文(BC)

默认情况下,我考虑的是公元前 1 年的情况。我理解项目的解决方案。 Outout simplement,le projet grandi,grossi et se découvre également de nouveaux BC's。

我已到达事后背景分离的苏文特。 La présence de plusieurs contextes peut 能指:

  • 对职业的不理解
  • 职业生涯中的不确定性
  • 极其复杂

组织者 ses BC's

微服务,CQRS 是这样的:ça coûte un rein,si c'est mal orchestré:ça en coute deux。如果你部署了军队,你就有机会了。西农,地方原则,澳洲行军:

src/BC1/Controller/
src/BC1/Command/
src/BC1/Domain/

src/BC2/Controller/
src/BC2/Command/
src/BC2/Domain/
Enter fullscreen mode Exit fullscreen mode

普尔奎伊? BC 的最终操作是分开或划定的。 Très risquée également。 BC 的同步器是两个相对的。

从这个角度来看,您可以对 BC 的代码进行迭代、理解、重构和替换,域到域、从BCBC。请注意“成熟模式”和理解技巧。您将在 BC 上享受到与 K8 不同的自由...以及您的K8的乐趣。

结论

在此之前,我们将详细介绍 DDD 和 Symfony 所采用的技术。 Rappelez-vous le but de cet 文章和儿子的消息:“ Oubliez la tech & concentrez vous sur le métier

这些详细技术是通过 PHP 和 Symfony 生态系统协同作用的 DDD 方法来浓缩的知识、经验、方法或妥协的组合。 Mais encore une fois: 这是 DDD 中最重要的时刻。

这些方法是通过代码“注释”来进行配置的。 Mais de concentrer les attempts & la valeur sur le “quoi” et le “pourquoi”.

编辑 6 月 2 日
Lucas Courot a réagi via twitter sur 3 point:

  • 异步命令
  • 小黄瓜(Behat)
  • 例外情况下的通知

Un échange de plusieurs tweets resume nos idées, comments, divergentes or convergentes, toujours intéressantes.

文章来源:https://dev.to/ludofleury/domain-driven-design-avec-php-symfony-1p2h