flyingfoxx / commandcenter
适用于任何框架的命令和领域事件。包括Laravel的实现。
Requires
- php: >=5.4.0
Requires (Dev)
- phpspec/phpspec: ~2.0
This package is not auto-updated.
Last update: 2024-09-24 02:26:58 UTC
README
此软件包提供了一种框架无关的架构,用于在应用程序中利用命令和领域事件。任何组件都可以轻松扩展以适应您的特定使用。
包括Laravel的实现。
灵感来源于并扩展了 Jeffrey Way 在 Laracasts 的作品
安装
通过Composer安装CommandCenter。
"require": { "flyingfoxx/commandcenter": "~1.0" }
Laravel
如果您使用Laravel,请更新 app/config/app.php
以包含软件包的服务提供者。
'Flyingfoxx\CommandCenter\Laravel\CommandCenterServiceProvider'
请查看此文档末尾的Laravel部分以获取更多功能!
使用方法
在开始之前,不建议在架构不是很重要的小型项目中使用这种方法。此软件包有助于构建您的业务逻辑,坚持单一职责原则,并使控制器保持简洁。
CommandApplication
为了开始使用,您必须通过实现软件包的 CommandApplication
接口并注册任何绑定来将您的特定应用程序注册到CommandCenter。请参考包含的Laravel实现和服务提供者以在其他框架中使用。参考类如下
- Flyingfoxx\CommandCenter\Laravel\Application
- Flyingfoxx\CommandCenter\Laravel\CommandCenterServiceProvider
命令
一切从命令开始。命令是对领域执行的特定操作的“指令”。它被表示为一个简单的DTO(数据传输对象),携带执行该特定命令所需的数据。
例如,假设您需要注册一个新用户。您将创建一个 RegisterUserCommand
,其外观如下所示
<?php namespace Foxx\Users; class RegisterUserCommand { public $username; public $password; public function __construct($username, $password) { $this->username = $username; $this->password = $password; } }
以
Foxx
作为示例应用程序
因此,您现在可以将所有逻辑放入控制器中,而是创建一个命令,将数据传递到包含逻辑的处理程序。但现在您需要一个传递命令到其相应处理程序的方法。那么,总线怎么样?
命令总线
首先,您需要将软件包的 CommandBus
注入到控制器中。这将用于将命令传递到其相应处理程序。
<?php use Flyingfoxx\CommandCenter\CommandBus; class RegistrationController { protected $commandBus; public function __construct(CommandBus $commandBus) { $this->commandBus = $commandBus; } }
然后,创建并传递命令到命令总线。
<?php use Flyingfoxx\CommandCenter\CommandBus; use Foxx\Users\RegisterUserCommand; class RegistrationController { protected $commandBus; public function __construct(CommandBus $commandBus) { $this->commandBus = $commandBus; } public function store() { // Grab the input (using Laravel in this example) $input = Input::only('username', 'password'); // Create command $command = new RegisterUserCommand($input['username'], $input['password']); // Pass command to command bus $this->commandBus->execute($command); } }
通过这样做,命令总线将命令传递到其相应处理程序,在那里将执行命令的逻辑。
它是通过以下方式将命令类映射到相应处理程序类的
- RegisterUserCommand > RegisterUserHandler
- PostBlogEntryCommand > PostBlogEntryHandler
请记住,您可以通过实现软件包的
CommandTranslator
类轻松更改此映射。不要忘记更新任何应用程序绑定。
命令处理程序
现在您需要一个处理程序类来处理命令。这将是命令总线交付命令的地方。如果命令类是 RegisterUserCommand
,则处理程序类必须是 RegisterUserHandler
。
处理程序类必须实现软件包的 CommandHandler
接口,需要 handle()
方法。
<?php namespace Foxx\Users; use Flyingfoxx\CommandCenter\CommandHandler; class RegisterUserHandler implements CommandHandler { protected $user; public function __construct(User $user) { $this->user = $user; } public function handle($command) { $user = $this->user->register($command->username, $command->password); return $user; } }
它应该可以正常工作。你现在可以在你的应用程序中利用命令。但你现在需要一种方式来钩入这些命令以执行其他任务。你可以使用域事件和监听器,这些事件和监听器只会在事件发生时执行这些任务。
事件
域事件是当你的领域发生了一些重要的事情时。从上一个例子继续,一旦执行了RegisterUserCommand
,就发生了一个事件,即用户已被注册。
因此,你可以调用事件UserWasRegistered
,它将表示一个简单的DTO(数据传输对象),该对象携带事件监听器所需的数据。
<?php namespace Foxx\Events; use Foxx\Users\User; class UserWasRegistered { public $user; public function __construct(User $user) { $this->user = $user; } }
现在你有了事件,你必须在你的模型类中引发事件(在应用程序中创建事件的实例)。为此,你可以使用包的EventGenerator
特性,如下所示
<?php namespace Foxx\Users\User; use Flyingfoxx\CommandCenter\Eventing\EventGenerator; use Foxx\Events\UserWasRegistered; class User { use EventGenerator; protected $username; protected $password; public function register($username, $password) { $this->username = $username; $this->password = $password; $this->raise(new UserWasRegistered($this)); return $this; } }
好了,现在UserWasRegistered
事件已经被引发,并准备好分发(使你的应用程序知道其发生)。你可以在命令处理器类中通过注入包的EventDispatcher
类并调用dispatch($events)
方法来实现这一点。
<?php namespace Foxx\Users; use Flyingfoxx\CommandCenter\CommandHandler; use Flyingfoxx\CommandCenter\Eventing\EventDispatcher; class RegisterUserHandler implements CommandHandler { protected $user; protected $dispatcher; public function __construct(User $user, EventDispatcher $dispatcher) { $this->user = $user; $this->dispatcher = $dispatcher; } public function handle($command) { $user = $this->user->register($command->username, $command->password); $this->dispatcher->dispatch($user->releaseEvents()); return $user; } }
别忘了在实体对象上调用
releaseEvents()
(因为它使用了EventGenerator
特性)。这样,所有分发的事件都会从引发的事件中删除。
事件监听器
现在事件已经被引发和分发,你需要为该事件注册监听器。
遵循惯例,如果我们引发事件Foxx\Events\UserWasRegistered
,那么要监听的事件名称将是Foxx.Events.UserWasRegistered
。
下一步是在你的应用程序中注册一个事件监听器类。你可能需要在用户注册后发送电子邮件给用户。例如,在Laravel中你可能这样做
Event::listen('Foxx.Events.UserWasRegistered', 'Foxx\Listeners\EmailNotifier');
或者要注册此监听器以监听任何应用程序事件,你可能尝试这样做
Event::listen('Foxx.Events.*', 'Foxx\Listeners\EmailNotifier');
因此,现在,当在此命名空间下分发任何事件时,此监听器类将触发其handle()
方法。当然,你可能只想使用此监听器类来响应某些事件。你可以使用包的EventListener
类来实现这一点。
通过简单地扩展此EventListener
类,你可以创建遵循惯例的方法来处理每个特定事件。惯例是如果分发的事件是UserWasRegistered
,则监听器类中触发的方法将是whenUserWasRegistered
。如果找不到此方法,它将简单地继续。
有了这个,你的EmailNotifier
类可能看起来像这样
<?php namespace Foxx\Listeners; use Flyingfoxx\CommandCenter\Eventing\EventListener; use Flyingfoxx\Events\UserWasRegistered; class EmailNotifier extends EventListener { public function whenUserWasRegistered(UserWasRegistered $event) { // send an email to the user } }
装饰命令总线
有时你可能想要装饰命令总线以在处理命令之前执行额外操作。此包已包含一个验证装饰器。
验证
包含的验证命令总线可以作为装饰器与主命令总线一起使用。这必须在你的特定应用程序中实现。一旦命令被传递给命令总线,它将检查相关的验证器类并调用其validate($command)
方法。否则,它将继续。这样,你可以在执行命令和引发任何域事件之前执行任何验证。
创建验证器类的惯例如下
- RegisterUserCommand > RegisterUserValidator
只需包含一个validate($command)
方法,并像往常一样执行你的验证。
<?php namespace Foxx\Users; use Flyingfoxx\CommandCenter\CommandValidator; class RegisterUserValidator implements CommandValidator { public function validate($command) { var_dump('validating register user command'); } }
你可以通过简单地创建一个实现包的
CommandBus
接口的类并遵循装饰器设计模式来创建自己的装饰器。最佳选择是复制包含的ValidationCommandBus
类并根据自己的需求修改它。
别忘了在特定应用程序中正确加载你的新装饰器。别忘了为你的新装饰器包含一个新的翻译方法,方法是通过扩展
MainCommandTranslator
类或创建一个实现包的CommandTranslator
类的新类。
Laravel
如果您是Laravel用户,那么在这个包中为您提供了现成的解决方案。它包括Laravel实现包的CommandApplication
接口以及一个可以加载到配置中的服务提供者。默认的命令总线使用验证装饰器,已经为您设置好了。再次提醒,如果您需要提供额外的装饰器,按照验证命令总线的模式进行,应该可以正常工作。
这个包包含了几个为Laravel使用而设计的特质。这些特质有助于清理您的类并提高可读性。
Commander
这个特质本质上是对命令总线进行了封装,可以在任何控制器中使用。您无需注入命令总线,而是可以按照以下方式注入包的Commander
特质:
<?php use Flyingfoxx\CommandCenter\Laravel\Commander; use Foxx\Users\RegisterUserCommand; class RegistrationController { use Commander; public function store() { // Grab the input (using Laravel in this example) $input = Input::only('username', 'password'); // Create command $command = new RegisterUserCommand($input['username'], $input['password']); // Pass command to command bus $this->execute($command); } }
现在您可以直接在控制器本身上调用execute。另一个选择是将这个方法放入基控制器中,只编写一次。
Dispatcher
这个特质本质上是对事件分派器进行了封装,可以在您的处理器类中使用。您无需注入事件分派器,而是可以按照以下方式注入包的Dispatcher
特质:
<?php namespace Foxx\Users; use Flyingfoxx\CommandCenter\CommandHandler; use Flyingfoxx\CommandCenter\Laravel\Dispatcher; class RegisterUserHandler implements CommandHandler { use Dispatcher; protected $user; public function __construct(User $user) { $this->user = $user; } public function handle($command) { $user = $this->user->register($command->username, $command->password); $this->dispatchEventsFor($user); return $user; } }
因此,您不需要调用dispatch($events)
,而是需要在处理器类上调用dispatchEventsFor($entity)
,传入实体。这个特质还会自动释放传入实体上的事件。
Laravel 5 更新
现在,随着Laravel 5的发布,您可以使用方法注入以及使用表单请求对象作为命令对象。映射如下:
- RegisterUserRequest => RegisterUserHandler
- PostBlogEntryRequest => PostBlogEntryHandler
其他所有内容都应该按同样的方式工作。以下是一个示例控制器使用方法:
<?php namespace Foxx\Http\Controllers; use Flyingfoxx\CommandCenter\Laravel\Commander; use Foxx\Users\RegisterUserRequest; class RegistrationController { use Commander; public function store(RegisterUserRequest $request) { // Pass command to command bus $this->execute($request); } }
结论
以上就是所有内容。请随意扩展,提问或发表评论。同时,请确保查看Jeffrey Way在Laracasts上的Commands and Domain Events系列,了解更多相关信息。