구어적으로는 command bus 패턴으로 더 잘 알려져 있지만 라이브러리는 명령 과 쿼리를 구별하고 명령 처리기에서 반환 값을 강제로 적용하여 CQRS 패턴과 일치하도록 할 수 있습니다.
이는 독립 실행형 라이브러리 이며 더 나은 상호 운용성을 허용하는 PSR-11 컨테이너 및 PSR-3 로그 인터페이스라는 두 가지 종속성만 있습니다.
목차:
작곡가를 사용하여 라이브러리를 설치하십시오.
composer require sco/message-bus
PSR-4 자동 로딩 표준을 따르고 PsrContainerContainerInterface
구현하는 문제이며 라이브러리가 테스트 스위트 ScoMessageBusTestsStubContainerInMemoryContainer
에 사용하는 것만큼 간단할 수 있는 자체 서비스 컨테이너 클래스를 생성해야 합니다. ScoMessageBusTestsStubContainerInMemoryContainer
또는 작곡가에게 PHP-DI와 같은 PSR-11 표준을 준수하는 서비스 컨테이너 라이브러리를 요구할 수 있습니다.
require ' vendor/autoload.php '
$ container = new InMemoryContainer( $ services )
$ bus = new Sco MessageBus Bus ( $ container );
$ bus -> dispatch ( new FindPostByIdQuery ( 1 ))
여기서는 라이브러리에서 제공하는 버스 클래스를 장식하거나 서비스 로케이터를 삽입하는 두 가지 접근 방식을 사용할 수 있습니다. 더 많은 정보를 원하시면 Symfony Docs를 읽어보세요.
Symfony의 SymfonyContractsServiceServiceSubscriberInterface
인터페이스를 구현할 새로운 Decorator 클래스를 생성할 수 있습니다.
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 );
});
각 명령 또는 쿼리 와 해당 결과 개체 콤보에는 고유한 ID(예: 명령) 가 할당되고 해당 결과 개체에는 00000001
의 ID가 지정됩니다. 이는 로깅, 감사 또는 디버깅 목적에 유용할 수 있습니다.
기본 ID 생성 전략은 외부 종속성을 최소로 유지하는 간단한 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 (),
);
라이브러리는 일관된 API를 제공하기 위해 핸들러 반환 값을 결과 값 객체 로 래핑하므로 반환 값이 항상 동일한 유형일 수 있습니다.
모든 결과 값 개체는 ScoMessageBusResult
추상 클래스를 확장하고 3개 그룹으로 나눌 수 있습니다.
ScoMessageBusResultBoolean
ScoMessageBusResultInteger
ScoMessageBusResultNumeric
ScoMessageBusResultText
ScoMessageBusResultNone
(널 값 래핑)ScoMessageBusResultDelegated
Countable
, ArrayAccess
및 IteratorAggregate
인터페이스를 구현하는 ScoMessageBusResultCollection
및 ScoMessageBusResultMap
또한 추상 클래스 ScoMessageBusResult
확장하고 이를 적절한 핸들러에 반환하여 사용자 정의 결과 값 개체를 추가할 수도 있습니다.
라이브러리는 PSR-12 표준을 따릅니다.