รู้จักกันในชื่อเรียกขานว่ารูป command bus แต่ไลบรารีสร้างความแตกต่างระหว่าง คำสั่ง และ แบบสอบถาม และอนุญาตให้คุณบังคับใช้ค่าที่ไม่ส่งคืนใน Command Handlers เพื่อให้คุณสอดคล้องกับรูปแบบ CQRS
นี่เป็น ไลบรารีแบบสแตนด์อโลน โดยมีเพียงสองส่วนเท่านั้นที่เป็นอินเทอร์เฟซ PSR-11 Container และ PSR-3 Log เพื่อให้สามารถทำงานร่วมกันได้ดียิ่งขึ้น
สารบัญ:
ติดตั้งไลบรารีโดยใช้ผู้แต่ง:
composer require sco/message-bus
คุณจะต้องปฏิบัติตามมาตรฐานการโหลดอัตโนมัติ PSR-4 และสร้างคลาส Service Container ของคุณเอง ซึ่งเป็นเรื่องของการนำ PsrContainerContainerInterface
ไปใช้ และสามารถทำได้ง่ายเหมือนกับที่ไลบรารีใช้สำหรับชุดทดสอบ ScoMessageBusTestsStubContainerInMemoryContainer
หรือคุณสามารถผู้แต่งต้องการไลบรารี Service Container ซึ่งเป็นไปตามมาตรฐาน PSR-11 เช่น PHP-DI
require ' vendor/autoload.php '
$ container = new InMemoryContainer( $ services )
$ bus = new Sco MessageBus Bus ( $ container );
$ bus -> dispatch ( new FindPostByIdQuery ( 1 ))
เราสามารถใช้สองแนวทางที่นี่ ตกแต่งคลาส Bus ที่ห้องสมุดจัดเตรียมไว้ หรือฉีด Service Locator สำหรับข้อมูลเพิ่มเติม คุณสามารถอ่าน Symfony Docs
เราสามารถสร้างคลาสมัณฑนากรใหม่ซึ่งจะใช้อินเทอร์เฟซ SymfonyContractsServiceServiceSubscriberInterface
ของ Symfony:
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 จะไม่เป็นแบบสาธารณะตามค่าเริ่มต้น และไม่ควรเป็นอย่างนั้นจริงๆ ดังนั้น เว้นแต่คุณจะเพิ่มตัวจัดการของคุณลงในอาร์เรย์นี้เมื่อผู้ทำแผนที่ ทำการแมปเสร็จแล้ว จะไม่สามารถค้นหาตัวจัดการได้ และเซอร์วิสที่ไม่พบข้อยกเว้นคอนเทนเนอร์จะถูกส่งออกไป
วิธีการที่แตกต่างออกไปคือการใส่ Service Locator พร้อมกับตัวจัดการทั้งหมดเข้าไปใน Bus ของห้องสมุด ซึ่งจะทำได้ในไฟล์ 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 เพื่อเพิ่มตัวจัดการให้กับ Bus โดยอัตโนมัติ:
ใช้ !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 สิ่งที่คุณต้องทำคือลงทะเบียน Bus ใน Service Container ของ 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)]
ให้กับคลาส Command/Query ของคุณ ชื่อพารามิเตอร์ handler
สามารถละเว้นได้ ขึ้นอยู่กับความชอบส่วนตัวของคุณScoMessageBusMapper
แต่ละคำสั่งจะถูกส่งผ่านสายโซ่ของมิดเดิลแวร์ ตามค่าเริ่มต้น chain จะว่างเปล่า แต่ไลบรารีจะเสนอ Middleware บางส่วนให้ทันที:
begin
, commit
และ rollback
เป็นออบเจ็กต์ Closure
ธรรมดา ดังนั้นคุณสามารถใช้วิธี ORM หรือ Persistence ใดก็ได้ที่คุณต้องการ หากต้องการสร้างมิดเดิลแวร์ที่คุณกำหนดเอง คุณจะต้องใช้อินเทอร์เฟซ 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
});
Transaction Middleware ยอมรับอาร์กิวเมนต์ของฟังก์ชันสามข้อ แต่ละข้อสำหรับทุกขั้นตอนของธุรกรรม: เริ่มต้น ยอมรับ และย้อนกลับ การใช้แนวทางนี้ทำให้คุณสามารถใช้ 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
(ตัดค่า null)ScoMessageBusResultDelegated
ซึ่งตัดอ็อบเจ็กต์และมอบหมายการเรียกคุณสมบัติและวิธีการไปยังอ็อบเจ็กต์ต้นแบบScoMessageBusResultCollection
และ ScoMessageBusResultMap
ซึ่งตัดหมายเลขอาร์เรย์ที่จัดทำดัชนี (รายการ) และอาร์เรย์ที่จัดทำดัชนีสตริง (แผนที่) และใช้ Countable
, ArrayAccess
และ IteratorAggregate
อินเทอร์เฟซ คุณยังสามารถเพิ่มออบเจ็กต์ค่าผลลัพธ์ที่คุณกำหนดเองได้ด้วยการขยายคลาสนามธรรม ScoMessageBusResult
และส่งคืนมาในตัวจัดการที่เหมาะสม
ห้องสมุดเป็นไปตามมาตรฐาน PSR-12