通俗地講,它被稱為command bus模式,但該庫對命令和查詢進行了區分,並允許您在命令處理程序中強制不返回值,以保持與 CQRS 模式保持一致。
這是一個獨立的函式庫,唯一的兩個依賴項是 PSR-11 Container 和 PSR-3 Log 接口,以實現更好的互通性。
目錄:
使用 Composer 安裝庫:
composer require sco/message-bus
您需要遵循 PSR-4 自動載入標準,並建立自己的服務容器類,這是實作PsrContainerContainerInterface
的問題,並且可以像庫用於其測試套件ScoMessageBusTestsStubContainerInMemoryContainer
,或者您可以編寫器需要一個遵循 PSR-11 標準(如 PHP-DI)的服務容器庫。
require ' vendor/autoload.php '
$ container = new InMemoryContainer( $ services )
$ bus = new Sco MessageBus Bus ( $ container );
$ bus -> dispatch ( new FindPostByIdQuery ( 1 ))
我們在這裡可以使用兩種方法,裝飾庫提供的 Bus 類,或註入服務定位器。有關更多信息,您可以閱讀 Symfony 文檔
我們可以建立一個新的 Decorator 類別,它將實作 Symfony 的SymfonyContractsServiceServiceSubscriberInterface
介面:
use Sco MessageBus Bus ;
use Sco MessageBus Message ;
use Sco MessageBus Result ;
use Psr Container ContainerInterface ;
use Symfony Contracts Service ServiceSubscriberInterface ;
class MessageBus implements ServiceSubscriberInterface
{
private Bus $ bus ;
public function __construct ( ContainerInterface $ locator )
{
$ this -> bus = new Bus ( $ locator , [], null , new UuidV4Identity ());
}
public function dispatch ( Sco MessageBus Message $ message ): Result
{
return $ this -> bus -> dispatch ( $ message );
}
public static function getSubscribedServices (): array
{
return [
FindPostByIdHandler::class,
SavePostHandler::class
];
}
}
使用這種方法,應用程式中的所有處理程序都必須添加到getSubscribedServices
返回的數組中,因為Symfony 中的服務默認情況下不是公開的,而且它們確實不應該公開,所以除非您在映射器時將處理程式加入到此陣列中完成映射後,它將無法找到處理程序,並且將拋出服務未找到容器異常。
另一種方法是將具有所有處理程序的服務定位器注入到庫的匯流排中。這將在服務註冊 yaml 檔案中完成。
匿名服務定位器:
services :
_defaults :
autowire : true
autoconfigure : true
# Anonymous Service Locator
ScoMessageBusBus :
arguments :
$container : !service_locator
' @FindPostByIdHandler ' : ' handler_one '
' @SavePostHandler ' : ' handler_two '
顯式服務定位器定義:
services :
_defaults :
autowire : true
autoconfigure : true
# Explicit Service Locator
message_handler_service_locator :
class : SymfonyComponentDependencyInjectionServiceLocator
arguments :
- ' @FindPostByIdHandler '
- ' @SavePostHandler '
ScoMessageBusBus :
arguments :
$container : ' @message_handler_service_locator '
讓我們擴展這些配置並使用 Symfony 服務容器的標籤功能自動將處理程序新增至總線:
使用!tagged_locator
:
services :
_defaults :
autowire : true
autoconfigure : true
_instanceof :
ScoMessageBusHandler :
tags : ['message_handler']
# Anonymous Service Locator
ScoMessageBusBus :
arguments :
$container : !tagged_locator message_handler
顯式服務定位器定義:
services :
_defaults :
autowire : true
autoconfigure : true
_instanceof :
ScoMessageBusHandler :
tags : ['message_handler']
# Explicit Service Locator
message_handler_service_locator :
class : SymfonyComponentDependencyInjectionServiceLocator
arguments :
- !tagged_iterator message_handler
ScoMessageBusBus :
arguments :
$container : ' @message_handler_service_locator '
要在 Laravel 框架中有效地使用它,您所要做的就是在 Laravel 的服務容器中註冊總線,並將該容器作為參數提供給庫的 Bus 類別:
$ this -> app -> bind ( Sco MessageBus Bus::class, function ( $ app ) {
return new Sco MessageBus Bus ( $ app );
});
每個命令或查詢及其各自的結果物件組合將被指派一個唯一的標識,例如命令,並且其各自的結果物件將具有標識00000001
。這對於日誌記錄、審計或調試目的非常有用。
預設的身份產生策略是一個簡單的ScoMessageBusIdentityRandomString
產生器,以將外部依賴性保持在最低限度。要使用其他東西,您可能需要像 https://github.com/ramsey/uuid 這樣的函式庫並實作ScoMessageBusIdentity
。
use Sco MessageBus Identity ;
class UuidIdentity implements Identity
{
public function generate () : string
{
return Uuid:: uuid7 ()-> toString ();
}
}
FindPostByIdQuery
將被對應到FindPostByIdHandler
,或者SavePostCommand
將被對應到SavePostHandler
。#[IsCommand(handler: SavePostHandler::class)]
或#[IsQuery(handler: FindPostByIdHandler::class)]
新增至您的命令/查詢類別。 handler
參數名稱可以省略,這取決於您的個人喜好。ScoMessageBusMapper
介面來實現。每個命令都將通過一系列中間件傳遞。預設情況下,該鍊是空的,但該庫確實提供了一些開箱即用的中間件:
begin
、 commit
和rollback
步驟是普通的Closure
對象,因此您可以使用您喜歡的任何 ORM 或持久性方法。要建立您自己的自訂中間件,您需要實作ScoMessageBusMiddleware
介面並將其提供給匯流排:
use Sco MessageBus Bus ;
use Sco MessageBus Message ;
use Sco MessageBus Middleware ;
class CustomMiddleware implements Middleware
{
public function __invoke ( Message $ message , Closure $ next ) : mixed
{
// Do something before message handling
$ result = $ next ( $ message );
// Do something after message handling
return $ result ;
}
}
$ bus = new Bus (middlewares: [ new CustomMiddleware ()]);
如果新增ScoMessageBusMiddlewareEventMiddleware
您將能夠訂閱以下事件:
MessageReceivedEvent - 在收到訊息但在處理之前引發。
use Sco MessageBus Event Subscriber ;
use Sco MessageBus Event MessageReceivedEvent ;
$ subscriber = new Subscriber ();
$ subscriber -> addListener (MessageReceivedEvent::class, function ( MessageReceivedEvent $ event ) {
$ event -> getName (); // Name of the Event
$ event -> getMessage ();; // Command or Query that has been received
});
MessageHandledEvent - 成功處理訊息後引發。
use Sco MessageBus Event Subscriber ;
use Sco MessageBus Event MessageHandledEvent ;
$ subscriber = new Subscriber ();
$ subscriber -> addListener (MessageHandledEvent::class, function ( MessageHandledEvent $ event ) {
$ event -> getName (); // Name of the Event
$ event -> getMessage (); // Command or Query being handled
$ event -> getResult (); // Result for the handled message
});
MessageFailedEvent - 當訊息處理失敗並引發異常時引發。
use Sco MessageBus Event Subscriber ;
use Sco MessageBus Event MessageFailedEvent ;
$ subscriber = new Subscriber ();
$ subscriber -> addListener (MessageFailedEvent::class, function ( MessageFailedEvent $ event ) {
$ event -> getName (); // Name of the Event
$ event -> getMessage (); // Command or Query being handled
$ event -> getError (); // Captured Exception
});
事務中間件接受三個函數參數,每個參數對應事務的每個階段:開始、提交和回滾。採用此方法可讓您使用您喜歡的任何 ORM,甚至使用本機 PDO 物件與持久層進行互動。
$ pdo = new PDO ( ' {connection_dsn} ' )
$ transaction = new Sco MessageBus Middleware TransactionMiddleware (
fn (): bool => $ pdo -> beginTransaction (),
fn (): bool => $ pdo -> commit (),
fn ( Throwable $ error ): bool => $ pdo -> rollBack (),
);
程式庫將 Handler 傳回值包裝到Result 值物件中以提供一致的 API,以便您可以依賴始終具有相同類型的傳回值。
所有 Result 值物件都擴展了ScoMessageBusResult
抽象類,並且可以分為 3 組:
ScoMessageBusResultBoolean
ScoMessageBusResultInteger
ScoMessageBusResultNumeric
ScoMessageBusResultText
ScoMessageBusResultNone
(包含空值)ScoMessageBusResultDelegated
包裝物件並將對屬性和方法的呼叫委託給底層對象ScoMessageBusResultCollection
和ScoMessageBusResultMap
包裝數字索引數組(列表)和字串索引數組(映射)並實現Countable
、 ArrayAccess
和IteratorAggregate
接口您也可以透過擴充抽象類別ScoMessageBusResult
並在適當的處理程序中傳回它們來新增自己的自訂 Result 值物件。
庫遵循 PSR-12 標準。