popcorn4dinner/commands

命令模式的实现,设计用于作为洋葱架构应用的应用层

0.2.1 2018-04-19 10:18 UTC

This package is not auto-updated.

Last update: 2024-09-29 05:01:56 UTC


README

命令模式的具有意见的实现,设计用于作为基于洋葱架构应用的应用层。

./onion_schema.png

安装

composer require popcorn4dinner/commands

使用

您应用程序中的每个操作都将由2个类表示。命令和处理器:命令代表您操作的输入。它是一个简单的具有状态的对象,包含执行操作所需的所有数据。

class PlaceOrder implements CommandInterface
{
    public $itemId;

    public $placedAt;

    public $price;
}

处理器负责执行操作的流程。它确保所有必需的步骤都按正确的顺序执行。

class PlaceOrderHandler extends AbstractAuthenticatedCommandHandler
{
    /**
     * @param CommandInterface $command
     * @return Order
     * @throws PolicyValidationException
     */
    public function handle(CommandInterface $command, $user = null): Order
    {    
        $order = $this->orderFactory->createOrderFor($command->orderItem, $command->placedAt, $user->id)

        if($this->orderValidator->isValid($order, OrderAction::create())){
            $order = $this->orderRepository->create($order);

            $this->paymentGateway->schedulePaymentFor($order->price);

            $this->mailer->sendConfrmationEmailTo($user, $order);
            $this->mailer->sendIncomingOrderNotificationTo($CUSTOMER_SERVICE, $order);

            $this->crm->upload($order);
        }

        return $order;
    }

}

处理器类型

此库提供两种处理器类型,一种用于具有用户概念的应用程序(AbstractAuthenticatedCommandHandler),另一种用于没有用户概念的应用程序(AbstractCommandHandler)。请参阅示例文件夹以获取更多详细信息。

日志记录

如果您使用实现 Psr/log/LoggerInterface 的记录器实例化您的处理器,则应用程序中执行的所有操作以及可能的失败都将自动记录。

填充命令

此库附带一个小的助手,如果遵循简单的约定,它将为您填充命令对象。

  1. 您命令中的属性必须与您请求中的参数同名
  2. 请求参数的名称为蛇形,命令属性为驼峰式

示例

使用此助手可以使您的控制器非常精简:现在它的唯一责任是在您的应用程序和Web之间进行接口。

class UserController {

[...]

   public function resetPassword(
        Request $request,
        RequestPasswordResetHandler $handler,
        \Twig_Environment $twig
    )
    {
        if ($request->getMethod() === static::HTTP_GET) {
            return $twig->render('forgot-password.twig');

        } else {
            $command = $this->commandPopulator->populate(new RequestPasswordReset(), $request);
            $user = $handler->handle($command);

            return $twig->render('login.twig', ['message' => 'You will receive an email with reset instructions shortly.']);
        }
    }

}

关于洋葱架构的一些话

在我看来,洋葱架构本质上是一回事:一个简化版的领域驱动设计(DDD),有助于您保持代码易于阅读、扩展、维护和推理。这是一篇关于洋葱架构的优秀博客文章,值得一读:http://jeffreypalermo.com/blog/the-onion-architecture-part-1/

简要总结

如果您想让您的应用程序成为洋葱,您将基本上区分三个层次:基础设施、应用和领域。也就是说,大多数文献描述了更多内容,或者它们略有不同。我尽量保持简单,所以我们采用上面提到的三个。

黄金法则: 只允许反向依赖。这意味着基础设施中的所有内容都可以依赖于应用程序或领域层,但反之则不行。为了使这成为可能,通常的做法是在您的领域内部创建接口,以确定您要使用的基础设施的合同。同时,这也允许您在运行时替换数据库、文件系统,甚至框架,而不会影响应用程序的重要部分。您甚至可以想到这样的场景,即对于测试,您想使用内存数据库而不是运行SQL服务器,或将电子邮件写入文件而不是发送它们。遵循洋葱架构,这些事情根本不是问题。

./onion_schema.png

想象我们正在设计一个允许在线商店下订单的微服务...

基础设施

洋葱的基础设施层包含UI、与数据库、文件系统等进行通信的存储库等。如果您使用MVC框架,这也是您的基础设施层的一部分。

应用

这里就是使用这个库的地方。应用层负责您应用程序的流程。这是您决定需要按什么顺序执行哪些步骤的地方。以接收到的订单为例

class PlaceOrderHandler extends AbstractAuthenticatedCommandHandler
{
    /**
     * @param CommandInterface $command
     * @return Order
     * @throws PolicyValidationException
     */
    public function handle(CommandInterface $command, $user = null): Order
    {    
        $order = $this->orderFactory->createOrderFor($command->orderItem, $command->placedAt, $user->id)

        if($this->orderValidator->isValid($order, OrderAction::create())){
            $order = $this->orderRepository->create($order);

            $this->paymentGateway->schedulePaymentFor($order->price);

            $this->mailer->sendConfrmationEmailTo($user, $order);
            $this->mailer->sendIncomingOrderNotificationTo(static::CUSTOMER_SERVICE, $order);

            $this->crm->upload($order);
        }

        return $order;
    }

}

在这个例子中,您的主要域模型可能是一个订单。您的的一部分也可能包括所有关于订单应该是什么样子以及如何与外部服务(如支付网关物流合作伙伴的API)通信的知识。在我们的例子中,您可能会找到类似的类

  • 订单
  • OrderAction
  • OrderValidator
  • OrderRepositoryInterface
  • MailerInterface
  • PaymentGatewayInterface
  • CrmInterface