يُعرف بالعامية باسم نمط command bus ، لكن المكتبة تميز بين الأوامر والاستعلامات وتسمح لك بفرض عدم وجود قيم إرجاع في معالجات الأوامر لإبقائك متوافقًا مع نمط CQRS.
هذه مكتبة قائمة بذاتها ، والتبعيتان الوحيدتان هما واجهات حاوية PSR-11 وواجهات سجل PSR-3 للسماح بإمكانية التشغيل البيني بشكل أفضل.
جدول المحتويات:
تثبيت المكتبة باستخدام الملحن:
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 ))
يمكننا استخدام طريقتين هنا، تزيين فئة الناقل التي توفرها المكتبة، أو إدخال محدد موقع الخدمة. لمزيد من المعلومات يمكنك قراءة مستندات Symfony
يمكننا إنشاء فئة Decorator جديدة والتي ستقوم بتنفيذ واجهة 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 وتوفير الحاوية كوسيطة لفئة الناقل بالمكتبة:
$ 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 (),
);
تقوم المكتبة بتغليف قيم إرجاع المعالج في كائنات قيمة النتيجة لتوفير واجهة برمجة تطبيقات متسقة، بحيث يمكنك الاعتماد على أن تكون قيم الإرجاع دائمًا من نفس النوع.
جميع كائنات قيمة النتيجة تمتد إلى الفئة المجردة ScoMessageBusResult
ويمكن تقسيمها إلى ثلاث مجموعات:
ScoMessageBusResultBoolean
ScoMessageBusResultInteger
ScoMessageBusResultNumeric
ScoMessageBusResultText
ScoMessageBusResultNone
(يلتف حول القيم الخالية)ScoMessageBusResultDelegated
الذي يلتف الكائنات ويفوض استدعاءات الخصائص والأساليب للكائن الأساسيScoMessageBusResultCollection
و ScoMessageBusResultMap
التي تغلف المصفوفات المفهرسة (القوائم) والمصفوفات المفهرسة بالسلسلة (الخرائط) وتنفذ واجهات Countable
و ArrayAccess
و IteratorAggregate
يمكنك أيضًا إضافة كائنات قيمة النتيجة المخصصة الخاصة بك عن طريق توسيع الفئة المجردة ScoMessageBusResult
وإعادتها إلى المعالج المناسب.
تتبع المكتبة معيار PSR-12.