diff --git a/README.md b/README.md index a28378d..2c6a76e 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ PSB - ProophServiceBus ====================== -PHP 5.5+ Lightweight Service Bus Facade supporting CQRS and Microservices +PHP 5.5+ Lightweight Service Bus Facade supporting CQRS and Micro Services [![Build Status](https://travis-ci.org/prooph/service-bus.png?branch=master)](https://travis-ci.org/prooph/service-bus) -Message API ------------ +Messaging API +------------- -prooph/service-bus is a lightweight messaging facade sitting in front of your model. The idea is that the API of your model -is defined through messages. +prooph/service-bus is a lightweight messaging facade. +It allows you to define the API of your model with the help of messages. 1. Command messages describe the actions your model can handle. 2. Event messages describe things that happened while your model handled a command. @@ -18,9 +18,9 @@ is defined through messages. prooph/service-bus shields your model. Data input and output ports become irrelevant and no longer influence the business logic. I'm looking at you Hexagonal Architecture. -prooph/service-bus decouples your model from any framework except prooph/service-bus of course :-). You can use a +prooph/service-bus decouples your model from any framework. You can use a web framework like Zend, Symfony, Laravel and co. to handle http requests and pass them via prooph/service-bus to your model -but you can also receive the same messages via CLI or from a messaging infrastructure like RabbitMQ or Beanstalkd. +but you can also receive the same messages via CLI or from a message queue system like RabbitMQ or Beanstalkd. ![psb_architecture](docs/img/psb_architecture.png) @@ -28,20 +28,17 @@ but you can also receive the same messages via CLI or from a messaging infrastru Installation ------------ -You can install prooph/service-bus via composer by adding `"prooph/service-bus": "~3.0"` as requirement to your composer.json. +You can install prooph/service-bus via composer by adding `"prooph/service-bus": "~4.0"` as requirement to your composer.json. Quick Start ----------- -The simplest way to get started is to set up one of the message buses provided by prooph/service-bus. - ```php route('Prooph\ServiceBus\Example\Command\EchoText') //Expand command bus with the router plugin $commandBus->utilize($router); -//Expand command bus with the callback invoke strategy -$commandBus->utilize(new CallbackStrategy()); - //We create a new Command -$echoText = EchoText::fromString('It works'); +$echoText = new EchoText('It works'); //... and dispatch it $commandBus->dispatch($echoText); @@ -72,16 +66,14 @@ Documentation ------------- - [Overview](docs/service_bus_system.md) -- [CommandBus](docs/command_bus.md) -- [EventBus](docs/event_bus.md) -- [QueryBus](docs/query_bus.md) +- [Message Bus API](docs/message_bus.md) - [Plugins](docs/plugins.md) -- [Asynchronous MessageDispatcher](docs/message_dispatcher.md) +- [Message Queue Producers](docs/queue_producer.md) # ZF2 Integration [prooph/proophessor](https://github.com/prooph/proophessor) seamlessly integrates prooph/service-bus with a ZF2 application. - +Note: Currently proophessor only supports prooph/service-bus 3.x. Support for 4.x is coming soon. Support ------- diff --git a/composer.json b/composer.json index 9815ebd..e8025e2 100644 --- a/composer.json +++ b/composer.json @@ -12,12 +12,9 @@ } ], "keywords": [ - "php", - "ESB", - "Service Bus", + "prooph", "Messaging", "CQRS", - "library", "DDD", "domain-driven design", "ZF2" @@ -25,26 +22,26 @@ "require": { "php": ">=5.5", "beberlei/assert": "~2.0", - "prooph/common" : "~2.2", - "react/promise" : "~2.2" + "prooph/common" : "~3.1", + "react/promise" : "~2.2", + "container-interop/container-interop" : "~1.1" }, "require-dev": { - "phpunit/phpunit": "3.7.*", + "phpunit/phpunit": "~4.7", "satooshi/php-coveralls": "dev-master" }, "suggest": { "prooph/event-store": "Use ProophEventStore and let the EventBus dispatch persisted DomainEvents", - "zendframework/zend-servicemanager": "Use Zf2 ServiceLocator to lazy load your handlers and listeners" + "zendframework/zend-servicemanager": "Use Zf2 ServiceManager to lazy load your handlers and listeners" }, "autoload": { - "psr-0": { + "psr-4": { "Prooph\\ServiceBus\\": "src/" } }, "autoload-dev": { - "psr-0": { - "Prooph\\ServiceBusTest\\": "tests/", - "Prooph\\EventSourcingTest\\": "vendor/prooph/event-sourcing/tests" + "psr-4": { + "Prooph\\ServiceBusTest\\": "tests/" } } } diff --git a/docs/command_bus.md b/docs/command_bus.md deleted file mode 100644 index 9d78442..0000000 --- a/docs/command_bus.md +++ /dev/null @@ -1,123 +0,0 @@ -The CommandBus -============== - -[Back to documentation](../README.md#documentation) - -# Usage - -When you want to apply [CQRS](http://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf) the command bus is your best friend. -It takes an incoming command message and route it the responsible command handler. -The advantage of using a CommandBus instead of calling command handlers directly is, that you can change your model without effecting -the application logic. You can work with command versions to dispatch a newer version to a new command handler and older -versions to old command handlers. Your model can support different versions at the same time which makes migrations a lot easier. - -# API - -```php -class CommandBus extends MessageBus -{ - /** - * @param \Prooph\Common\Event\ActionEventListenerAggregate|\Psr\Log\LoggerInterface $plugin - * @return $this - * @throws Exception\RuntimeException - */ - public function utilize($plugin); - - /** - * @param \Prooph\Common\Event\ActionEventListenerAggregate|\Psr\Log\LoggerInterface $plugin - * @return $this - * @throws Exception\RuntimeException - */ - public function deactivate($plugin); - - /** - * @param string $eventName - * @param callable $listener - * @param int $priority - * @return \Zend\Stdlib\CallbackHandler - */ - public function on($eventName, $listener, $priority = 1); - - /** - * @param \Zend\Stdlib\CallbackHandler $callbackHandler - * @return bool - */ - public function off(CallbackHandler $callbackHandler); - - /** - * @param mixed $command - * @throws Exception\CommandDispatchException - */ - public function dispatch($command); -} -``` - -The public API of the CommandBus is very simple. Four of the five methods deal with adding or removing plugins and the last -one triggers the dispatch process of the given command. - -** Note: Only `dispatch` is implemented by the CommandBus the four other public methods are provided by the basic MessageBus implementation. - -# Event-Driven Dispatch - -The command dispatch is an event-driven process provided by a Prooph\Common\Event\ActionEventDispatcher. -When a command is passed to the CommandBus via `CommandBus::dispatch` a new [CommandDispatch](../src/Prooph/ServiceBus/Process/CommandDispatch.php) process is created by the CommandBus and populated with the given command. -Then the CommandBus triggers a chain of action events. Plugins can listen on these action events. They always get the CommandDispatch as the only argument and they can -modify it to help the CommandBus finish the process. A CommandBus without any registered plugins is useless and will throw an exception because -it does not know which command handler is responsible for the command. -Following action events are triggered in the listed order: - -- `initialize`: This action event is triggered right after CommandBus::dispatch($command) is invoked. At this time the CommandDispatch only contains the command. - -- `detect-message-name` (optional): Before a command handler can be located, the CommandBus needs to know how the command is named. Their are two -possibilities to provide the information. The command can implement the [Prooph\Common\Messaging\HasMessageName](https://github.com/prooph/common/blob/master/src/Messaging/HasMessageName.php) interface. -In this case the CommandBus picks the command name directly from the command and inject it manually in the CommandDispatch. The `detect-message-name` event is not triggered. If the command -does not implement the interface the `detect-message-name` event is triggered and a plugin needs to inject the name using `CommandDispatch::setCommandName`. - -- `route`: During the `route` action event a plugin should provide the responsible command handler either in form of a ready to use object or callable or as a string -representing an alias of the command handler that can be used by a DIC to locate an instance. The plugin should provide the handler by using -`CommandDispatch::setCommandHandler`. - -- `locate-handler` (optional): After routing the command, the CommandBus checks if the command handler was provided as a string. If so it triggers the -`locate-handler` event. This is the latest time to provide an object or callable as command handler. If no plugin was able to provide one the CommandBus throws an exception. - -- `invoke-handler`: Having the command handler in place it's time to invoke it with the command. The CommandBus always triggers the event. It performs no default action even if the -command handler is a callable. - -- `handle-error`: If at any time a plugin or the CommandBus itself throws an exception it is caught and passed to the CommandDispatch. The normal action event chain breaks and a -`handle-error` event is triggered instead. Plugins can access the exception by calling `CommandDispatch::getException`. -A `handle-error` plugin or a `finalize` plugin can unset the exception by calling `CommandDispatch::setException(null)`. -When all plugins are informed about the error and no one has unset the exception the CommandBus throws a Prooph\ServiceBus\Exception\CommandDispatchException to inform the outside world about the error. - -- `finalize`: This action event is always triggered at the end of the process no matter if the process was successful or an exception was thrown. It is the ideal place to -attach a monitoring plugin. - -# Commands - -A command can nearly be everything. PSB tries to get out of your way as much as it can. You are ask to use your own command implementation or you use the -default [Command](https://github.com/prooph/common/blob/master/src/Messaging/Command.php) class provided by prooph/common. It is a very good base class -and PSB ships with translator plugins to translate a Command into a remote message -that can be send to a remote interface. Check the [Remote Message Dispatcher](message_dispatcher.md) for more details. - -# Plugins - -Plugins can be simple callables (use the methods `on` and `off` to attach/detach them), implementations of the -\Prooph\Common\Event\ActionEventListenerAggregate (use the methods `utilize` and `deactivate` to attach/detach them) or an instance of -Psr\Log\LoggerInterface (also use methods `utilize` and `deactivate` to attach/detach it). -The signature of a plugin method/callable that listens on a CommandDispatch event is: - -```php -function (\Prooph\ServiceBus\Process\CommandDispatch $commandDispatch) {}; -``` - -Check the list of available [plugins](plugins.md) shipped with ProophServiceBus. If they don't meet your needs don't hesitate to write your -own plugins. It is really straight forward. - -# Logging - -If you add a Psr\Log\LoggerInterface as a plugin it is passed to the CommandDispatch and available during the dispatch so the -listener plugins can log their activities. - - - - - diff --git a/docs/event_bus.md b/docs/event_bus.md deleted file mode 100644 index 73eb090..0000000 --- a/docs/event_bus.md +++ /dev/null @@ -1,128 +0,0 @@ -The EventBus -============ - -[Back to documentation](../README.md#documentation) - -# Usage - -When you want to apply [CQRS](http://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf) you need a way to inform the outside world -about events that happened in your write model be it your read model projectors or other systems that rely on the information. -An EventBus is responsible for dispatching event messages to all interested listeners. If a listener is part of another system -the event may need to be send to a remote interface. The Prooph\ServiceBus\EventBus is capable to handle synchronous event -dispatching as well as asynchronous/remote event dispatching by using suitable plugins. - -# API - -```php -class EventBus extends MessageBus -{ - /** - * @param \Prooph\Common\Event\ActionEventListenerAggregate|\Psr\Log\LoggerInterface $plugin - * @return $this - * @throws Exception\RuntimeException - */ - public function utilize($plugin); - - /** - * @param \Prooph\Common\Event\ActionEventListenerAggregate|\Psr\Log\LoggerInterface $plugin - * @return $this - * @throws Exception\RuntimeException - */ - public function deactivate($plugin); - - /** - * @param string $eventName - * @param callable $listener - * @param int $priority - * @return \Zend\Stdlib\CallbackHandler - */ - public function on($eventName, $listener, $priority = 1); - - /** - * @param \Zend\Stdlib\CallbackHandler $callbackHandler - * @return bool - */ - public function off(CallbackHandler $callbackHandler); - - /** - * @param mixed $event - * @throws Exception\EventDispatchException - */ - public function dispatch($event); -} -``` - -The public api of the EventBus is very simple. Four of the five methods deal with adding or removing plugins and the last -one triggers the dispatch process of the given event. - -** Note: Only `dispatch` is implemented by the EventBus the four other public methods are provided by the basic MessageBus implementation. - -** Note: For the event-driven dispatch process the term `event` is used, too. For example the first argument of the -method `EventBus::on` is called "eventName" or plugins should implement the \Prooph\Common\Event\ActionEventListenerAggregate. But these -namings have nothing to do with the event messages dispatched by the EventBus. The same wording is used to describe something that happens -now (triggering an action event) and something that happened in the past (the event message). - -# Event-Driven Dispatch - -The event dispatch is an event-driven process provided by a Prooph\Common\Event\ActionEventDispatcher. -When an event message is passed to the EventBus via `EventBus::dispatch` a new [EventDispatch](../src/Prooph/ServiceBus/Process/EventDispatch.php) process is created by the EventBus and populated with the given event message. -Then the EventBus triggers a chain of action events. Plugins can listen on the action events. They always get the EventDispatch as the only argument and they can -modify it to help the EventBus finish the process. An EventBus without any registered plugins is useless and will throw an exception because -it does not know which event message listener is interested in the event message. -Following action events are triggered in the listed order: - -- `initialize`: This action event is triggered right after EventBus::dispatch($event) is invoked. At this time the EventDispatch only contains the event message. - -- `detect-message-name` (optional): Before an event message listener can be located, the EventBus needs to know how the event message is named. Their are two -possibilities to provide the information. The event message can implement the [Prooph\Common\Messaging\HasMessageName](https://github.com/prooph/common/blob/master/src/Messaging/HasMessageName.php) interface. -In this case the EventBus picks the message name directly from the event message and inject it manually in the EventDispatch. The `detect-message-name` action event is not triggered. If the event message -does not implement the interface the `detect-message-name` action event is triggered and a plugin needs to inject the name using `EventDispatch::setEventName`. - -- `route`: During the `route` action event one or more plugins should provide a list of interested event message listeners either in form of ready to use objects or callables or as strings -representing aliases of the event message listeners that can be used by a DIC to locate the listener instances. The plugins should provide and modify the list by using -`EventDispatch::setEventListeners` and `EventDispatch::addEventListener`. - -- `locate-listener` (optional): After routing the event message, the EventBus loops over the list of interested event message listeners and checks for each of them -if the event message listener was provided as a string. If so it triggers a -`locate-listener` process event. This is the latest time to provide an object or callable as event message listener. The listener alias can be requested from the EventDispatch by -calling the method `EventDispatch::getCurrentEventListener` and the event message listener instance can be set via method `EventDispatch::setCurrentEventListener` If no plugin was able to provide an instance the EventBus throws an exception. - -- `invoke-listener`: Foreach event message listener the EventBus triggers the `invoke-listener` action event. The EventBus always triggers the action event. It performs no default even if the -event message listener is a callable. Plugins can access the currently active event message listener by requesting it from the `EventDispatch::getCurrentEventListener` method. - -- `handle-error`: If at any time a plugin or the EventBus itself throws an exception it is caught and passed to the EventDispatch. The normal action event chain breaks and a -`handle-error` action event is triggered instead. Plugins can access the exception by calling `EventDispatch::getException`. -A `handle-error` plugin or a `finalize` plugin can unset the exception by calling `EventDispatch::setException(null)`. -When all plugins are informed about the error and no one has unset the exception -the EventBus throws a Prooph\ServiceBus\Exception\EventDispatchException to inform the outside world about the error. - -- `finalize`: This action event is always triggered at the end of the process no matter if the process was successful or an exception was thrown. It is the ideal place to -attach a monitoring plugin. - -# Event Messages - -An event message can nearly be everything. PSB tries to get out of your way as much as it can. You are ask to use your own event message implementation or you use the -default [DomainEvent](https://github.com/prooph/common/blob/master/src/Messaging/DomainEvent.php) class provided by prooph/common. It is a very good base class -and PSB ships with translator plugins to translate an event message into a remote message -that can be send to a remote interface. Check the [Remote Message Dispatcher](message_dispatcher.md) for more details. - -# Plugins - -Plugins can be simple callables (use the methods `on` and `off` to attach/detach them), implementations of the -\Prooph\Common\Event\ActionEventListenerAggregate (use the methods `utilize` and `deactivate` to attach/detach them) or an instance of -Psr\Log\LoggerInterface (also use methods `utilize` and `deactivate` to attach/detach it). -The signature of a plugin method/callable that listens on an EventDispatch is: - -```php -function (\Prooph\ServiceBus\Process\EventDispatch $eventDispatch) {}; -``` - -Check the list of available [plugins](plugins.md) shipped with ProophServiceBus. If they don't meet your needs don't hesitate to write your -own plugins. It is really straight forward. - -# Logging - -If you add a Psr\Log\LoggerInterface as a plugin, it is passed to the EventDispatch and available during the dispatch so the -plugins can log their activities. - - diff --git a/docs/message_bus.md b/docs/message_bus.md new file mode 100644 index 0000000..18bef16 --- /dev/null +++ b/docs/message_bus.md @@ -0,0 +1,125 @@ +# Message Buses + +[Back to documentation](../README.md#documentation) + +## Commanding + +When you want to apply [CQRS](http://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf) the command bus is your best friend. +It takes an incoming command message and route it to the responsible command handler. +The advantage of using a CommandBus instead of calling command handlers directly is, that you can change your model without effecting +the application logic. You can work with command versions to dispatch a newer version to a new command handler and older +versions to old command handlers. Your model can support different versions at the same time which makes migrations a lot easier. +Second feature of a command bus could be automatic transaction handling. +And for distributed systems it is also interesting to push the command on a queue and handle it asynchronous. + +## Eventing + +When dividing your domain logic into modules or bounded contexts you need a way to inform the outside world +about events that happened in your model. +An EventBus is responsible for dispatching event messages to all interested listeners. If a listener is part of another system +the event may need to be send to a remote interface. The Prooph\ServiceBus\EventBus is capable to handle synchronous event +dispatching as well as asynchronous/remote event dispatching by using suitable plugins. + +## Querying + +A system based on [Microservices](http://martinfowler.com/articles/microservices.html) requires a lightweight communication channel. +The two most used protocols are HTTP request-response with resource API's and lightweight messaging. The latter is supported by prooph/service-bus +out-of-the-box but HTTP API's can be integrated too. +The QueryBus is responsible for routing a query message to a so called finder. The query indicates that the producer expects a response. +The finder's responsibility is to fetch data from a data source using the query parameters defined in the query message. It is up to the finder if the data is fetched synchronous +or asynchronous, so the QueryBus returns a [promise](https://github.com/reactphp/promise) to the query producer which gets resolved by the finder. + +## API + +All three bus types extend the same base class [Prooph\ServiceBus\MessageBus](../MessageBus.php) and therefor make use of an event-driven message dispatch process. +Take a look at the CommandBus API. It is the same for EventBus and QueryBus except that the QueryBus returns a promise from `QueryBus::dispatch`. + +```php +class CommandBus extends MessageBus +{ + /** + * @param \Prooph\Common\Event\ActionEventListenerAggregate $plugin + */ + public function utilize($plugin); + + /** + * @param \Prooph\Common\Event\ActionEventListenerAggregate $plugin + */ + public function deactivate($plugin); + + /** + * @return \Prooph\Common\Event\ActionEventEmitter + */ + public function getActionEventEmitter(); + + /** + * @param mixed $command + * @throws Exception\ServiceBusException + */ + public function dispatch($command); +} +``` + +The public API of a message bus is very simple. You can attach and detach plugins which are simple event listener aggregates +and you can dispatch a message. + +## Event-Driven Dispatch + +Internally a prooph message bus uses an [event-driven process](https://github.com/prooph/common#actioneventemitter) to dispatch messages. +This offers a lot of flexibility without the need to define interfaces for messages. +A message can be everything even a string. prooph/service-bus doesn't care. But using some defaults will reduce the +number of required plugins and increase performance. + +But first let's take a look at the internals of a message dispatch process and the differences between the bus types. + +### initialize + +This action event is triggered right after MessageBus::dispatch($message) is invoked. At this time the action event only contains the `message`. + +### detect-message-name (optional) + +Before a message handler can be located, the message bus needs to know how the message is named. Their are two +possibilities to provide the information. The message can implement the [Prooph\Common\Messaging\HasMessageName](https://github.com/prooph/common/blob/master/src/Messaging/HasMessageName.php) interface. +In this case the message bus picks the name directly from the message and set it as param `message-name` in the action event for later use. The `detect-message-name` event is not triggered. If the message +does not implement the interface the `detect-message-name` event is triggered and a plugin needs to inject the name using `ActionEvent::setParam('message-name', $messageName)`. +Finally if no `message-name` was set by a listener the message bus uses the FQCN of message if it is an object or the type of message in all other cases. + +### route + +During the `route` action event a plugin (typically a [router](plugins.md#routers)) should provide the responsible message handler either in form of a ready to use `callable`, a object or just a string. +The latter should be a service id that can be passed to a service locator to get an instance of the handler. +The message handler should be set as action event param `message-handler` (for CommandBus and QueryBus) or `event-listeners` (for EventBus). + +As you can see command and query bus work with a single message handler whereby the event bus works with multiple listeners. +This is one of the most important differences. Only the event bus allows multiple message handlers per message and therefor uses +a slightly different dispatch process. + +### locate-handler (optional) + +After routing the message, the message bus checks if the handler was provided as a string. If so it triggers the +`locate-handler` action event. This is the latest time to provide an object or callable as message handler. If no plugin was able to provide one the message bus throws an exception. + +### invoke-handler / invoke-finder (optional) + +Having the message handler in place it's time to invoke it with the message. If the `message-handler` is a `callable` the `invoke-handler` action event is not triggered but instead +the handler is invoked by the message bus (true for all three bus types). +At this stage all three bus types behave a bit different. + +- CommandBus: invokes the handler with the command message, or triggers the invoke-handler action event if the handler is not a callable. +- QueryBus: much the same as the command bus but the message handler is invoked with the query message and a [deferred](https://github.com/reactphp/promise/blob/master/src/Deferred.php) +that needs to be resolved by the message handler aka finder. If the finder is not a callable the query bus triggers a `invoke-finder` action event to indicate +that a finder should be invoked and not a normal message handler. +- EventBus: loops over all `event-listeners` and triggers the `locate-handler` and `invoke-handler` action events for each message listener. + +### handle-error + +If at any time a plugin or the message bus itself throws an exception it is caught and passed as param `exception` to the action event. The normal action event chain breaks and a +`handle-error` event is triggered instead. Plugins can then access the exception by getting it from the action event. +A `handle-error` plugin or a `finalize` plugin can unset the exception by calling `ActionEvent::setParam("exception", null)`. +When all plugins are informed about the error and no one has unset the exception the message bus throws a Prooph\ServiceBus\Exception\MessageDispatchException to inform the outside world about the error. + +### finalize + +This action event is always triggered at the end of the process no matter if the process was successful or an exception was thrown. It is the ideal place to +attach a monitoring plugin. + diff --git a/docs/message_dispatcher.md b/docs/message_dispatcher.md deleted file mode 100644 index 3ca4ea9..0000000 --- a/docs/message_dispatcher.md +++ /dev/null @@ -1,101 +0,0 @@ -RemoteMessageDispatcher -============================== - -[Back to documentation](../README.md#documentation) - -# Usage - -Messaging becomes really interesting when you process your messages asynchronous. For example push your messages on a queue, -set up a cron job to periodically check the queue for new messages and process them. The bus implementations of PSB can -hide such an asynchronous workflow behind a unified interface. You can start with synchronous message dispatching by -routing your messages directly to message handlers and if you later want to improve response times you can switch to -async processing on message basis by routing the appropriate messages to a [RemoteMessageDispatcher](../src/Prooph/ServiceBus/Message/RemoteMessageDispatcher.php). -Queries require a special remote message dispatcher implementation namely a [RemoteQueryDispatcher](../src/Prooph/ServiceBus/Message/RemoteQueryDispatcher.php). - -## Synchronous Dispatch -```php -//This example shows the simplified set up of a synchronous dispatch -$router = new EventRouter(); - -//We route the event directly to a listener -$router->route('SomethingDone')->to(new SomethingDoneListener()); - -$eventBus->utilize($router); - -$eventBus->utilize(new OnInvokeStrategy()); - -$eventBus->dispatch(new SomethingDone()); -``` - -Normally the ready to use EventBus is injected in a controller or service which then only uses the `EventBus::dispatch` -method without knowing something about the router set up. If you later change the routing like it is shown in the example below, -your controller or service logic can continue to work without any adaptions. - -## Asynchronous Dispatch -```php -//This example shows the simplified set up of an asynchronous dispatch -//We dispatch an event in the example but the same technique can be used to -//dispatch a command asynchronously -$router = new EventRouter(); - -//We route the event to an async message dispatcher -//which implements Prooph\ServiceBus\Message\RemoteMessageDispatcher -$router->route('SomethingDone')->to(new My\Async\MessageDispatcher()); - -$eventBus->utilize($router); - -//The domain event needs to be translated to a Prooph\Common\Messaging\RemoteMessage -//The ForwardToRemoteMessageDispatcherStrategy checks if the listener of the event -//is an instance of Prooph\ServiceBus\Message\RemoteMessageDispatcher -//and translates the event with the help of a Prooph\ServiceBus\Message\ToRemoteMessageTranslator -$eventBus->utilize(new ForwardToRemoteMessageDispatcherStrategy(new ProophDomainMessageToRemoteMessageTranslator())); - - -//Now the event is dispatched to a RemoteMessageDispatcher instead of a listener -$eventBus->dispatch(new SomethingDone()); - -//Behind the scenes the remote message is translated to an array and pushed on a message queue -//There are various messaging tools available. We try to support the most important ones and -//continue to add more. Check the list below for available adapters. If your favorite adapter is -//not on the list you can easily implement it -//by implementing the Prooph\ServiceBus\Message\RemoteMessageDispatcher interface -// -//Now imagine that a worker has pulled the message array from the queue and want to process it -//It can simply create a remote message object from the array ... -$message = \Prooph\Common\Messaging\RemoteMessage::fromArray($messageArr); - -//... set up another EventBus with a Prooph\ServiceBus\Message\FromRemoteMessageTranslator -//to translate the incoming message back to a domain event ... -$eventBus = new EventBus(); - -$eventBus->utilize(new FromRemoteMessageTranslator()); - -$router = new EventRouter(); - -//This time the event is dispatched to the interested listener -$router->route('SomethingDone')->to(new SomethingDoneListener()); - -$eventBus->utilize($router); - -$eventBus->utilize(new OnInvokeStrategy()); - -$eventBus->dispatch($message); -``` - -# Available RemoteMessageDispatchers - -- [InMemoryRemoteMessageDispatcher](../src/Prooph/ServiceBus/Message/InMemoryRemoteMessageDispatcher.php): useful for tests, - you can replace your async dispatcher with this one -- [PhpResqueMessageDispatcher](https://github.com/prooph/psb-php-resque-dispatcher): Perfect choice for async - command processing using a ultra fast redis queue -- [BernardMessageDispatcher](https://github.com/prooph/psb-bernard-dispatcher): Queue multi-backend providing different - drivers like Doctrine DBAL and Predis (see http://bernardphp.com for a complete list of drivers) -- [GuzzleHttpMessageDispatcher](https://github.com/prooph/psb-http-dispatcher): Send messages to a remote system using - HTTP - -# RemoteQueryDispatcher - -A remote query dispatcher implementation needs to provide a response by resolving the handed over deferred. -In a messaging system based on RabbitMQ for example you can make use of a callback queue feature. -HTTP APIs provide responses naturally. -So these are both good candidates to use for remote querying. diff --git a/docs/plugins.md b/docs/plugins.md index 10961c6..af6f48d 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -3,16 +3,12 @@ PSB Plugins [Back to documentation](../README.md#documentation) -Plugins expand a message bus with additional functionality. The basic task of a message bus, be it a [CommandBus](command_bus.md) or [EventBus](event_bus.md), -is to dispatch a message. To achieve this goal the bus needs to collect some information about the message and perform -actions to ensure that a responsible message handler is invoked. Detailed information about the process can be found on the appropriate bus documentation pages. -Plugins hook into the dispatch process and provide the required information like the name of the message or a routing map and they also -prepare the message for invocation, locate the message handlers and invoke them. +Plugins expand a message bus with additional functionality. PSB ships with a list of useful plugins that can be mixed and matched with your own implementations: # Routers -## Prooph\ServiceBus\Router\CommandRouter +## Prooph\ServiceBus\Plugin\Router\CommandRouter Use the CommandRouter to provide a list of commands (identified by their names) and their responsible command handlers. @@ -33,14 +29,14 @@ $router->route('My.Command.PayOrder')->to("payment_processor"); $commandBus->utilize($router); ``` -## Prooph\ServiceBus\Router\QueryRouter +## Prooph\ServiceBus\Plugin\Router\QueryRouter Use the QueryRouter to provide a list of queries (identified by their names) and their responsible finders. The QueryRouter share the same base class with the CommandRouter so its interface looks exactly the same. -## Prooph\ServiceBus\Router\EventRouter +## Prooph\ServiceBus\Plugin\Router\EventRouter Use the EventRouter to provide a list of event messages (identified by their names) and all interested listeners per event message. @@ -58,7 +54,7 @@ $router->route('My.Event.OrderWasPayed')->to("delivery_processor"); $eventBus->utilize($router); ``` -## Prooph\ServiceBus\Router\RegexRouter +## Prooph\ServiceBus\Plugin\Router\RegexRouter The RegexRouter works with regular expressions to determine handlers for messages. It can be used together with a CommandBus, a QueryBus and an EventBus but for the latter it behaves a bit different. When routing a command or query the RegexRouter makes sure that only one pattern matches. @@ -93,59 +89,27 @@ $eventBus->utilize($router); # Invoke Strategies An invoke strategy knows how a message handler can be invoked. You can register many invoke strategies at once depending on -how many different handlers you are using. The best way is to choose a convention and go with it. PSB ships with the invoke strategies +how many different handler types you are using. The best way is to choose a convention and go with it. PSB ships with the invoke strategies listed below. If your favorite convention is not there you can easily write your own invoke strategy -by extending the [AbstractInvokeStrategy](../src/Prooph/ServiceBus/InvokeStrategy/AbstractInvokeStrategy.php) and implementing the +by extending the [AbstractInvokeStrategy](../src/Prooph/ServiceBus/Plugin/InvokeStrategy/AbstractInvokeStrategy.php) and implementing the `canInvoke` and `invoke` methods. ## Available Strategies -- `CallbackStrategy`: Is responsible for invoking callable message handlers, can be used together with a CommandBus and EventBus - `HandleCommandStrategy`: Is responsible for invoking a `handle` method of a command handler. Forces the rule that a command handler should only be responsible for handling one specific command. - `OnEventStrategy`: Prefixes the short class name of an event with `on`. A listener should have a public method named this way: OrderCartUpdater::onArticleWasBought. -- `FinderInvokeStrategy`: This strategy is responsible for invoking finders. It either looks for a finder method named like the short class name of the query or it -checks if the finder is callable (implements the magic __invoke method f.e.). -- `ForwardToRemoteMessageDispatcherStrategy`: This is a special invoke strategy that is capable of translating a command or event to -a [RemoteMessage](https://github.com/prooph/common/blob/master/src/Messaging/RemoteMessage.php) and invoke a [RemoteMessageDispatcher](message_dispatcher.md). -Add this strategy to a bus together with a [ToRemoteMessageTranslator](../src/Prooph/ServiceBus/Message/ToRemoteMessageTranslator.php) and -route a command or event to a RemoteMessageDispatcher to process the message async: +- `FinderInvokeStrategy`: This strategy is responsible for invoking finders. It looks for a finder method named like the short class name of the query. -```php -$eventBus->utilize(new ForwardToRemoteMessageDispatcherStrategy(new ProophDomainMessageToRemoteMessageTranslator())); - -$router = new EventRouter(); - -$router->route('SomethingDone')->to(new My\Async\MessageDispatcher()); - -$eventBus->utilize($router); - -$eventBus->dispatch(new SomethingDone()); -``` - -- `ForwardToRemoteQueryDispatcherStrategy`: Like the `ForwardToRemoteMessageDispatcherStrategy` this invoke strategy translates a -query to a [RemoteMessage](https://github.com/prooph/common/blob/master/src/Messaging/RemoteMessage.php) but it invokes a [RemoteQueryDispatcher](message_dispatcher.md#RemoteQueryDispatcher) instead. - - -# FromRemoteMessageTranslator - -The [FromRemoteMessageTranslator](../src/Prooph/ServiceBus/Message/FromRemoteMessageTranslator.php) plugin does the opposite of the `ForwardToRemoteMessageDispatcherStrategy`. -It listens on the `initialize` dispatch action event of a CommandBus, QueryBus or EventBus and if it detects an incoming [RemoteMessage](https://github.com/prooph/common/blob/master/src/Messaging/RemoteMessage.php) -it translates the message to a [Command](https://github.com/prooph/common/blob/master/src/Messaging/Command.php), [Query](https://github.com/prooph/common/blob/master/src/Messaging/Query.php) or [DomainEvent](https://github.com/prooph/common/blob/master/src/Messaging/DomainEvent.php) depending on the type -provided in the [MessageHeader](https://github.com/prooph/common/blob/master/src/Messaging/MessageHeader.php). A receiver of an asynchronous dispatched message, for example a worker of a -message queue, can pull a [RemoteMessage](https://github.com/prooph/common/blob/master/src/Messaging/RemoteMessage.php) from the queue and forward it to a appropriate configured CommandBus or EventBus without additional work. - -*Note: If the message name is an existing class it is used instead of the default implementation. - But the custom message class MUST provide a static `fromRemoteMessage` factory method, otherwise the translator will break with a fatal error! +Note: When a message bus detects that the message handler is callable invoke strategies are skipped and the message handler is directly invoked by the message bus. -# ServiceLocatorProxy +# ServiceLocatorPlugin -This plugin uses a Prooph\Common\ServiceLocator implementation to lazy instantiate command handlers and event listeners. -The following example uses a ZF2 ServiceManager as a DIC and illustrates how it can be used together with a command bus: +This plugin uses a `Interop\Container\ContainerInterface` implementation to lazy-load message handlers. +The following example uses a ZF2 ServiceManager as a service locator and illustrates how it can be used together with a command bus: ```php use Zend\ServiceManager\ServiceManager; -use Prooph\Common\ServiceLocator\ZF2\ZF2ServiceManagerProxy; //We tell the ServiceManager that it should provide an instance of My\Command\DoSomethingHandler //when we request it with the alias My.Command.DoSomethingHandler @@ -155,8 +119,8 @@ $serviceManager = new ServiceManager(new Config([ ] ])); -//The ZF2ServiceManagerProxy implements Prooph\Common\ServiceLocator -$commandBus->utilize(new ServiceLocatorProxy(ZF2ServiceManagerProxy::proxy($serviceManager))); +//The ZF2\ServiceManager implements Interop\Container\ContainerInterface since v2.6 +$commandBus->utilize(new ServiceLocatorPlugin($serviceManager); $router = new CommandRouter(); @@ -166,6 +130,5 @@ $router->route('My.Command.DoSomething')->to('My.Command.DoSomethingHandler'); $commandBus->utilize($router); ``` -With this technique you can configure the routing for all your messages without the need to create all the message handlers -on every request. Only the responsible command handler or all interested event listeners (when dealing with event messages) -are lazy loaded by the ServiceManager. If you prefer to use another DIC then write your own proxy which implements Prooph\Common\ServiceLocator. \ No newline at end of file +With this technique you can configure the routing for all your messages without the need to create all message handlers +on every request. Only the responsible message handlers are lazy loaded by the service locator plugin. diff --git a/docs/query_bus.md b/docs/query_bus.md deleted file mode 100644 index ec8f765..0000000 --- a/docs/query_bus.md +++ /dev/null @@ -1,124 +0,0 @@ -The QueryBus -============== - -[Back to documentation](../README.md#documentation) - -# Usage - -When you want to build a system based on [Microservices](http://martinfowler.com/articles/microservices.html) you need a lightweight communication channel. -The two protocols used most commonly are HTTP request-response with resource API's and lightweight messaging. The latter is supported by prooph/service-bus -out-of-the-box but HTTP API's can be integrated too. -The QueryBus is responsible for routing a query message to a so called finder. The query indicates that the producer expects a response. -The finder's responsibility is to fetch data from a data source using the query parameters defined in the query message. It is up to the finder if the data is fetched synchronous -or asynchronous, so the QueryBus returns a [promise](https://github.com/reactphp/promise) to the query producer which gets resolved by the finder. - -# API - -```php -class QueryBus extends MessageBus -{ - /** - * @param \Prooph\Common\Event\ActionEventListenerAggregate|\Psr\Log\LoggerInterface $plugin - * @return $this - * @throws Exception\RuntimeException - */ - public function utilize($plugin); - - /** - * @param \Prooph\Common\Event\ActionEventListenerAggregate|\Psr\Log\LoggerInterface $plugin - * @return $this - * @throws Exception\RuntimeException - */ - public function deactivate($plugin); - - /** - * @param string $eventName - * @param callable $listener - * @param int $priority - * @return \Zend\Stdlib\CallbackHandler - */ - public function on($eventName, $listener, $priority = 1); - - /** - * @param \Zend\Stdlib\CallbackHandler $callbackHandler - * @return bool - */ - public function off(CallbackHandler $callbackHandler); - - /** - * @param mixed $query - * @return \React\Promise\Promise - */ - public function dispatch($query); -} -``` - -The public API of the QueryBus is very simple. Four of the five methods deal with adding or removing plugins and the last -one triggers the dispatch process of the given query. - -** Note: Only `dispatch` is implemented by the QueryBus the four other public methods are provided by the basic MessageBus implementation. - -# Event-Driven Dispatch - -The query dispatch is an event-driven process provided by a Prooph\Common\Event\ActionEventDispatcher. -When a query is passed to the QueryBus via `QueryBus::dispatch` a new [QueryDispatch](../src/Prooph/ServiceBus/Process/QueryDispatch.php) process is created by the QueryBus and populated with the given query. -Then the QueryBus triggers a chain of action events. Plugins can listen on these action events. They always get the QueryDispatch as the only argument and they can -modify it to help the QueryBus finish the process. A QueryDispatch without any registered plugins is useless and will throw an exception because -it does not know which finder is responsible for the query. -Following action events are triggered in the listed order: - -- `initialize`: This action event is triggered right after QueryDispatch::dispatch($query) is invoked. At this time the QueryDispatch only contains the query. - -- `detect-message-name` (optional): Before a finder can be located, the QueryBus needs to know how the query is named. Their are two -possibilities to provide the information. The query can implement the [Prooph\Common\Messaging\HasMessageName](https://github.com/prooph/common/blob/master/src/Messaging/HasMessageName.php) interface. -In this case the QueryBus picks the query name directly from the query and inject it manually in the QueryDispatch. The `detect-message-name` event is not triggered. If the query -does not implement the interface the `detect-message-name` event is triggered and a plugin needs to inject the name using `QueryDispatch::setQueryName`. - -- `route`: During the `route` action event a plugin should provide the responsible finder either in form of a ready to use object or callable or as a string -representing an alias of the finder that can be used by a DIC to locate an instance. The plugin should provide the handler by using -`QueryDispatch::setFinder`. - -- `locate-finder` (optional): After routing the query, the QueryBus checks if the finder was provided as a string. If so it triggers the -`locate-finder` event. This is the latest time to provide an object or callable as finder. If no plugin was able to provide one the QueryBus throws an exception. - -- `invoke-finder`: Having the finder in place it's time to invoke it with the query and a [deferred](https://github.com/reactphp/promise/blob/master/src/Deferred.php). -The QueryBus always triggers the event. It performs no default action even if the finder is a callable. - -- `handle-error`: If at any time a plugin or the QueryBus itself throws an exception it is caught and passed to the QueryDispatch. The normal action event chain breaks and a -`handle-error` event is triggered instead. Plugins can access the exception by calling `QueryDispatch::getException`. -A `handle-error` plugin or a `finalize` plugin can unset the exception by calling `QueryDispatch::setException(null)`. -When all plugins are informed about the error and no one has unset the exception the QueryBus rejects the promise with a Prooph\ServiceBus\Exception\QueryDispatchException to inform the producer about the error. - -- `finalize`: This action event is always triggered at the end of the process no matter if the process was successful or the promise was rejected. It is the ideal place to -attach a monitoring plugin. - -# Queries - -A query can nearly be everything. PSB tries to get out of your way as much as it can. You are ask to use your own query implementation or you use the -default [Query](https://github.com/prooph/common/blob/master/src/Messaging/Query.php) class provided by prooph/common. It is a very good base class -and PSB ships with translator plugins to translate a Query into a remote message -that can be send to a remote interface. Check the [Remote Message Dispatcher](message_dispatcher.md) for more details. - -# Plugins - -Plugins can be simple callables (use the methods `on` and `off` to attach/detach them), implementations of the -\Prooph\Common\Event\ActionEventListenerAggregate (use the methods `utilize` and `deactivate` to attach/detach them) or an instance of -Psr\Log\LoggerInterface (also use methods `utilize` and `deactivate` to attach/detach it). -The signature of a plugin method/callable that listens on a CommandDispatch event is: - -```php -function (\Prooph\ServiceBus\Process\QueryDispatch $queryDispatch) {}; -``` - -Check the list of available [plugins](plugins.md) shipped with ProophServiceBus. If they don't meet your needs don't hesitate to write your -own plugins. It is really straight forward. - -# Logging - -If you add a Psr\Log\LoggerInterface as a plugin it is passed to the QueryDispatch and available during the dispatch so the -listener plugins can log their activities. - - - - - diff --git a/docs/queue_producer.md b/docs/queue_producer.md new file mode 100644 index 0000000..d573e43 --- /dev/null +++ b/docs/queue_producer.md @@ -0,0 +1,28 @@ +QueueProducer +============= + +[Back to documentation](../README.md#documentation) + +# Usage + +Messaging becomes really interesting when you process your messages asynchronous. For example push your messages on a database queue, +set up a cron job to periodically check the queue for new messages and process them. The bus implementations of PSB can +hide such an asynchronous workflow behind a unified interface. You can start with synchronous message dispatching by +routing your messages directly to message handlers and if you later want to improve response times you can switch to +async processing on a per message basis by routing the appropriate messages to a queue producer listed below. + +# Available QueueProducer + +- [PhpResqueDispatcher](https://github.com/prooph/psb-php-resque-dispatcher): Perfect choice for async + command processing using a ultra fast redis queue +- [BernardDispatcher](https://github.com/prooph/psb-bernard-dispatcher): Queue multi-backend providing different + drivers like Doctrine DBAL and Predis (see http://bernardphp.com for a complete list of drivers) +- [GuzzleHttpDispatcher](https://github.com/prooph/psb-http-dispatcher): Send messages to a remote system using + HTTP + +# RemoteQueryDispatcher + +A remote query dispatcher implementation needs to provide a response by resolving the handed over deferred. +In a messaging system based on RabbitMQ for example you can make use of a callback queue feature. +HTTP APIs provide responses naturally. +So these are both good candidates to use for remote querying. diff --git a/docs/service_bus_system.md b/docs/service_bus_system.md index 7d757ad..80b8acd 100644 --- a/docs/service_bus_system.md +++ b/docs/service_bus_system.md @@ -11,7 +11,7 @@ important differences. - The QueryBus also dispatches a message to only one finder but it returns a [promise](https://github.com/reactphp/promise). All buses provide an event-driven dispatch process to give plugins -the possibility to hook into the process and manipulate it. +the possibility to hook into the process. # Messaging @@ -22,12 +22,18 @@ dispatch process on a message bus. The bus is responsible for delivering the mes part of an external system that can only be accessed via a remote interface. For commands and events that means fire and forget. The producer gets no -response when it triggers the dispatch except an error occurs during the dispatch process. +response when it triggers the dispatch except an error occurs during the dispatch process itself. In this case the message bus throws an exception. When dispatching a query the message producer gets a promise back from the QueryBus. He also doesn't know if the query is dispatched synchronous or asynchronous but he can attach to the `promise::then` method to receive the response -of query. +of the query when it becomes available. + +## Message Objects + +You are free to use your own message objects (or even primitive types if you want). All message buses are smart enough to handle them. +If you need custom logic to handle your messages check out the list of available [plugins](plugins.md) or write your own bus plugin. +It is pretty straight forward. # Synchronous Versus Asynchronous Processing @@ -35,5 +41,7 @@ PSB provides both possibilities behind a unified interface. Remember the statement "Messaging means fire and forget". The message producer never knows if the message is processed synchronous or asynchronous. It depends on the bus configuration and/or the used plugins. A message can directly be routed to it's handler. In this case we talk about synchronous -message processing. If the receiver of the message is a [Prooph\ServiceBus\Message\RemoteMessageDispatcher](message_dispatcher.md) +message processing. If the handler of the message is a [queue producer](queue_producer.md) the message is normally processed asynchronously. + +Check out the [Message Bus API](message_bus.md) for details. diff --git a/examples/quick-start.php b/examples/quick-start.php index 7506c6b..0b9af7c 100644 --- a/examples/quick-start.php +++ b/examples/quick-start.php @@ -9,7 +9,7 @@ * Date: 15.03.14 - 22:34 */ namespace { - require_once '../vendor/autoload.php'; + require_once __DIR__ . '/../vendor/autoload.php'; } namespace Prooph\ServiceBus\Example\Command { @@ -19,26 +19,39 @@ class EchoText extends Command { /** - * @param string $text - * @return EchoText + * @var string */ - public static function fromString($text) + private $text; + + public function __construct($text) { - return new self(__CLASS__, $text); + $this->text = $text; } - protected function convertPayload($textOrPayload) + public function getText() { - if (is_string($textOrPayload)) { - $textOrPayload = array('text' => $textOrPayload); - } + return $this->text; + } - return $textOrPayload; + /** + * Return message payload as array + * + * @return array + */ + public function payload() + { + return ['text' => $this->text]; } - public function getText() + /** + * This method is called when message is instantiated named constructor fromArray + * + * @param array $payload + * @return void + */ + protected function setPayload(array $payload) { - return $this->payload['text']; + $this->text = $payload['text']; } } } @@ -46,8 +59,7 @@ public function getText() namespace { use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\Example\Command\EchoText; - use Prooph\ServiceBus\InvokeStrategy\CallbackStrategy; - use Prooph\ServiceBus\Router\CommandRouter; + use Prooph\ServiceBus\Plugin\Router\CommandRouter; $commandBus = new CommandBus(); @@ -62,11 +74,8 @@ public function getText() //Expand command bus with the router plugin $commandBus->utilize($router); - //Expand command bus with the callback invoke strategy - $commandBus->utilize(new CallbackStrategy()); - //We create a new Command - $echoText = EchoText::fromString('It works'); + $echoText = new EchoText('It works'); //... and dispatch it $commandBus->dispatch($echoText); diff --git a/src/CommandBus.php b/src/CommandBus.php new file mode 100644 index 0000000..2284ec6 --- /dev/null +++ b/src/CommandBus.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 14.09.14 - 16:32 + */ + +namespace Prooph\ServiceBus; + +use Prooph\ServiceBus\Exception\RuntimeException; + +/** + * Class CommandBus + * + * A command bus is capable of dispatching a message to a command handler. + * Only one handler per message is allowed! + * + * @package Prooph\ServiceBus + * @author Alexander Miertsch + */ +class CommandBus extends MessageBus +{ + /** + * @param mixed $command + * @return void + * @throws Exception\MessageDispatchException + */ + public function dispatch($command) + { + $actionEvent = $this->getActionEventEmitter()->getNewActionEvent(); + + $actionEvent->setTarget($this); + + try { + $this->initialize($command, $actionEvent); + + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { + $actionEvent->setName(self::EVENT_ROUTE); + + $this->trigger($actionEvent); + } + + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { + throw new RuntimeException(sprintf( + "CommandBus was not able to identify a CommandHandler for command %s", + $this->getMessageType($command) + )); + } + + if (is_string($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER))) { + $actionEvent->setName(self::EVENT_LOCATE_HANDLER); + + $this->trigger($actionEvent); + } + + $commandHandler = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); + + if (is_callable($commandHandler)) { + $commandHandler($command); + } else { + $actionEvent->setName(self::EVENT_INVOKE_HANDLER); + $this->trigger($actionEvent); + } + + $this->triggerFinalize($actionEvent); + } catch (\Exception $ex) { + $this->handleException($actionEvent, $ex); + } + } +} + \ No newline at end of file diff --git a/src/EventBus.php b/src/EventBus.php new file mode 100644 index 0000000..f9b6d70 --- /dev/null +++ b/src/EventBus.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 16.09.14 - 21:33 + */ + +namespace Prooph\ServiceBus; + +/** + * Class EventBus + * + * An event bus is capable of dispatching a message to multiple listeners. + * + * @package Prooph\ServiceBus + * @author Alexander Miertsch + */ +class EventBus extends MessageBus +{ + const EVENT_PARAM_EVENT_LISTENERS = 'event-listeners'; + + /** + * @param mixed $event + * @return void + */ + public function dispatch($event) + { + $actionEvent = $this->getActionEventEmitter()->getNewActionEvent(); + + $actionEvent->setTarget($this); + + try { + $this->initialize($event, $actionEvent); + + $actionEvent->setName(self::EVENT_ROUTE); + + $this->trigger($actionEvent); + + foreach ($actionEvent->getParam(self::EVENT_PARAM_EVENT_LISTENERS, []) as $eventListener) { + + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_HANDLER, $eventListener); + + if (is_string($eventListener)) { + $actionEvent->setName(self::EVENT_LOCATE_HANDLER); + + $this->trigger($actionEvent); + } + + $eventListener = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); + + if (is_callable($eventListener)) { + $eventListener($event); + } else { + $actionEvent->setName(self::EVENT_INVOKE_HANDLER); + + $this->trigger($actionEvent); + } + } + + $this->triggerFinalize($actionEvent); + } catch (\Exception $ex) { + $this->handleException($actionEvent, $ex); + } + } +} + \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Exception/CommandDispatchException.php b/src/Exception/MessageDispatchException.php similarity index 50% rename from src/Prooph/ServiceBus/Exception/CommandDispatchException.php rename to src/Exception/MessageDispatchException.php index 29d66d4..b2b305a 100644 --- a/src/Prooph/ServiceBus/Exception/CommandDispatchException.php +++ b/src/Exception/MessageDispatchException.php @@ -11,54 +11,54 @@ namespace Prooph\ServiceBus\Exception; -use Prooph\ServiceBus\Process\CommandDispatch; +use Prooph\Common\Event\ActionEvent; /** - * Class CommandDispatchException + * Class MessageDispatchException * * @package Prooph\ServiceBus\Exception * @author Alexander Miertsch */ -class CommandDispatchException extends RuntimeException +class MessageDispatchException extends RuntimeException { /** - * @var CommandDispatch + * @var ActionEvent */ - protected $commandDispatch; + protected $actionEvent; /** - * @param CommandDispatch $commandDispatch + * @param ActionEvent $actionEvent * @param \Exception $previousException - * @return CommandDispatchException + * @return MessageDispatchException */ - public static function failed(CommandDispatch $commandDispatch, \Exception $previousException = null) + public static function failed(ActionEvent $actionEvent, \Exception $previousException = null) { $ex = new self( sprintf( - "Command dispatch failed during %s phase.%s", - $commandDispatch->getName(), + "Message dispatch failed during %s phase.%s", + $actionEvent->getName(), (is_null($previousException))? '' : ' Error: ' . $previousException->getMessage() ), 422, $previousException ); - $ex->setFailedDispatch($commandDispatch); + $ex->setFailedDispatch($actionEvent); return $ex; } /** - * @return CommandDispatch + * @return ActionEvent */ - public function getFailedCommandDispatch() + public function getFailedDispatchEvent() { - return $this->commandDispatch; + return $this->actionEvent; } - protected function setFailedDispatch(CommandDispatch $commandDispatch) + protected function setFailedDispatch(ActionEvent $actionEvent) { - $this->commandDispatch = $commandDispatch; + $this->actionEvent = $actionEvent; } } \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Exception/RuntimeException.php b/src/Exception/RuntimeException.php similarity index 100% rename from src/Prooph/ServiceBus/Exception/RuntimeException.php rename to src/Exception/RuntimeException.php diff --git a/src/Prooph/ServiceBus/Exception/ServiceBusException.php b/src/Exception/ServiceBusException.php similarity index 100% rename from src/Prooph/ServiceBus/Exception/ServiceBusException.php rename to src/Exception/ServiceBusException.php diff --git a/src/MessageBus.php b/src/MessageBus.php new file mode 100644 index 0000000..0c4debb --- /dev/null +++ b/src/MessageBus.php @@ -0,0 +1,189 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 13.01.15 - 14:59 + */ + +namespace Prooph\ServiceBus; + +use Prooph\Common\Event\ActionEvent; +use Prooph\Common\Event\ActionEventEmitter; +use Prooph\Common\Event\ActionEventListenerAggregate; +use Prooph\Common\Event\ProophActionEventEmitter; +use Prooph\Common\Messaging\HasMessageName; +use Prooph\ServiceBus\Exception\MessageDispatchException; +use Prooph\ServiceBus\Exception\RuntimeException; + +/** + * Class MessageBus + * + * Base class for a message bus implementation + * + * @package Prooph\ServiceBus + * @author Alexander Miertsch + */ +abstract class MessageBus +{ + const EVENT_INITIALIZE = "initialize"; + const EVENT_DETECT_MESSAGE_NAME = "detect-message-name"; + const EVENT_ROUTE = "route"; + const EVENT_LOCATE_HANDLER = "locate-handler"; + const EVENT_INVOKE_HANDLER = "invoke-handler"; + const EVENT_HANDLE_ERROR = "handle-error"; + const EVENT_FINALIZE = "finalize"; + + const EVENT_PARAM_MESSAGE = 'message'; + const EVENT_PARAM_MESSAGE_NAME = 'message-name'; + const EVENT_PARAM_MESSAGE_HANDLER = 'message-handler'; + const EVENT_PARAM_EXCEPTION = 'exception'; + + /** + * @var ActionEventEmitter + */ + protected $events; + + /** + * @param mixed $message + * @return mixed|void depends on the bus type + */ + abstract public function dispatch($message); + + /** + * @param ActionEventListenerAggregate $plugin + */ + public function utilize(ActionEventListenerAggregate $plugin) + { + $plugin->attach($this->getActionEventEmitter()); + } + + /** + * @param ActionEventListenerAggregate $plugin + */ + public function deactivate(ActionEventListenerAggregate $plugin) + { + $plugin->detach($this->getActionEventEmitter()); + } + + /** + * @param mixed $message + * @param ActionEvent $actionEvent + */ + protected function initialize($message, ActionEvent $actionEvent) + { + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE, $message); + + if ($message instanceof HasMessageName) { + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_NAME, $message->messageName()); + } + + $actionEvent->setName(self::EVENT_INITIALIZE); + + $this->trigger($actionEvent); + + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_NAME) === null) { + $actionEvent->setName(self::EVENT_DETECT_MESSAGE_NAME); + + $this->trigger($actionEvent); + + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_NAME) === null) { + $actionEvent->setParam(self::EVENT_PARAM_MESSAGE_NAME, $this->getMessageType($message)); + } + } + } + + /** + * @param ActionEvent $actionEvent + * @param \Exception $ex + * @throws Exception\MessageDispatchException + */ + protected function handleException(ActionEvent $actionEvent, \Exception $ex) + { + $failedPhase = $actionEvent->getName(); + + $actionEvent->setParam(self::EVENT_PARAM_EXCEPTION, $ex); + $this->triggerError($actionEvent); + $this->triggerFinalize($actionEvent); + + //Check if a listener has removed the exception to indicate that it was able to handle it + if ($ex = $actionEvent->getParam(self::EVENT_PARAM_EXCEPTION)) { + $actionEvent->setName($failedPhase); + throw MessageDispatchException::failed($actionEvent, $ex); + } + } + + /** + * @param ActionEvent $actionEvent + * @throws Exception\RuntimeException + */ + protected function trigger(ActionEvent $actionEvent) + { + $this->getActionEventEmitter()->dispatch($actionEvent); + + if ($actionEvent->propagationIsStopped()) { + throw new RuntimeException("Dispatch has stopped unexpectedly."); + } + } + + /** + * @param ActionEvent $actionEvent + */ + protected function triggerError(ActionEvent $actionEvent) + { + $actionEvent->setName(self::EVENT_HANDLE_ERROR); + + $this->getActionEventEmitter()->dispatch($actionEvent); + } + + /** + * @param ActionEvent $actionEvent + */ + protected function triggerFinalize(ActionEvent $actionEvent) + { + $actionEvent->setName(self::EVENT_FINALIZE); + + $this->getActionEventEmitter()->dispatch($actionEvent); + } + + + /** + * Inject an ActionEventDispatcher instance + * + * @param ActionEventEmitter $actionEventDispatcher + * @return void + */ + public function setActionEventDispatcher(ActionEventEmitter $actionEventDispatcher) + { + $this->events = $actionEventDispatcher; + } + + /** + * Retrieve the action event dispatcher + * + * Lazy-loads a dispatcher if none is registered. + * + * @return ActionEventEmitter + */ + public function getActionEventEmitter() + { + if (is_null($this->events)) { + $this->setActionEventDispatcher(new ProophActionEventEmitter()); + } + + return $this->events; + } + + /** + * @param mixed $message + * @return string + */ + protected function getMessageType($message) + { + return is_object($message)? get_class($message) : gettype($message); + } +} + \ No newline at end of file diff --git a/src/Plugin/InvokeStrategy/AbstractInvokeStrategy.php b/src/Plugin/InvokeStrategy/AbstractInvokeStrategy.php new file mode 100644 index 0000000..b0d51a2 --- /dev/null +++ b/src/Plugin/InvokeStrategy/AbstractInvokeStrategy.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 16.09.14 - 20:52 + */ + +namespace Prooph\ServiceBus\Plugin\InvokeStrategy; + +use Prooph\Common\Event\ActionEvent; +use Prooph\Common\Event\ActionEventEmitter; +use Prooph\Common\Event\ActionEventListenerAggregate; +use Prooph\Common\Event\DetachAggregateHandlers; +use Prooph\ServiceBus\MessageBus; + +/** + * Class AbstractInvokeStrategy + * + * @package Prooph\ServiceBus\InvokeStrategy + * @author Alexander Miertsch + */ +abstract class AbstractInvokeStrategy implements ActionEventListenerAggregate +{ + use DetachAggregateHandlers; + + protected $priority = 0; + + /** + * @param mixed $handler + * @param mixed $message + * @return bool + */ + abstract protected function canInvoke($handler, $message); + + /** + * @param mixed $handler + * @param mixed $message + */ + abstract protected function invoke($handler, $message); + + /** + * Attach one or more listeners + * + * @param ActionEventEmitter $events + * + * @return void + */ + public function attach(ActionEventEmitter $events) + { + $this->trackHandler($events->attachListener(MessageBus::EVENT_INVOKE_HANDLER, $this, $this->priority)); + } + + /** + * @param ActionEvent $e + */ + public function __invoke(ActionEvent $e) + { + $message = $e->getParam(MessageBus::EVENT_PARAM_MESSAGE); + $handler = $e->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); + + if ($this->canInvoke($handler, $message)) { + $this->invoke($handler, $message); + } + } +} + \ No newline at end of file diff --git a/src/Prooph/ServiceBus/InvokeStrategy/FinderInvokeStrategy.php b/src/Plugin/InvokeStrategy/FinderInvokeStrategy.php similarity index 54% rename from src/Prooph/ServiceBus/InvokeStrategy/FinderInvokeStrategy.php rename to src/Plugin/InvokeStrategy/FinderInvokeStrategy.php index 6f2a797..473c934 100644 --- a/src/Prooph/ServiceBus/InvokeStrategy/FinderInvokeStrategy.php +++ b/src/Plugin/InvokeStrategy/FinderInvokeStrategy.php @@ -8,13 +8,14 @@ * * Date: 5/23/15 - 4:48 PM */ -namespace Prooph\ServiceBus\InvokeStrategy; +namespace Prooph\ServiceBus\Plugin\InvokeStrategy; -use Prooph\Common\Event\ActionEventDispatcher; +use Prooph\Common\Event\ActionEvent; +use Prooph\Common\Event\ActionEventEmitter; use Prooph\Common\Event\ActionEventListenerAggregate; use Prooph\Common\Event\DetachAggregateHandlers; use Prooph\Common\Messaging\HasMessageName; -use Prooph\ServiceBus\Process\QueryDispatch; +use Prooph\ServiceBus\QueryBus; /** * Class FinderInvokeStrategy @@ -22,7 +23,7 @@ * This is a special invoke strategy for finders handling a query message and providing a response by resolving the * deferred of the query dispatch. * - * The invoke strategy can handle callable finders and finders which have a method named like the short name of the query. + * The invoke strategy can handle finders which have a method named like the short name of the query. * * @package Prooph\ServiceBus\InvokeStrategy * @author Alexander Miertsch @@ -32,41 +33,41 @@ final class FinderInvokeStrategy implements ActionEventListenerAggregate use DetachAggregateHandlers; /** - * @param ActionEventDispatcher $dispatcher + * @param ActionEventEmitter $dispatcher */ - public function attach(ActionEventDispatcher $dispatcher) + public function attach(ActionEventEmitter $dispatcher) { - $this->trackHandler($dispatcher->attachListener(QueryDispatch::INVOKE_FINDER, [$this, 'onInvokeFinder'])); + $this->trackHandler($dispatcher->attachListener(QueryBus::EVENT_INVOKE_FINDER, $this)); } /** - * @param QueryDispatch $queryDispatch + * @param ActionEvent $actionEvent */ - public function onInvokeFinder(QueryDispatch $queryDispatch) + public function __invoke(ActionEvent $actionEvent) { - $finder = $queryDispatch->getFinder(); + $finder = $actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLER); + + $query = $actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE); + + $deferred = $actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED); if (is_object($finder)) { - $queryName = $this->determineQueryName($queryDispatch->getQuery()); + $queryName = $this->determineQueryName($query); if (method_exists($finder, $queryName)) { - $finder->{$queryName}($queryDispatch->getQuery(), $queryDispatch->getDeferred()); + $finder->{$queryName}($query, $deferred); return; } } - - if (is_callable($finder)) { - $finder($queryDispatch->getQuery(), $queryDispatch->getDeferred()); - } } /** * @param mixed $query * @return string */ - protected function determineQueryName($query) + private function determineQueryName($query) { - $queryName = ($query instanceof HasMessageName)? $query->messageName() : get_class($query); + $queryName = ($query instanceof HasMessageName)? $query->messageName() : is_object($query)? get_class($query) : gettype($query); return join('', array_slice(explode('\\', $queryName), -1)); } } \ No newline at end of file diff --git a/src/Prooph/ServiceBus/InvokeStrategy/HandleCommandStrategy.php b/src/Plugin/InvokeStrategy/HandleCommandStrategy.php similarity index 52% rename from src/Prooph/ServiceBus/InvokeStrategy/HandleCommandStrategy.php rename to src/Plugin/InvokeStrategy/HandleCommandStrategy.php index 9eb1e1c..308de44 100644 --- a/src/Prooph/ServiceBus/InvokeStrategy/HandleCommandStrategy.php +++ b/src/Plugin/InvokeStrategy/HandleCommandStrategy.php @@ -9,8 +9,7 @@ * Date: 09.03.14 - 21:41 */ -namespace Prooph\ServiceBus\InvokeStrategy; -use Prooph\Common\Messaging\Command; +namespace Prooph\ServiceBus\Plugin\InvokeStrategy; /** * Class HandleCommandStrategy @@ -22,26 +21,22 @@ class HandleCommandStrategy extends AbstractInvokeStrategy { /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent + * @param mixed $handler + * @param mixed $message * @return bool */ - public function canInvoke($aHandler, $aCommandOrEvent) + public function canInvoke($handler, $message) { - if (! $aCommandOrEvent instanceof Command) { - return false; - } - - return method_exists($aHandler, 'handle'); + return method_exists($handler, 'handle'); } /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent + * @param mixed $handler + * @param mixed $message */ - public function invoke($aHandler, $aCommandOrEvent) + public function invoke($handler, $message) { - $aHandler->handle($aCommandOrEvent); + $handler->handle($message); } } \ No newline at end of file diff --git a/src/Plugin/InvokeStrategy/OnEventStrategy.php b/src/Plugin/InvokeStrategy/OnEventStrategy.php new file mode 100644 index 0000000..c436311 --- /dev/null +++ b/src/Plugin/InvokeStrategy/OnEventStrategy.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 11.03.14 - 21:40 + */ + +namespace Prooph\ServiceBus\Plugin\InvokeStrategy; + +use Prooph\Common\Messaging\HasMessageName; + +/** + * Class OnEventStrategy + * + * @package Prooph\ServiceBus\InvokeStrategy + * @author Alexander Miertsch + */ +class OnEventStrategy extends AbstractInvokeStrategy +{ + /** + * @param mixed $handler + * @param mixed $message + * @return bool + */ + public function canInvoke($handler, $message) + { + $handleMethod = 'on' . $this->determineEventName($message); + + return method_exists($handler, $handleMethod); + } + + /** + * @param mixed $handler + * @param mixed $message + */ + public function invoke($handler, $message) + { + $handleMethod = 'on' . $this->determineEventName($message); + + $handler->{$handleMethod}($message); + } + + /** + * @param mixed $event + * @return string + */ + protected function determineEventName($event) + { + $eventName = ($event instanceof HasMessageName)? $event->messageName() : is_object($event)? get_class($event) : gettype($event); + return join('', array_slice(explode('\\', $eventName), -1)); + } +} + \ No newline at end of file diff --git a/src/Plugin/MessageFactoryPlugin.php b/src/Plugin/MessageFactoryPlugin.php new file mode 100644 index 0000000..ec374fc --- /dev/null +++ b/src/Plugin/MessageFactoryPlugin.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 7/26/15 - 10:12 PM + */ +namespace Prooph\ServiceBus\Plugin; + +use Prooph\Common\Event\ActionEvent; +use Prooph\Common\Event\ActionEventEmitter; +use Prooph\Common\Event\ActionEventListenerAggregate; +use Prooph\Common\Event\DetachAggregateHandlers; +use Prooph\Common\Messaging\MessageFactory; +use Prooph\ServiceBus\MessageBus; + +/** + * Class MessageFactoryPlugin + * + * This plugin listens on the MessageBus::EVENT_INITIALIZE action event. + * It checks if the message of the action event is given as an array and + * if the array contains a key "message_name". + * If both conditions are met the plugin uses the injected Prooph\Common\Messaging\MessageFactory + * to translate the message array into a Prooph\Common\Messaging\DomainMessage + * + * @package Prooph\ServiceBus\Plugin + * @author Alexander Miertsch + */ +final class MessageFactoryPlugin implements ActionEventListenerAggregate +{ + use DetachAggregateHandlers; + + /** + * @var MessageFactory + */ + private $messageFactory; + + /** + * @param MessageFactory $messageFactory + */ + public function __construct(MessageFactory $messageFactory) + { + $this->messageFactory = $messageFactory; + } + + /** + * @param ActionEventEmitter $dispatcher + */ + public function attach(ActionEventEmitter $dispatcher) + { + $this->trackHandler($dispatcher->attachListener(MessageBus::EVENT_INITIALIZE, $this)); + } + + /** + * @param ActionEvent $actionEvent + */ + public function __invoke(ActionEvent $actionEvent) + { + $message = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); + + if (! is_array($message)) return; + + if (! array_key_exists('message_name', $message)) return; + + $messageName = $message['message_name']; + unset($message['message_name']); + + $message = $this->messageFactory->createMessageFromArray($messageName, $message); + + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE, $message); + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, $messageName); + } +} \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Router/CommandRouter.php b/src/Plugin/Router/CommandRouter.php similarity index 91% rename from src/Prooph/ServiceBus/Router/CommandRouter.php rename to src/Plugin/Router/CommandRouter.php index 1c2c62a..1f99d25 100644 --- a/src/Prooph/ServiceBus/Router/CommandRouter.php +++ b/src/Plugin/Router/CommandRouter.php @@ -9,7 +9,7 @@ * Date: 14.09.14 - 23:05 */ -namespace Prooph\ServiceBus\Router; +namespace Prooph\ServiceBus\Plugin\Router; /** * Class CommandRouter diff --git a/src/Prooph/ServiceBus/Router/EventRouter.php b/src/Plugin/Router/EventRouter.php similarity index 72% rename from src/Prooph/ServiceBus/Router/EventRouter.php rename to src/Plugin/Router/EventRouter.php index 0944e4d..e293b0a 100644 --- a/src/Prooph/ServiceBus/Router/EventRouter.php +++ b/src/Plugin/Router/EventRouter.php @@ -9,13 +9,16 @@ * Date: 23.09.14 - 20:20 */ -namespace Prooph\ServiceBus\Router; +namespace Prooph\ServiceBus\Plugin\Router; -use Prooph\Common\Event\ActionEventDispatcher; +use Assert\Assertion; +use Prooph\Common\Event\ActionEvent; +use Prooph\Common\Event\ActionEventEmitter; use Prooph\Common\Event\ActionEventListenerAggregate; use Prooph\Common\Event\DetachAggregateHandlers; +use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\Exception\RuntimeException; -use Prooph\ServiceBus\Process\EventDispatch; +use Prooph\ServiceBus\MessageBus; /** * Class EventRouter @@ -59,13 +62,13 @@ public function __construct(array $eventMap = null) } /** - * @param ActionEventDispatcher $events + * @param ActionEventEmitter $events * * @return void */ - public function attach(ActionEventDispatcher $events) + public function attach(ActionEventEmitter $events) { - $this->trackHandler($events->attachListener(EventDispatch::ROUTE, array($this, "onRouteEvent"))); + $this->trackHandler($events->attachListener(MessageBus::EVENT_ROUTE, array($this, "onRouteEvent"))); } /** @@ -75,7 +78,8 @@ public function attach(ActionEventDispatcher $events) */ public function route($eventName) { - \Assert\that($eventName)->notEmpty()->string(); + Assertion::string($eventName); + Assertion::notEmpty($eventName); if (! is_null($this->tmpEventName) && empty($this->eventMap[$this->tmpEventName])) { throw new RuntimeException(sprintf("event %s is not mapped to a listener.", $this->tmpEventName)); @@ -128,31 +132,25 @@ public function andTo($eventListener) } /** - * @param EventDispatch $eventDispatch + * @param ActionEvent $actionEvent */ - public function onRouteEvent(EventDispatch $eventDispatch) + public function onRouteEvent(ActionEvent $actionEvent) { - if (is_null($eventDispatch->getEventName()) && $eventDispatch->isLoggingEnabled()) { - $eventDispatch->getLogger()->notice( - sprintf("%s: EventDispatch contains no event name", get_called_class()) - ); + $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + + if (empty($messageName)) { return; } - if (!isset($this->eventMap[$eventDispatch->getEventName()])) { - if ($eventDispatch->isLoggingEnabled()) { - $eventDispatch->getLogger()->debug( - sprintf( - "%s: Cannot route %s to a listener. No listener registered for event.", - get_called_class(), - $eventDispatch->getEventName() - ) - ); - } + if (!isset($this->eventMap[$messageName])) { return; } - $eventDispatch->setEventListeners($this->eventMap[$eventDispatch->getEventName()]); + $listeners = $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, []); + + $listeners = array_merge($listeners, $this->eventMap[$messageName]); + + $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, $listeners); } } \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Router/QueryRouter.php b/src/Plugin/Router/QueryRouter.php similarity index 91% rename from src/Prooph/ServiceBus/Router/QueryRouter.php rename to src/Plugin/Router/QueryRouter.php index 0e266a3..222bd35 100644 --- a/src/Prooph/ServiceBus/Router/QueryRouter.php +++ b/src/Plugin/Router/QueryRouter.php @@ -8,7 +8,7 @@ * * Date: 5/23/15 - 6:49 PM */ -namespace Prooph\ServiceBus\Router; +namespace Prooph\ServiceBus\Plugin\Router; /** * Class QueryRouter diff --git a/src/Prooph/ServiceBus/Router/RegexRouter.php b/src/Plugin/Router/RegexRouter.php similarity index 62% rename from src/Prooph/ServiceBus/Router/RegexRouter.php rename to src/Plugin/Router/RegexRouter.php index b166489..46cccd3 100644 --- a/src/Prooph/ServiceBus/Router/RegexRouter.php +++ b/src/Plugin/Router/RegexRouter.php @@ -9,17 +9,18 @@ * Date: 30.10.14 - 22:29 */ -namespace Prooph\ServiceBus\Router; +namespace Prooph\ServiceBus\Plugin\Router; use Assert\Assertion; -use Prooph\Common\Event\ActionEventDispatcher; +use Prooph\Common\Event\ActionEvent; +use Prooph\Common\Event\ActionEventEmitter; use Prooph\Common\Event\ActionEventListenerAggregate; use Prooph\Common\Event\DetachAggregateHandlers; +use Prooph\ServiceBus\CommandBus; +use Prooph\ServiceBus\EventBus; use Prooph\ServiceBus\Exception\RuntimeException; -use Prooph\ServiceBus\Process\CommandDispatch; -use Prooph\ServiceBus\Process\EventDispatch; -use Prooph\ServiceBus\Process\MessageDispatch; -use Prooph\ServiceBus\Process\QueryDispatch; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\QueryBus; /** * Class RegexRouter @@ -61,13 +62,13 @@ public function __construct(array $patternMap = null) } /** - * @param ActionEventDispatcher $events + * @param ActionEventEmitter $events * * @return void */ - public function attach(ActionEventDispatcher $events) + public function attach(ActionEventEmitter $events) { - $this->trackHandler($events->attachListener(MessageDispatch::ROUTE, [$this, 'onRoute'], 100)); + $this->trackHandler($events->attachListener(MessageBus::EVENT_ROUTE, [$this, 'onRoute'], 100)); } /** @@ -119,24 +120,23 @@ public function to($handler) } /** - * @param MessageDispatch $messageDispatch + * @param ActionEvent $actionEvent */ - public function onRoute(MessageDispatch $messageDispatch) + public function onRoute(ActionEvent $actionEvent) { - if ($messageDispatch instanceof CommandDispatch || $messageDispatch instanceof QueryDispatch) $this->onRouteToSingleHandler($messageDispatch); - else $this->onRouteEvent($messageDispatch); + if ($actionEvent->getTarget() instanceof CommandBus || $actionEvent->getTarget() instanceof QueryBus) $this->onRouteToSingleHandler($actionEvent); + else $this->onRouteEvent($actionEvent); } /** - * @param MessageDispatch $messageDispatch + * @param ActionEvent $actionEvent * @throws \Prooph\ServiceBus\Exception\RuntimeException */ - private function onRouteToSingleHandler(MessageDispatch $messageDispatch) + private function onRouteToSingleHandler(ActionEvent $actionEvent) { - if (is_null($messageDispatch->getMessageName()) && $messageDispatch->isLoggingEnabled()) { - $messageDispatch->getLogger()->notice( - sprintf("%s: MessageDispatch contains no message name", get_called_class()) - ); + $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + + if (empty($messageName)) { return; } @@ -144,46 +144,41 @@ private function onRouteToSingleHandler(MessageDispatch $messageDispatch) foreach($this->patternMap as $map) { list($pattern, $handler) = each($map); - if (preg_match($pattern, $messageDispatch->getMessageName())) { + if (preg_match($pattern, $messageName)) { if ($alreadyMatched) { throw new RuntimeException(sprintf( "Multiple handlers detected for message %s. The patterns %s and %s matches both", - $messageDispatch->getMessageName(), + $messageName, $alreadyMatched, $pattern )); } else { - if ($messageDispatch instanceof CommandDispatch) { - $messageDispatch->setCommandHandler($handler); - } elseif ($messageDispatch instanceof QueryDispatch) { - $messageDispatch->setFinder($handler); - } else { - $messageDispatch->setParam('message-handler', $handler); - } - - $alreadyMatched = $pattern; + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); + + $alreadyMatched = true; } } } } /** - * @param EventDispatch $eventDispatch + * @param ActionEvent $actionEvent */ - private function onRouteEvent(EventDispatch $eventDispatch) + private function onRouteEvent(ActionEvent $actionEvent) { - if (is_null($eventDispatch->getEventName()) && $eventDispatch->isLoggingEnabled()) { - $eventDispatch->getLogger()->notice( - sprintf("%s: EventDispatch contains no event name", get_called_class()) - ); + $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + + if (empty($messageName)) { return; } foreach($this->patternMap as $map) { list($pattern, $handler) = each($map); - if (preg_match($pattern, $eventDispatch->getEventName())) { - $eventDispatch->addEventListener($handler); + if (preg_match($pattern, $messageName)) { + $listeners = $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, []); + $listeners[] = $handler; + $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, $listeners); } } } diff --git a/src/Prooph/ServiceBus/Router/SingleHandlerRouter.php b/src/Plugin/Router/SingleHandlerRouter.php similarity index 62% rename from src/Prooph/ServiceBus/Router/SingleHandlerRouter.php rename to src/Plugin/Router/SingleHandlerRouter.php index 887f060..7f2060a 100644 --- a/src/Prooph/ServiceBus/Router/SingleHandlerRouter.php +++ b/src/Plugin/Router/SingleHandlerRouter.php @@ -8,15 +8,17 @@ * * Date: 5/23/15 - 6:22 PM */ -namespace Prooph\ServiceBus\Router; +namespace Prooph\ServiceBus\Plugin\Router; -use Prooph\Common\Event\ActionEventDispatcher; +use Assert\Assertion; +use Prooph\Common\Event\ActionEvent; +use Prooph\Common\Event\ActionEventEmitter; use Prooph\Common\Event\ActionEventListenerAggregate; use Prooph\Common\Event\DetachAggregateHandlers; +use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\Exception\RuntimeException; -use Prooph\ServiceBus\Process\CommandDispatch; -use Prooph\ServiceBus\Process\MessageDispatch; -use Prooph\ServiceBus\Process\QueryDispatch; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\QueryBus; /** * Class SingleHandlerRouter @@ -51,13 +53,13 @@ public function __construct(array $messageMap = null) } /** - * @param ActionEventDispatcher $events + * @param ActionEventEmitter $events * * @return void */ - public function attach(ActionEventDispatcher $events) + public function attach(ActionEventEmitter $events) { - $this->trackHandler($events->attachListener(MessageDispatch::ROUTE, array($this, "onRouteMessage"))); + $this->trackHandler($events->attachListener(MessageBus::EVENT_ROUTE, array($this, "onRouteMessage"))); } /** @@ -67,7 +69,8 @@ public function attach(ActionEventDispatcher $events) */ public function route($messageName) { - \Assert\that($messageName)->notEmpty()->string(); + Assertion::string($messageName); + Assertion::notEmpty($messageName); if (! is_null($this->tmpMessageName)) { throw new RuntimeException(sprintf("Message %s is not mapped to a handler.", $this->tmpMessageName)); @@ -108,38 +111,22 @@ public function to($messageHandler) } /** - * @param MessageDispatch $messageDispatch + * @param ActionEvent $actionEvent */ - public function onRouteMessage(MessageDispatch $messageDispatch) + public function onRouteMessage(ActionEvent $actionEvent) { - if (is_null($messageDispatch->getMessageName()) && $messageDispatch->isLoggingEnabled()) { - $messageDispatch->getLogger()->notice( - sprintf("%s: MessageDispatch contains no message name", get_called_class()) - ); + $messageName = (string)$actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME); + + if (empty($messageName)) { return; } - if (!isset($this->messageMap[$messageDispatch->getMessageName()])) { - if ($messageDispatch->isLoggingEnabled()) { - $messageDispatch->getLogger()->debug( - sprintf( - "%s: Cannot route %s to a handler. No handler registered for message.", - get_called_class(), - $messageDispatch->getMessageName() - ) - ); - } + if (!isset($this->messageMap[$messageName])) { return; } - $handler = $this->messageMap[$messageDispatch->getMessageName()]; + $handler = $this->messageMap[$messageName]; - if ($messageDispatch instanceof CommandDispatch) { - $messageDispatch->setCommandHandler($handler); - } elseif ($messageDispatch instanceof QueryDispatch) { - $messageDispatch->setFinder($handler); - } else { - $messageDispatch->setParam('message-handler', $handler); - } + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); } } \ No newline at end of file diff --git a/src/Plugin/ServiceLocatorPlugin.php b/src/Plugin/ServiceLocatorPlugin.php new file mode 100644 index 0000000..041b42d --- /dev/null +++ b/src/Plugin/ServiceLocatorPlugin.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 23.09.14 - 20:56 + */ + +namespace Prooph\ServiceBus\Plugin; + +use Interop\Container\ContainerInterface; +use Prooph\Common\Event\ActionEvent; +use Prooph\Common\Event\ActionEventEmitter; +use Prooph\Common\Event\ActionEventListenerAggregate; +use Prooph\Common\Event\DetachAggregateHandlers; +use Prooph\ServiceBus\MessageBus; + +/** + * Class ServiceLocatorPlugin + * + * This plugin can be used to lazy load message handlers. + * Initialize it with a Interop\Container\ContainerInterface + * and route your messages to the service id only. + * + * @package Prooph\ServiceBus\ServiceLocator + * @author Alexander Miertsch + */ +class ServiceLocatorPlugin implements ActionEventListenerAggregate +{ + use DetachAggregateHandlers; + + /** + * @var ContainerInterface + */ + protected $serviceLocator; + + /** + * @param ContainerInterface $serviceLocator + */ + public function __construct(ContainerInterface $serviceLocator) + { + $this->serviceLocator = $serviceLocator; + } + /** + * @param ActionEventEmitter $events + * + * @return void + */ + public function attach(ActionEventEmitter $events) + { + $this->trackHandler($events->attachListener(MessageBus::EVENT_LOCATE_HANDLER, array($this, 'onLocateMessageHandler'))); + } + + public function onLocateMessageHandler(ActionEvent $actionEvent) + { + $messageHandlerAlias = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); + + if (is_string($messageHandlerAlias) && $this->serviceLocator->has($messageHandlerAlias)) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $this->serviceLocator->get($messageHandlerAlias)); + } + } +} + \ No newline at end of file diff --git a/src/Prooph/ServiceBus/CommandBus.php b/src/Prooph/ServiceBus/CommandBus.php deleted file mode 100644 index f11682e..0000000 --- a/src/Prooph/ServiceBus/CommandBus.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 14.09.14 - 16:32 - */ - -namespace Prooph\ServiceBus; - -use Prooph\ServiceBus\Exception\CommandDispatchException; -use Prooph\ServiceBus\Exception\RuntimeException; -use Prooph\ServiceBus\Process\CommandDispatch; - -/** - * Class CommandBus - * - * @package Prooph\ServiceBus - * @author Alexander Miertsch - */ -class CommandBus extends MessageBus -{ - /** - * @param mixed $command - * @return void - * @throws Exception\CommandDispatchException - */ - public function dispatch($command) - { - $commandDispatch = CommandDispatch::initializeWith($command, $this); - - if (! is_null($this->logger)) { - $commandDispatch->useLogger($this->logger); - } - - try { - $this->trigger($commandDispatch); - - if (is_null($commandDispatch->getCommandName())) { - $commandDispatch->setName(CommandDispatch::DETECT_MESSAGE_NAME); - - $this->trigger($commandDispatch); - } - - if (is_null($commandDispatch->getCommandHandler())) { - $commandDispatch->setName(CommandDispatch::ROUTE); - - $this->trigger($commandDispatch); - } - - if (is_null($commandDispatch->getCommandHandler())) { - throw new RuntimeException(sprintf( - "CommandBus was not able to identify a CommandHandler for command %s", - (is_object($command))? get_class($command) : json_encode($command) - )); - } - - if (is_string($commandDispatch->getCommandHandler())) { - $commandDispatch->setName(CommandDispatch::LOCATE_HANDLER); - - $this->trigger($commandDispatch); - } - - $commandDispatch->setName(CommandDispatch::INVOKE_HANDLER); - - $this->trigger($commandDispatch); - - } catch (\Exception $ex) { - $failedPhase = $commandDispatch->getName(); - - $commandDispatch->setException($ex); - $this->triggerError($commandDispatch); - $this->triggerFinalize($commandDispatch); - - //Check if a listener has removed the exception to indicate that it was able to handle it - if ($ex = $commandDispatch->getException()) { - $commandDispatch->setName($failedPhase); - throw CommandDispatchException::failed($commandDispatch, $ex); - } - - return; - } - - $this->triggerFinalize($commandDispatch); - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/EventBus.php b/src/Prooph/ServiceBus/EventBus.php deleted file mode 100644 index 070356f..0000000 --- a/src/Prooph/ServiceBus/EventBus.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 16.09.14 - 21:33 - */ - -namespace Prooph\ServiceBus; - -use Prooph\ServiceBus\Exception\EventDispatchException; -use Prooph\ServiceBus\Process\EventDispatch; -use Zend\EventManager\EventManagerInterface; - -class EventBus extends MessageBus -{ - /** - * @param mixed $event - * @throws Exception\EventDispatchException - */ - public function dispatch($event) - { - $eventDispatch = EventDispatch::initializeWith($event, $this); - - if (! is_null($this->logger)) { - $eventDispatch->useLogger($this->logger); - } - - try { - $this->trigger($eventDispatch); - - if (is_null($eventDispatch->getEventName())) { - $eventDispatch->setName(EventDispatch::DETECT_MESSAGE_NAME); - - $this->trigger($eventDispatch); - } - - $eventDispatch->setName(EventDispatch::ROUTE); - - $this->trigger($eventDispatch); - - foreach ($eventDispatch->getEventListeners() as $eventListener) { - - $eventDispatch->setCurrentEventListener($eventListener); - - if (is_string($eventListener)) { - $eventDispatch->setName(EventDispatch::LOCATE_LISTENER); - - $this->trigger($eventDispatch); - } - - $eventDispatch->setName(EventDispatch::INVOKE_LISTENER); - - $this->trigger($eventDispatch); - } - - } catch (\Exception $ex) { - $failedPhase = $eventDispatch->getName(); - $eventDispatch->setException($ex); - - $this->triggerError($eventDispatch); - $this->triggerFinalize($eventDispatch); - - //Check if a listener has removed the exception to indicate that it was able to handle it - if ($ex = $eventDispatch->getException()) { - $eventDispatch->setName($failedPhase); - throw EventDispatchException::failed($eventDispatch, $ex); - } - - return; - - } - - $this->triggerFinalize($eventDispatch); - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Exception/EventDispatchException.php b/src/Prooph/ServiceBus/Exception/EventDispatchException.php deleted file mode 100644 index 88dc891..0000000 --- a/src/Prooph/ServiceBus/Exception/EventDispatchException.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 16.09.14 - 22:02 - */ - -namespace Prooph\ServiceBus\Exception; - -use Prooph\ServiceBus\Process\EventDispatch; - -class EventDispatchException extends RuntimeException -{ - /** - * @var EventDispatch - */ - protected $eventDispatch; - - /** - * @param EventDispatch $eventDispatch - * @param \Exception $previousException - * @return EventDispatchException - */ - public static function failed(EventDispatch $eventDispatch, \Exception $previousException = null) - { - $ex = new self( - sprintf( - "Event dispatch failed during %s phase.%s", - $eventDispatch->getName(), - (is_null($previousException))? '' : ' Error: ' . $previousException->getMessage() - ), - 422, - $previousException - ); - - $ex->setFailedDispatch($eventDispatch); - - return $ex; - } - - /** - * @return EventDispatch - */ - public function getFailedCommandDispatch() - { - return $this->eventDispatch; - } - - protected function setFailedDispatch(EventDispatch $eventDispatch) - { - $this->eventDispatch = $eventDispatch; - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Exception/QueryDispatchException.php b/src/Prooph/ServiceBus/Exception/QueryDispatchException.php deleted file mode 100644 index f32bd48..0000000 --- a/src/Prooph/ServiceBus/Exception/QueryDispatchException.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 22.05.15 - 22:16 - */ -namespace Prooph\ServiceBus\Exception; - - -use Prooph\ServiceBus\Process\QueryDispatch; - -final class QueryDispatchException extends RuntimeException -{ - /** - * @var QueryDispatch - */ - protected $queryDispatch; - - /** - * @param QueryDispatch $queryDispatch - * @param \Exception $previousException - * @return CommandDispatchException - */ - public static function failed(QueryDispatch $queryDispatch, \Exception $previousException = null) - { - $ex = new self( - sprintf( - "Query dispatch failed during %s phase.%s", - $queryDispatch->getName(), - (is_null($previousException))? '' : ' Error: ' . $previousException->getMessage() - ), - 422, - $previousException - ); - - $ex->setFailedDispatch($queryDispatch); - - return $ex; - } - - /** - * @return QueryDispatch - */ - public function getFailedQueryDispatch() - { - return $this->queryDispatch; - } - - protected function setFailedDispatch(QueryDispatch $queryDispatch) - { - $this->queryDispatch = $queryDispatch; - } -} \ No newline at end of file diff --git a/src/Prooph/ServiceBus/InvokeStrategy/AbstractInvokeStrategy.php b/src/Prooph/ServiceBus/InvokeStrategy/AbstractInvokeStrategy.php deleted file mode 100644 index a761960..0000000 --- a/src/Prooph/ServiceBus/InvokeStrategy/AbstractInvokeStrategy.php +++ /dev/null @@ -1,89 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 16.09.14 - 20:52 - */ - -namespace Prooph\ServiceBus\InvokeStrategy; - -use Prooph\Common\Event\ActionEvent; -use Prooph\Common\Event\ActionEventDispatcher; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; -use Prooph\ServiceBus\Process\CommandDispatch; -use Prooph\ServiceBus\Process\EventDispatch; - -/** - * Class AbstractInvokeStrategy - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -abstract class AbstractInvokeStrategy implements ActionEventListenerAggregate -{ - use DetachAggregateHandlers; - - protected $priority = 0; - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - * @return bool - */ - abstract protected function canInvoke($aHandler, $aCommandOrEvent); - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - */ - abstract protected function invoke($aHandler, $aCommandOrEvent); - - /** - * Attach one or more listeners - * - * @param ActionEventDispatcher $events - * - * @return void - */ - public function attach(ActionEventDispatcher $events) - { - $this->trackHandler($events->attachListener(CommandDispatch::INVOKE_HANDLER, array($this, 'onInvoke'), $this->priority)); - $this->trackHandler($events->attachListener(EventDispatch::INVOKE_LISTENER, array($this, 'onInvoke'), $this->priority)); - } - - /** - * @param ActionEvent $e - */ - public function onInvoke(ActionEvent $e) - { - $message = null; - $handler = null; - - if ($e instanceof CommandDispatch) { - $message = $e->getCommand(); - $handler = $e->getCommandHandler(); - }else if ($e instanceof EventDispatch) { - $message = $e->getEvent(); - $handler = $e->getCurrentEventListener(); - } else { - return; - } - - if ($this->canInvoke($handler, $message)) { - $this->invoke($handler, $message); - if ($e->isLoggingEnabled()) { - $e->getLogger()->info(sprintf( - "Message %s invoked on handler %s", - is_object($message)? get_class($message) : json_encode($message), - is_object($handler)? get_class($handler) : json_encode($handler) - )); - } - } - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/InvokeStrategy/CallbackStrategy.php b/src/Prooph/ServiceBus/InvokeStrategy/CallbackStrategy.php deleted file mode 100644 index 2306fce..0000000 --- a/src/Prooph/ServiceBus/InvokeStrategy/CallbackStrategy.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 08.03.14 - 22:44 - */ - -namespace Prooph\ServiceBus\InvokeStrategy; - -/** - * Class CallbackStrategy - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -class CallbackStrategy extends AbstractInvokeStrategy -{ - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - * @return bool - */ - public function canInvoke($aHandler, $aCommandOrEvent) - { - return is_callable($aHandler); - } - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - */ - public function invoke($aHandler, $aCommandOrEvent) - { - call_user_func($aHandler, $aCommandOrEvent); - } -} diff --git a/src/Prooph/ServiceBus/InvokeStrategy/ForwardToRemoteMessageDispatcherStrategy.php b/src/Prooph/ServiceBus/InvokeStrategy/ForwardToRemoteMessageDispatcherStrategy.php deleted file mode 100644 index 12dc32a..0000000 --- a/src/Prooph/ServiceBus/InvokeStrategy/ForwardToRemoteMessageDispatcherStrategy.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 16.09.14 - 22:51 - */ - -namespace Prooph\ServiceBus\InvokeStrategy; - -use Prooph\Common\Messaging\RemoteMessage; -use Prooph\ServiceBus\Message\ProophDomainMessageToRemoteMessageTranslator; -use Prooph\ServiceBus\Message\RemoteMessageDispatcher; -use Prooph\ServiceBus\Message\ToRemoteMessageTranslator; - -/** - * Class ForwardToRemoteMessageDispatcherStrategy - * - * This invoke strategy comes into play when a domain message should be dispatched to a - * RemoteMessageDispatcher. The strategy translates the domain message to a Prooph\Common\Messaging\RemoteMessage - * with the help of a ToRemoteMessageTranslator and forwards the RemoteMessage to the RemoteMessageDispatcher. - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -class ForwardToRemoteMessageDispatcherStrategy extends AbstractInvokeStrategy -{ - /** - * @var ToRemoteMessageTranslator - */ - protected $messageTranslator; - - /** - * @param ToRemoteMessageTranslator $messageTranslator - */ - public function __construct(ToRemoteMessageTranslator $messageTranslator = null) - { - $this->messageTranslator = $messageTranslator; - } - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - * @return bool - */ - protected function canInvoke($aHandler, $aCommandOrEvent) - { - if ($aHandler instanceof RemoteMessageDispatcher) { - if ($aCommandOrEvent instanceof RemoteMessage - || $this->getMessageTranslator()->canTranslateToRemoteMessage($aCommandOrEvent)) { - return true; - } - } - - return false; - } - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - */ - protected function invoke($aHandler, $aCommandOrEvent) - { - $message = $aCommandOrEvent; - - if (! $message instanceof RemoteMessage) { - $message = $this->getMessageTranslator()->translateToRemoteMessage($aCommandOrEvent); - } - - $aHandler->dispatch($message); - } - - /** - * @return ToRemoteMessageTranslator - */ - protected function getMessageTranslator() - { - if (is_null($this->messageTranslator)) { - $this->messageTranslator = new ProophDomainMessageToRemoteMessageTranslator(); - } - - return $this->messageTranslator; - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/InvokeStrategy/ForwardToRemoteQueryDispatcherStrategy.php b/src/Prooph/ServiceBus/InvokeStrategy/ForwardToRemoteQueryDispatcherStrategy.php deleted file mode 100644 index ab6ede3..0000000 --- a/src/Prooph/ServiceBus/InvokeStrategy/ForwardToRemoteQueryDispatcherStrategy.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 5/23/15 - 7:35 PM - */ -namespace Prooph\ServiceBus\InvokeStrategy; - -use Prooph\Common\Event\ActionEventDispatcher; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; -use Prooph\Common\Messaging\RemoteMessage; -use Prooph\ServiceBus\Message\ProophDomainMessageToRemoteMessageTranslator; -use Prooph\ServiceBus\Message\RemoteQueryDispatcher; -use Prooph\ServiceBus\Message\ToRemoteMessageTranslator; -use Prooph\ServiceBus\Process\QueryDispatch; - -/** - * Class ForwardToRemoteQueryDispatcherStrategy - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -class ForwardToRemoteQueryDispatcherStrategy implements ActionEventListenerAggregate -{ - use DetachAggregateHandlers; - - /** - * @var ToRemoteMessageTranslator - */ - protected $messageTranslator; - - /** - * @param ToRemoteMessageTranslator $messageTranslator - */ - public function __construct(ToRemoteMessageTranslator $messageTranslator = null) - { - $this->messageTranslator = $messageTranslator; - } - - /** - * @param ActionEventDispatcher $dispatcher - */ - public function attach(ActionEventDispatcher $dispatcher) - { - $this->trackHandler($dispatcher->attachListener(QueryDispatch::INVOKE_FINDER, [$this, 'onInvokeFinder'])); - } - - /** - * @param QueryDispatch $queryDispatch - */ - public function onInvokeFinder(QueryDispatch $queryDispatch) - { - $finder = $queryDispatch->getFinder(); - $query = $queryDispatch->getQuery(); - - if ($finder instanceof RemoteQueryDispatcher - && ($query instanceof RemoteMessage || $this->getMessageTranslator()->canTranslateToRemoteMessage($query))) { - - $query = $queryDispatch->getQuery(); - - if (! $query instanceof RemoteMessage ) { - $query = $this->getMessageTranslator()->translateToRemoteMessage($query); - } - - $finder->dispatchQuery($query, $queryDispatch->getDeferred()); - } - } - - /** - * @return ToRemoteMessageTranslator - */ - protected function getMessageTranslator() - { - if (is_null($this->messageTranslator)) { - $this->messageTranslator = new ProophDomainMessageToRemoteMessageTranslator(); - } - - return $this->messageTranslator; - } -} \ No newline at end of file diff --git a/src/Prooph/ServiceBus/InvokeStrategy/OnEventStrategy.php b/src/Prooph/ServiceBus/InvokeStrategy/OnEventStrategy.php deleted file mode 100644 index 6eb4166..0000000 --- a/src/Prooph/ServiceBus/InvokeStrategy/OnEventStrategy.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 11.03.14 - 21:40 - */ - -namespace Prooph\ServiceBus\InvokeStrategy; -use Prooph\Common\Messaging\DomainEvent; -use Prooph\Common\Messaging\HasMessageName; - -/** - * Class OnEventStrategy - * - * @package Prooph\ServiceBus\InvokeStrategy - * @author Alexander Miertsch - */ -class OnEventStrategy extends AbstractInvokeStrategy -{ - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - * @return bool - */ - public function canInvoke($aHandler, $aCommandOrEvent) - { - if (! $aCommandOrEvent instanceof DomainEvent) { - return false; - } - - $handleMethod = 'on' . $this->determineEventName($aCommandOrEvent); - - return method_exists($aHandler, $handleMethod); - } - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - */ - public function invoke($aHandler, $aCommandOrEvent) - { - $handleMethod = 'on' . $this->determineEventName($aCommandOrEvent); - - $aHandler->{$handleMethod}($aCommandOrEvent); - } - - /** - * @param mixed $anEvent - * @return string - */ - protected function determineEventName($anEvent) - { - $eventName = ($anEvent instanceof HasMessageName)? $anEvent->messageName() : get_class($anEvent); - return join('', array_slice(explode('\\', $eventName), -1)); - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Message/FromRemoteMessageTranslator.php b/src/Prooph/ServiceBus/Message/FromRemoteMessageTranslator.php deleted file mode 100644 index ff4aa05..0000000 --- a/src/Prooph/ServiceBus/Message/FromRemoteMessageTranslator.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 23.09.14 - 19:23 - */ - -namespace Prooph\ServiceBus\Message; - -use Prooph\Common\Event\ActionEventDispatcher; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; -use Prooph\Common\Messaging\Command; -use Prooph\Common\Messaging\DomainEvent; -use Prooph\Common\Messaging\MessageHeader; -use Prooph\Common\Messaging\Query; -use Prooph\Common\Messaging\RemoteMessage; -use Prooph\ServiceBus\Process\MessageDispatch; - -/** - * Class FromRemoteMessageTranslator - * - * If the incoming message is of type Prooph\Common\Messaging\RemoteMessage - * it is translated to a Prooph\Common\Messaging\DomainMessage respecting the Prooph\Common\Messaging\MessageHeader::TYPE_* - * and if RemoteMessage::name can be resolved to an existing class it is used instead of the base classes. - * - * Note: A custom command or domain event class MUST implement a static fromRemoteMessage factory method otherwise - * the translator will break! - * - * @package Prooph\ServiceBus\Message - * @author Alexander Miertsch - */ -class FromRemoteMessageTranslator implements ActionEventListenerAggregate -{ - use DetachAggregateHandlers; - - /** - * Plugin listens on MessageDispatch::INITIALIZE with priority 100 - * - * @param MessageDispatch $messageDispatch - */ - public function __invoke(MessageDispatch $messageDispatch) - { - $message = $messageDispatch->getMessage(); - - if ($message instanceof RemoteMessage) { - $message = $this->translateFromRemoteMessage($message); - - $messageDispatch->setMessage($message); - } - } - - /** - * @param ActionEventDispatcher $events - * - * @return void - */ - public function attach(ActionEventDispatcher $events) - { - $this->trackHandler($events->attachListener(MessageDispatch::INITIALIZE, $this, 100)); - } - - /** - * @param RemoteMessage $remoteMessage - * @return mixed - */ - public function translateFromRemoteMessage(RemoteMessage $remoteMessage) - { - $defaultMessageClass = ($remoteMessage->header()->type() === MessageHeader::TYPE_COMMAND) - ? Command::class : - ($remoteMessage->header()->type() === MessageHeader::TYPE_QUERY) - ? Query::class : DomainEvent::class; - - $messageClass = (class_exists($remoteMessage->name()))? $remoteMessage->name() : $defaultMessageClass; - - return $messageClass::fromRemoteMessage($remoteMessage); - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Message/InMemoryRemoteMessageDispatcher.php b/src/Prooph/ServiceBus/Message/InMemoryRemoteMessageDispatcher.php deleted file mode 100644 index f2e85c9..0000000 --- a/src/Prooph/ServiceBus/Message/InMemoryRemoteMessageDispatcher.php +++ /dev/null @@ -1,93 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 10.03.14 - 20:35 - */ - -namespace Prooph\ServiceBus\Message; - -use Prooph\Common\Messaging\MessageHeader; -use Prooph\Common\Messaging\RemoteMessage; -use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\QueryBus; -use React\Promise\Deferred; - -/** - * Class InMemoryRemoteMessageDispatcher - * - * @package Prooph\ServiceBus\Message - * @author Alexander Miertsch - */ -class InMemoryRemoteMessageDispatcher implements RemoteMessageDispatcher, RemoteQueryDispatcher -{ - /** - * @var CommandBus - */ - protected $commandBus; - - /** - * @var EventBus - */ - protected $eventBus; - - /** - * @var QueryBus - */ - protected $queryBus; - - /** - * @param CommandBus $commandBus - * @param EventBus $eventBus - * @param QueryBus $queryBus - */ - public function __construct(CommandBus $commandBus, EventBus $eventBus, QueryBus $queryBus = null) - { - $this->commandBus = $commandBus; - $this->eventBus = $eventBus; - $this->queryBus = $queryBus; - } - - /** - * @param RemoteMessage $message - * @throws \Exception If handling of message fails - * @return void - */ - public function dispatch(RemoteMessage $message) - { - - if ($message->header()->type() === MessageHeader::TYPE_COMMAND) { - - $this->commandBus->dispatch($message); - - return; - } - - if ($message->header()->type() === MessageHeader::TYPE_EVENT) { - - $this->eventBus->dispatch($message); - - return; - } - } - - /** - * @param RemoteMessage $queryMessage - * @param Deferred $deferred - * @throws \RuntimeException - * @return void - */ - public function dispatchQuery(RemoteMessage $queryMessage, Deferred $deferred) - { - if ($this->queryBus === null) { - throw new \RuntimeException("Unable to dispatch query message. No QueryBus set"); - } - - $deferred->resolve($this->queryBus->dispatch($queryMessage)); - } -} diff --git a/src/Prooph/ServiceBus/Message/ProophDomainMessageToRemoteMessageTranslator.php b/src/Prooph/ServiceBus/Message/ProophDomainMessageToRemoteMessageTranslator.php deleted file mode 100644 index 06a7121..0000000 --- a/src/Prooph/ServiceBus/Message/ProophDomainMessageToRemoteMessageTranslator.php +++ /dev/null @@ -1,52 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 11.03.14 - 22:43 - */ - -namespace Prooph\ServiceBus\Message; - -use Prooph\Common\Messaging\DomainMessage; -use Prooph\Common\Messaging\RemoteMessage; -use Prooph\ServiceBus\Exception\RuntimeException; - -/** - * Class ProophDomainMessageToRemoteMessageTranslator - * - * @package Prooph\ServiceBus\Message - * @author Alexander Miertsch - */ -class ProophDomainMessageToRemoteMessageTranslator implements ToRemoteMessageTranslator -{ - /** - * @param $domainMessage - * @return bool - */ - public function canTranslateToRemoteMessage($domainMessage) - { - if ($domainMessage instanceof DomainMessage) return true; - return false; - } - - /** - * @param mixed $domainMessage - * @throws \Prooph\ServiceBus\Exception\RuntimeException - * @return RemoteMessage - */ - public function translateToRemoteMessage($domainMessage) - { - if ($domainMessage instanceof DomainMessage) return $domainMessage->toRemoteMessage(); - - throw new RuntimeException( - sprintf( - "Can not build remote message. Invalid domain message type %s given", - is_object($domainMessage)? get_class($domainMessage) : gettype($domainMessage) - ) - ); - } -} diff --git a/src/Prooph/ServiceBus/Message/RemoteMessageDispatcher.php b/src/Prooph/ServiceBus/Message/RemoteMessageDispatcher.php deleted file mode 100644 index 90f3dba..0000000 --- a/src/Prooph/ServiceBus/Message/RemoteMessageDispatcher.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 08.03.14 - 15:27 - */ - -namespace Prooph\ServiceBus\Message; - -use Prooph\Common\Messaging\RemoteMessage; - -/** - * Interface RemoteMessageDispatcher - * - * @package Prooph\ServiceBus\Message - * @author Alexander Miertsch - */ -interface RemoteMessageDispatcher -{ - /** - * @param RemoteMessage $message - * @return void - */ - public function dispatch(RemoteMessage $message); -} \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Message/RemoteQueryDispatcher.php b/src/Prooph/ServiceBus/Message/RemoteQueryDispatcher.php deleted file mode 100644 index e82b9af..0000000 --- a/src/Prooph/ServiceBus/Message/RemoteQueryDispatcher.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 5/23/15 - 6:58 PM - */ - -namespace Prooph\ServiceBus\Message; -use Prooph\Common\Messaging\RemoteMessage; -use React\Promise\Deferred; - -/** - * Interface RemoteQueryDispatcher - * - * A remote query dispatcher is capable of dispatching a query message to a remote system - * and resolve the given deferred with the answer of the remote system. - * - * @package Prooph\ServiceBus\Message - * @author Alexander Miertsch - */ -interface RemoteQueryDispatcher -{ - /** - * @param RemoteMessage $queryMessage - * @param Deferred $deferred - * @return void - */ - public function dispatchQuery(RemoteMessage $queryMessage, Deferred $deferred); -} \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Message/ToRemoteMessageTranslator.php b/src/Prooph/ServiceBus/Message/ToRemoteMessageTranslator.php deleted file mode 100644 index a582e0a..0000000 --- a/src/Prooph/ServiceBus/Message/ToRemoteMessageTranslator.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 11.03.14 - 21:07 - */ - -namespace Prooph\ServiceBus\Message; - -use Prooph\Common\Messaging\RemoteMessage; - -/** - * Interface ToRemoteMessageTranslator - * - * @package Prooph\ServiceBus\Message - * @author Alexander Miertsch - */ -interface ToRemoteMessageTranslator -{ - /** - * @param $domainMessage - * @return bool - */ - public function canTranslateToRemoteMessage($domainMessage); - - /** - * @param mixed $domainMessage - * @return RemoteMessage - */ - public function translateToRemoteMessage($domainMessage); -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/MessageBus.php b/src/Prooph/ServiceBus/MessageBus.php deleted file mode 100644 index 9ad5632..0000000 --- a/src/Prooph/ServiceBus/MessageBus.php +++ /dev/null @@ -1,178 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 13.01.15 - 14:59 - */ - -namespace Prooph\ServiceBus; - -use Prooph\Common\Event\ActionEventDispatcher; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\ListenerHandler; -use Prooph\Common\Event\ProophActionEventDispatcher; -use Prooph\Common\Event\ZF2\Zf2ActionEventDispatcher; -use Prooph\ServiceBus\Exception\RuntimeException; -use Prooph\ServiceBus\Process\MessageDispatch; -use Psr\Log\LoggerInterface; - -/** - * Class MessageBus - * - * Base class for command and event bus implementations - * - * @package Prooph\ServiceBus - * @author Alexander Miertsch - */ -abstract class MessageBus -{ - /** - * @var LoggerInterface - */ - protected $logger; - - /** - * @var ActionEventDispatcher - */ - protected $events; - - /** - * @param mixed $message - * @return mixed|void depends on the bus type - */ - abstract public function dispatch($message); - - /** - * @param ActionEventListenerAggregate|LoggerInterface $plugin - * @return $this - * @throws Exception\RuntimeException - */ - public function utilize($plugin) - { - if ($plugin instanceof ActionEventListenerAggregate) { - $plugin->attach($this->getActionEventDispatcher()); - } else if ($plugin instanceof LoggerInterface) { - $this->logger = $plugin; - } else { - throw new RuntimeException( - sprintf( - "%s cannot use plugin of type %s.", - get_called_class(), - (is_object($plugin))? get_class($plugin) : gettype($plugin) - ) - ); - } - - return $this; - } - - /** - * @param ActionEventListenerAggregate|LoggerInterface $plugin - * @return $this - * @throws Exception\RuntimeException - */ - public function deactivate($plugin) - { - if ($plugin instanceof ActionEventListenerAggregate) { - $plugin->detach($this->getActionEventDispatcher()); - } else if ($plugin instanceof LoggerInterface) { - $this->logger = null; - } else { - throw new RuntimeException( - sprintf( - "%s cannot detach plugin of type %s.", - get_called_class(), - (is_object($plugin))? get_class($plugin) : gettype($plugin) - ) - ); - } - - return $this; - } - - /** - * @param string $eventName - * @param callable $listener - * @param int $priority - * @return ListenerHandler - */ - public function on($eventName, $listener, $priority = 1) - { - return $this->getActionEventDispatcher()->attachListener($eventName, $listener, $priority); - } - - /** - * @param ListenerHandler $listenerHandler - * @return bool - */ - public function off(ListenerHandler $listenerHandler) - { - return $this->getActionEventDispatcher()->detachListener($listenerHandler); - } - - /** - * @param MessageDispatch $messageDispatch - * @throws Exception\RuntimeException - */ - protected function trigger(MessageDispatch $messageDispatch) - { - $this->getActionEventDispatcher()->dispatch($messageDispatch); - - if ($messageDispatch->propagationIsStopped()) { - throw new RuntimeException("Dispatch has stopped unexpectedly."); - } - } - - /** - * @param MessageDispatch $messageDispatch - */ - protected function triggerError(MessageDispatch $messageDispatch) - { - $messageDispatch->setName(MessageDispatch::HANDLE_ERROR); - - $this->getActionEventDispatcher()->dispatch($messageDispatch); - } - - /** - * @param MessageDispatch $messageDispatch - */ - protected function triggerFinalize(MessageDispatch $messageDispatch) - { - $messageDispatch->setName(MessageDispatch::FINALIZE); - - $this->getActionEventDispatcher()->dispatch($messageDispatch); - } - - - /** - * Inject an ActionEventDispatcher instance - * - * @param ActionEventDispatcher $actionEventDispatcher - * @return void - */ - public function setActionEventDispatcher(ActionEventDispatcher $actionEventDispatcher) - { - $this->events = $actionEventDispatcher; - } - - /** - * Retrieve the action event dispatcher - * - * Lazy-loads a dispatcher if none is registered. - * - * @return ActionEventDispatcher - */ - public function getActionEventDispatcher() - { - if (is_null($this->events)) { - $this->setActionEventDispatcher(new ProophActionEventDispatcher()); - } - - return $this->events; - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Process/CommandDispatch.php b/src/Prooph/ServiceBus/Process/CommandDispatch.php deleted file mode 100644 index ac699b0..0000000 --- a/src/Prooph/ServiceBus/Process/CommandDispatch.php +++ /dev/null @@ -1,114 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 14.09.14 - 16:35 - */ - -namespace Prooph\ServiceBus\Process; - -use Prooph\Common\Messaging\HasMessageName; -use Prooph\Common\Messaging\MessageHeader; -use Prooph\Common\Messaging\RemoteMessage; -use Prooph\ServiceBus\CommandBus; - -/** - * Class CommandDispatch - * - * @package Prooph\ServiceBus\Process - * @author Alexander Miertsch - */ -class CommandDispatch extends MessageDispatch -{ - const LOCATE_HANDLER = "locate-handler"; - const INVOKE_HANDLER = "invoke-handler"; - - /** - * @param mixed $command - * @param CommandBus $commandBus - * @throws \InvalidArgumentException - * @return CommandDispatch - */ - public static function initializeWith($command, CommandBus $commandBus) - { - $instance = new static(static::INITIALIZE, $commandBus, array('message' => $command)); - - if ($command instanceof HasMessageName) { - $instance->setMessageName($command->messageName()); - } - - if ($command instanceof RemoteMessage) { - if ($command->header()->type() !== MessageHeader::TYPE_COMMAND) { - throw new \InvalidArgumentException( - sprintf("Message %s cannot be handled. Message is not of type command.", $command->name()) - ); - } - - $instance->setMessageName($command->name()); - } - - return $instance; - } - - /** - * @return string|null - */ - public function getCommandName() - { - return $this->getParam('message-name'); - } - - /** - * @param string $commandName - * @throws \InvalidArgumentException - */ - public function setCommandName($commandName) - { - $this->setMessageName($commandName); - } - - /** - * @return mixed - */ - public function getCommand() - { - return $this->getMessage(); - } - - /** - * @param mixed $command - */ - public function setCommand($command) - { - $this->setMessage($command); - } - - /** - * @return null|string|object|callable - */ - public function getCommandHandler() - { - return $this->getParam('command-handler'); - } - - /** - * @param string|object|callable $commandHandler - * @throws \InvalidArgumentException - */ - public function setCommandHandler($commandHandler) - { - if (! is_string($commandHandler) && ! is_object($commandHandler) && ! is_callable($commandHandler)) { - throw new \InvalidArgumentException(sprintf( - "Invalid command handler provided. Expected type is string, object or callable but type of %s given.", - gettype($commandHandler) - )); - } - - $this->setParam("command-handler", $commandHandler); - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Process/EventDispatch.php b/src/Prooph/ServiceBus/Process/EventDispatch.php deleted file mode 100644 index 0133f66..0000000 --- a/src/Prooph/ServiceBus/Process/EventDispatch.php +++ /dev/null @@ -1,174 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 16.09.14 - 21:30 - */ - -namespace Prooph\ServiceBus\Process; - -use Prooph\Common\Messaging\HasMessageName; -use Prooph\Common\Messaging\MessageHeader; -use Prooph\Common\Messaging\RemoteMessage; -use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\Exception\RuntimeException; - -/** - * Class EventDispatch - * - * @package Prooph\ServiceBus\Process - * @author Alexander Miertsch - */ -class EventDispatch extends MessageDispatch -{ - const LOCATE_LISTENER = "locate-listener"; - const INVOKE_LISTENER = "invoke-listener"; - - /** - * @param mixed $event - * @param EventBus $eventBus - * @throws \InvalidArgumentException - * @return EventDispatch - */ - public static function initializeWith($event, EventBus $eventBus) - { - $instance = new static(static::INITIALIZE, $eventBus, array('message' => $event)); - - if ($event instanceof HasMessageName) { - $instance->setMessageName($event->messageName()); - } - - if ($event instanceof RemoteMessage) { - if ($event->header()->type() !== MessageHeader::TYPE_EVENT) { - throw new \InvalidArgumentException( - sprintf("Message %s cannot be handled. Message is not of type event.", $event->name()) - ); - } - - $instance->setMessageName($event->name()); - } - - return $instance; - } - - /** - * @return string|null - */ - public function getEventName() - { - return $this->getMessageName(); - } - - /** - * @param string $eventName - * @throws \InvalidArgumentException - */ - public function setEventName($eventName) - { - $this->setMessageName($eventName); - } - - /** - * @return mixed - */ - public function getEvent() - { - return $this->getMessage(); - } - - /** - * @param mixed $event - */ - public function setEvent($event) - { - $this->setMessage($event); - } - - /** - * @return \ArrayObject(index => callable|string|object) - */ - public function getEventListeners() - { - //We cannot work with a simple default here, cause we need the exact reference to the listeners stack - $eventListeners = $this->getParam('event-listeners'); - - if (is_null($eventListeners)) { - $eventListeners = new \ArrayObject(); - - $this->setParam('event-listeners', $eventListeners); - } - - return $eventListeners; - } - - /** - * @param array(index => callable|string|object) $eventHandlerCollection - * @throws \Prooph\ServiceBus\Exception\RuntimeException - */ - public function setEventListeners(array $eventHandlerCollection) - { - if ($this->getName() === self::LOCATE_LISTENER || $this->getName() === self::INVOKE_LISTENER) { - throw new RuntimeException( - "Cannot set event listeners. EventDispatch is already in dispatching phase." - ); - } - - $this->setParam('event-listeners', new \ArrayObject()); - - foreach ($eventHandlerCollection as $eventHandler) { - $this->addEventListener($eventHandler); - } - } - - /** - * @param callable|string|object $eventListener - * @throws \Prooph\ServiceBus\Exception\RuntimeException - * @throws \InvalidArgumentException - */ - public function addEventListener($eventListener) - { - if ($this->getName() === self::LOCATE_LISTENER || $this->getName() === self::INVOKE_LISTENER) { - throw new RuntimeException( - "Cannot set event listeners. EventDispatch is already in dispatching phase." - ); - } - - if (! is_string($eventListener) && ! is_object($eventListener) && ! is_callable($eventListener)) { - throw new \InvalidArgumentException(sprintf( - "Invalid event listener provided. Expected type is string, object or callable but type of %s given.", - gettype($eventListener) - )); - } - - $this->getEventListeners()[] = $eventListener; - } - - /** - * @param callable|string|object $eventListener - * @throws \InvalidArgumentException - */ - public function setCurrentEventListener($eventListener) - { - if (! is_string($eventListener) && ! is_object($eventListener) && ! is_callable($eventListener)) { - throw new \InvalidArgumentException(sprintf( - "Invalid event listener provided. Expected type is string, object or callable but type of %s given.", - gettype($eventListener) - )); - } - - $this->setParam('current-event-listener', $eventListener); - } - - /** - * @return callable|string|object|null - */ - public function getCurrentEventListener() - { - return $this->getParam('current-event-listener'); - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Process/MessageDispatch.php b/src/Prooph/ServiceBus/Process/MessageDispatch.php deleted file mode 100644 index f03ef4a..0000000 --- a/src/Prooph/ServiceBus/Process/MessageDispatch.php +++ /dev/null @@ -1,127 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 13.01.15 - 15:06 - */ - -namespace Prooph\ServiceBus\Process; - -use Assert\Assertion; -use Prooph\Common\Event\ZF2\Zf2ActionEvent; -use Prooph\ServiceBus\Exception\RuntimeException; -use Psr\Log\LoggerInterface; - -/** - * Class MessageDispatch - * - * Basic implementation of a dispatch event used in the event-driven dispatch process of a message bus. - * - * @package Prooph\ServiceBus\Process - * @author Alexander Miertsch - */ -class MessageDispatch extends Zf2ActionEvent -{ - const INITIALIZE = "initialize"; - const DETECT_MESSAGE_NAME = "detect-message-name"; - const ROUTE = "route"; - const HANDLE_ERROR = "handle-error"; - const FINALIZE = "finalize"; - - /** - * @var LoggerInterface - */ - protected $logger; - - /** - * @var bool - */ - protected $isLoggingEnabled = false; - - /** - * @return null|string - */ - public function getMessageName() - { - return $this->getParam('message-name'); - } - - /** - * @param string $messageName - */ - public function setMessageName($messageName) - { - Assertion::string($messageName); - Assertion::notEmpty($messageName); - - $this->setParam('message-name', $messageName); - } - - /** - * @return mixed - */ - public function getMessage() - { - return $this->getParam('message'); - } - - /** - * @param mixed $message - */ - public function setMessage($message) - { - $this->setParam('message', $message); - } - - /** - * @throws \Prooph\ServiceBus\Exception\RuntimeException - * @return LoggerInterface - */ - public function getLogger() - { - if (is_null($this->logger)) { - throw new RuntimeException('No logger available'); - } - - return $this->logger; - } - - /** - * @param LoggerInterface $logger - */ - public function useLogger(LoggerInterface $logger) - { - $this->logger = $logger; - $this->isLoggingEnabled = true; - } - - /** - * @return bool - */ - public function isLoggingEnabled() - { - return $this->isLoggingEnabled; - } - - /** - * @param \Exception $exception - * @return $this - */ - public function setException(\Exception $exception) - { - $this->setParam('exception', $exception); - } - - /** - * @return null|\Exception - */ - public function getException() - { - return $this->getParam('exception'); - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/Process/QueryDispatch.php b/src/Prooph/ServiceBus/Process/QueryDispatch.php deleted file mode 100644 index 054499f..0000000 --- a/src/Prooph/ServiceBus/Process/QueryDispatch.php +++ /dev/null @@ -1,129 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 22.05.15 - 22:16 - */ -namespace Prooph\ServiceBus\Process; - -use Prooph\Common\Messaging\HasMessageName; -use Prooph\Common\Messaging\MessageHeader; -use Prooph\Common\Messaging\RemoteMessage; -use Prooph\ServiceBus\QueryBus; -use React\Promise\Deferred; - -/** - * Class QueryDispatch - * - * @package Prooph\ServiceBus\Process - * @author Alexander Miertsch - */ -class QueryDispatch extends MessageDispatch -{ - const LOCATE_FINDER = "locate-finder"; - const INVOKE_FINDER = "invoke-finder"; - - /** - * @var Deferred - */ - protected $deferred; - - /** - * @param mixed $query - * @param QueryBus $queryBus - * @param Deferred $deferred - * @throws \InvalidArgumentException - * @return QueryDispatch - */ - public static function initializeWith($query, QueryBus $queryBus, Deferred $deferred) - { - $instance = new static(static::INITIALIZE, $queryBus, array('message' => $query)); - - if ($query instanceof HasMessageName) { - $instance->setMessageName($query->messageName()); - } - - if ($query instanceof RemoteMessage) { - if ($query->header()->type() !== MessageHeader::TYPE_QUERY) { - throw new \InvalidArgumentException( - sprintf("Message %s cannot be handled. Message is not of type query.", $query->name()) - ); - } - - $instance->setMessageName($query->name()); - } - - $instance->deferred = $deferred; - - return $instance; - } - - /** - * @return string|null - */ - public function getQueryName() - { - return $this->getParam('message-name'); - } - - /** - * @param string $queryName - * @throws \InvalidArgumentException - */ - public function setQueryName($queryName) - { - $this->setMessageName($queryName); - } - - /** - * @return mixed - */ - public function getQuery() - { - return $this->getMessage(); - } - - /** - * @param mixed $query - */ - public function setQuery($query) - { - $this->setMessage($query); - } - - /** - * @return Deferred - */ - public function getDeferred() - { - return $this->deferred; - } - - /** - * @return null|string|object|callable - */ - public function getFinder() - { - return $this->getParam('query-finder'); - } - - /** - * @param string|object|callable $finder - * @throws \InvalidArgumentException - */ - public function setFinder($finder) - { - if (! is_string($finder) && ! is_object($finder) && ! is_callable($finder)) { - throw new \InvalidArgumentException(sprintf( - "Invalid finder provided. Expected type is string, object or callable but type of %s given.", - gettype($finder) - )); - } - - $this->setParam("query-finder", $finder); - } -} \ No newline at end of file diff --git a/src/Prooph/ServiceBus/QueryBus.php b/src/Prooph/ServiceBus/QueryBus.php deleted file mode 100644 index 9908e41..0000000 --- a/src/Prooph/ServiceBus/QueryBus.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 22.05.15 - 22:16 - */ -namespace Prooph\ServiceBus; - -use Prooph\ServiceBus\Exception\QueryDispatchException; -use Prooph\ServiceBus\Exception\RuntimeException; -use Prooph\ServiceBus\Process\QueryDispatch; -use React\Promise\Deferred; -use React\Promise\Promise; - -/** - * Class QueryBus - * - * The query bus dispatches a query message to a finder. - * The query is maybe dispatched async so the bus returns a promise - * which gets either resolved with the response of the finder or rejected with an exception. - * Additionally the finder can provide an update status but this is not guaranteed. - * - * @package Prooph\ServiceBus - * @author Alexander Miertsch - */ -class QueryBus extends MessageBus -{ - /** - * @param mixed $message - * @throws QueryDispatchException - * @return Promise - */ - public function dispatch($message) - { - $deferred = new Deferred(); - - $promise = $deferred->promise(); - - $queryDispatch = QueryDispatch::initializeWith($message, $this, $deferred); - - if (! is_null($this->logger)) { - $queryDispatch->useLogger($this->logger); - } - - try { - $this->trigger($queryDispatch); - - if (is_null($queryDispatch->getQueryName())) { - $queryDispatch->setName(QueryDispatch::DETECT_MESSAGE_NAME); - - $this->trigger($queryDispatch); - } - - if (is_null($queryDispatch->getFinder())) { - $queryDispatch->setName(QueryDispatch::ROUTE); - - $this->trigger($queryDispatch); - } - - if (is_null($queryDispatch->getFinder())) { - throw new RuntimeException(sprintf( - "QueryBus was not able to identify a Finder for query %s", - (is_object($message))? get_class($message) : json_encode($message) - )); - } - - if (is_string($queryDispatch->getFinder())) { - $queryDispatch->setName(QueryDispatch::LOCATE_FINDER); - - $this->trigger($queryDispatch); - } - - $queryDispatch->setName(QueryDispatch::INVOKE_FINDER); - - $this->trigger($queryDispatch); - - $this->triggerFinalize($queryDispatch); - } catch (\Exception $ex) { - $failedPhase = $queryDispatch->getName(); - - $queryDispatch->setException($ex); - $this->triggerError($queryDispatch); - $this->triggerFinalize($queryDispatch); - - //Check if a listener has removed the exception to indicate that it was able to handle it - if ($ex = $queryDispatch->getException()) { - $queryDispatch->setName($failedPhase); - $queryDispatch->getDeferred()->reject(QueryDispatchException::failed($queryDispatch, $ex)); - } - } - - return $promise; - } -} \ No newline at end of file diff --git a/src/Prooph/ServiceBus/ServiceLocator/ServiceLocatorProxy.php b/src/Prooph/ServiceBus/ServiceLocator/ServiceLocatorProxy.php deleted file mode 100644 index 453bc75..0000000 --- a/src/Prooph/ServiceBus/ServiceLocator/ServiceLocatorProxy.php +++ /dev/null @@ -1,72 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 23.09.14 - 20:56 - */ - -namespace Prooph\ServiceBus\ServiceLocator; - -use Prooph\Common\Event\ActionEventDispatcher; -use Prooph\Common\Event\ActionEventListenerAggregate; -use Prooph\Common\Event\DetachAggregateHandlers; -use Prooph\Common\ServiceLocator\ServiceLocator; -use Prooph\ServiceBus\Process\CommandDispatch; -use Prooph\ServiceBus\Process\EventDispatch; - -/** - * Class ServiceLocatorProxy - * - * @package Prooph\ServiceBus\ServiceLocator - * @author Alexander Miertsch - */ -class ServiceLocatorProxy implements ActionEventListenerAggregate -{ - use DetachAggregateHandlers; - - /** - * @var ServiceLocator - */ - protected $serviceLocator; - - /** - * @param ServiceLocator $serviceLocator - */ - public function __construct(ServiceLocator $serviceLocator) - { - $this->serviceLocator = $serviceLocator; - } - /** - * @param ActionEventDispatcher $events - * - * @return void - */ - public function attach(ActionEventDispatcher $events) - { - $this->trackHandler($events->attachListener(CommandDispatch::LOCATE_HANDLER, array($this, 'onLocateCommandHandler'))); - $this->trackHandler($events->attachListener(EventDispatch::LOCATE_LISTENER, array($this, 'onLocateEventListener'))); - } - - public function onLocateCommandHandler(CommandDispatch $commandDispatch) - { - $commandHandlerAlias = $commandDispatch->getCommandHandler(); - - if (is_string($commandHandlerAlias) && $this->serviceLocator->has($commandHandlerAlias)) { - $commandDispatch->setCommandHandler($this->serviceLocator->get($commandHandlerAlias)); - } - } - - public function onLocateEventListener(EventDispatch $eventDispatch) - { - $eventListenerAlias = $eventDispatch->getCurrentEventListener(); - - if (is_string($eventListenerAlias) && $this->serviceLocator->has($eventListenerAlias)) { - $eventDispatch->setCurrentEventListener($this->serviceLocator->get($eventListenerAlias)); - } - } -} - \ No newline at end of file diff --git a/src/Prooph/ServiceBus/StaticBusRegistry.php b/src/Prooph/ServiceBus/StaticBusRegistry.php deleted file mode 100644 index 04a0273..0000000 --- a/src/Prooph/ServiceBus/StaticBusRegistry.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 30.10.14 - 19:47 - */ - -namespace Prooph\ServiceBus; - -/** - * Class StaticBusRegistry - * - * The StaticBusRegistry can be used to set up globally available command and event bus. - * - * @package Prooph\ServiceBus - * @author Alexander Miertsch - */ -class StaticBusRegistry -{ - /** - * @var CommandBus - */ - private static $commandBus; - - /** - * @var EventBus - */ - private static $eventBus; - - /** - * @param \Prooph\ServiceBus\CommandBus $commandBus - */ - public static function setCommandBus(CommandBus $commandBus) - { - self::$commandBus = $commandBus; - } - - /** - * @throws \RuntimeException - * @return \Prooph\ServiceBus\CommandBus - */ - public static function getCommandBus() - { - if (is_null(self::$commandBus)) { - throw new \RuntimeException("Global command bus is not available. No instance registered!"); - } - - return self::$commandBus; - } - - /** - * @param \Prooph\ServiceBus\EventBus $eventBus - */ - public static function setEventBus(EventBus $eventBus) - { - self::$eventBus = $eventBus; - } - - /** - * @throws \RuntimeException - * @return \Prooph\ServiceBus\EventBus - */ - public static function getEventBus() - { - if (is_null(self::$eventBus)) { - throw new \RuntimeException("Global event bus is not available. No instance registered!"); - } - - return self::$eventBus; - } - - public static function reset() - { - self::$commandBus = null; - self::$eventBus = null; - } -} - \ No newline at end of file diff --git a/src/QueryBus.php b/src/QueryBus.php new file mode 100644 index 0000000..319ba50 --- /dev/null +++ b/src/QueryBus.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 22.05.15 - 22:16 + */ +namespace Prooph\ServiceBus; + +use Prooph\ServiceBus\Exception\MessageDispatchException; +use Prooph\ServiceBus\Exception\RuntimeException; +use React\Promise\Deferred; +use React\Promise\Promise; + +/** + * Class QueryBus + * + * The query bus dispatches a query message to a finder. + * The query is maybe dispatched async so the bus returns a promise + * which gets either resolved with the response of the finder or rejected with an exception. + * Additionally the finder can provide an update status but this is not guaranteed. + * + * @package Prooph\ServiceBus + * @author Alexander Miertsch + */ +class QueryBus extends MessageBus +{ + const EVENT_INVOKE_FINDER = 'invoke-finder'; + + const EVENT_PARAM_DEFERRED = 'query-deferred'; + + /** + * @param mixed $query + * @return Promise + */ + public function dispatch($query) + { + $deferred = new Deferred(); + + $promise = $deferred->promise(); + + $actionEvent = $this->getActionEventEmitter()->getNewActionEvent(); + + $actionEvent->setTarget($this); + + $actionEvent->setParam(self::EVENT_PARAM_DEFERRED, $deferred); + + try { + $this->initialize($query, $actionEvent); + + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { + $actionEvent->setName(self::EVENT_ROUTE); + $this->trigger($actionEvent); + } + + if ($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER) === null) { + throw new RuntimeException(sprintf( + "QueryBus was not able to identify a Finder for query %s", + $this->getMessageType($query) + )); + } + + if (is_string($actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER))) { + $actionEvent->setName(self::EVENT_LOCATE_HANDLER); + + $this->trigger($actionEvent); + } + + $finder = $actionEvent->getParam(self::EVENT_PARAM_MESSAGE_HANDLER); + + if (is_callable($finder)) { + $finder($query, $deferred); + } else { + $actionEvent->setName(self::EVENT_INVOKE_FINDER); + $this->trigger($actionEvent); + } + + $this->triggerFinalize($actionEvent); + } catch (\Exception $ex) { + $failedPhase = $actionEvent->getName(); + + $actionEvent->setParam(self::EVENT_PARAM_EXCEPTION, $ex); + + $this->triggerError($actionEvent); + $this->triggerFinalize($actionEvent); + + //Check if a listener has removed the exception to indicate that it was able to handle it + if ($ex = $actionEvent->getParam(self::EVENT_PARAM_EXCEPTION)) { + $actionEvent->setName($failedPhase); + $deferred->reject(MessageDispatchException::failed($actionEvent, $ex)); + } + } + + return $promise; + } +} \ No newline at end of file diff --git a/tests/CommandBusTest.php b/tests/CommandBusTest.php new file mode 100644 index 0000000..d40cc45 --- /dev/null +++ b/tests/CommandBusTest.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 8:17 PM + */ +namespace Prooph\ServiceBusTest; + +use Prooph\Common\Event\ActionEvent; +use Prooph\ServiceBus\CommandBus; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBusTest\Mock\CustomMessage; +use Prooph\ServiceBusTest\Mock\DoSomething; +use Prooph\ServiceBusTest\Mock\ErrorProducer; +use Prooph\ServiceBusTest\Mock\MessageHandler; + +final class CommandBusTest extends TestCase +{ + /** + * @var CommandBus + */ + private $commandBus; + + protected function setUp() + { + $this->commandBus = new CommandBus(); + } + /** + * @test + */ + function it_dispatches_a_message_using_the_default_process() + { + $doSomething = new DoSomething(['todo' => 'buy milk']); + + $receivedMessage = null; + + $this->commandBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $actionEvent) use (&$receivedMessage) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, function (DoSomething $doSomething) use (&$receivedMessage) { + $receivedMessage = $doSomething; + }); + }); + + $this->commandBus->dispatch($doSomething); + + $this->assertSame($doSomething, $receivedMessage); + } + + /** + * @test + */ + function it_triggers_all_defined_action_events() + { + $initializeIsTriggered = false; + $detectMessageNameIsTriggered = false; + $routeIsTriggered = false; + $locateHandlerIsTriggered = false; + $invokeHandlerIsTriggered = false; + $handleErrorIsTriggered = false; + $finalizeIsTriggered = false; + + //Should always be triggered + $this->commandBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_INITIALIZE, + function (ActionEvent $actionEvent) use (&$initializeIsTriggered) { + $initializeIsTriggered = true; + } + ); + + //Should be triggered because we dispatch a message that does not + //implement Prooph\Common\Messaging\HasMessageName + $this->commandBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_DETECT_MESSAGE_NAME, + function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered) { + $detectMessageNameIsTriggered = true; + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, "custom-message"); + } + ); + + //Should be triggered because we did not provide a message-handler yet + $this->commandBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_ROUTE, + function (ActionEvent $actionEvent) use (&$routeIsTriggered) { + $routeIsTriggered = true; + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === "custom-message") { + //We provide the message handler as a string (service id) to tell the bus to trigger the locate-handler event + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, "error-producer"); + } + } + ); + + //Should be triggered because we provided the message-handler as string (service id) + $this->commandBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_LOCATE_HANDLER, + function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered) { + $locateHandlerIsTriggered = true; + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === "error-producer") { + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, new ErrorProducer()); + } + } + ); + + //Should be triggered because the message-handler is not callable + $this->commandBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_INVOKE_HANDLER, + function (ActionEvent $actionEvent) use (&$invokeHandlerIsTriggered) { + $invokeHandlerIsTriggered = true; + $handler = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); + if ($handler instanceof ErrorProducer) { + $handler->throwException($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)); + } + } + ); + + //Should be triggered because the message-handler threw an exception + $this->commandBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_HANDLE_ERROR, + function (ActionEvent $actionEvent) use (&$handleErrorIsTriggered) { + $handleErrorIsTriggered = true; + + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Exception) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_EXCEPTION, null); + } + } + ); + + //Should always be triggered + $this->commandBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent) use (&$finalizeIsTriggered) { + $finalizeIsTriggered = true; + } + ); + + $customMessage = new CustomMessage("I have no further meaning"); + + $this->commandBus->dispatch($customMessage); + + $this->assertTrue($initializeIsTriggered); + $this->assertTrue($detectMessageNameIsTriggered); + $this->assertTrue($routeIsTriggered); + $this->assertTrue($locateHandlerIsTriggered); + $this->assertTrue($invokeHandlerIsTriggered); + $this->assertTrue($handleErrorIsTriggered); + $this->assertTrue($finalizeIsTriggered); + } + + /** + * @test + */ + function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name() + { + $handler = new MessageHandler(); + + $this->commandBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); + } + }); + + $customMessage = new CustomMessage("foo"); + + $this->commandBus->dispatch($customMessage); + + $this->assertSame($customMessage, $handler->getLastMessage()); + } + + /** + * @test + * @expectedException \Prooph\ServiceBus\Exception\ServiceBusException + */ + function it_throws_service_bus_exception_if_exception_is_not_handled_by_a_plugin() + { + $this->commandBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_INITIALIZE, function () { + throw new \Exception("ka boom"); + }); + + $this->commandBus->dispatch("throw it"); + } +} \ No newline at end of file diff --git a/tests/EventBusTest.php b/tests/EventBusTest.php new file mode 100644 index 0000000..fdfaf39 --- /dev/null +++ b/tests/EventBusTest.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 8:17 PM + */ +namespace Prooph\ServiceBusTest; + +use Prooph\Common\Event\ActionEvent; +use Prooph\ServiceBus\EventBus; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBusTest\Mock\CustomMessage; +use Prooph\ServiceBusTest\Mock\ErrorProducer; +use Prooph\ServiceBusTest\Mock\MessageHandler; +use Prooph\ServiceBusTest\Mock\SomethingDone; + +final class EventBusTest extends TestCase +{ + /** + * @var EventBus + */ + private $eventBus; + + protected function setUp() + { + $this->eventBus = new EventBus(); + } + /** + * @test + */ + function it_dispatches_a_message_using_the_default_process() + { + $somethingDone = new SomethingDone(['done' => 'bought milk']); + + $receivedMessage = null; + + $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $actionEvent) use (&$receivedMessage) { + $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [function (SomethingDone $somethingDone) use (&$receivedMessage) { + $receivedMessage = $somethingDone; + }]); + }); + + $this->eventBus->dispatch($somethingDone); + + $this->assertSame($somethingDone, $receivedMessage); + } + + /** + * @test + */ + function it_triggers_all_defined_action_events() + { + $initializeIsTriggered = false; + $detectMessageNameIsTriggered = false; + $routeIsTriggered = false; + $locateHandlerIsTriggered = false; + $invokeHandlerIsTriggered = false; + $handleErrorIsTriggered = false; + $finalizeIsTriggered = false; + + //Should always be triggered + $this->eventBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_INITIALIZE, + function (ActionEvent $actionEvent) use (&$initializeIsTriggered) { + $initializeIsTriggered = true; + } + ); + + //Should be triggered because we dispatch a message that does not + //implement Prooph\Common\Messaging\HasMessageName + $this->eventBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_DETECT_MESSAGE_NAME, + function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered) { + $detectMessageNameIsTriggered = true; + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, "custom-message"); + } + ); + + //Should be triggered because we did not provide a message-handler yet + $this->eventBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_ROUTE, + function (ActionEvent $actionEvent) use (&$routeIsTriggered) { + $routeIsTriggered = true; + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === "custom-message") { + //We provide the message handler as a string (service id) to tell the bus to trigger the locate-handler event + $actionEvent->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, ["error-producer"]); + } + } + ); + + //Should be triggered because we provided the message-handler as string (service id) + $this->eventBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_LOCATE_HANDLER, + function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered) { + $locateHandlerIsTriggered = true; + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === "error-producer") { + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, new ErrorProducer()); + } + } + ); + + //Should be triggered because the message-handler is not callable + $this->eventBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_INVOKE_HANDLER, + function (ActionEvent $actionEvent) use (&$invokeHandlerIsTriggered) { + $invokeHandlerIsTriggered = true; + $handler = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); + if ($handler instanceof ErrorProducer) { + $handler->throwException($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)); + } + } + ); + + //Should be triggered because the message-handler threw an exception + $this->eventBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_HANDLE_ERROR, + function (ActionEvent $actionEvent) use (&$handleErrorIsTriggered) { + $handleErrorIsTriggered = true; + + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Exception) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_EXCEPTION, null); + } + } + ); + + //Should always be triggered + $this->eventBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent) use (&$finalizeIsTriggered) { + $finalizeIsTriggered = true; + } + ); + + $customMessage = new CustomMessage("I have no further meaning"); + + $this->eventBus->dispatch($customMessage); + + $this->assertTrue($initializeIsTriggered); + $this->assertTrue($detectMessageNameIsTriggered); + $this->assertTrue($routeIsTriggered); + $this->assertTrue($locateHandlerIsTriggered); + $this->assertTrue($invokeHandlerIsTriggered); + $this->assertTrue($handleErrorIsTriggered); + $this->assertTrue($finalizeIsTriggered); + } + + /** + * @test + */ + function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name() + { + $handler = new MessageHandler(); + + $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [$handler]); + } + }); + + $customMessage = new CustomMessage("foo"); + + $this->eventBus->dispatch($customMessage); + + $this->assertSame($customMessage, $handler->getLastMessage()); + } + + /** + * @test + * @expectedException \Prooph\ServiceBus\Exception\ServiceBusException + */ + function it_throws_service_bus_exception_if_exception_is_not_handled_by_a_plugin() + { + $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_INITIALIZE, function () { + throw new \Exception("ka boom"); + }); + + $this->eventBus->dispatch("throw it"); + } + + /** + * @test + */ + function it_invokes_all_listeners() + { + $handler = new MessageHandler(); + + $this->eventBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(EventBus::EVENT_PARAM_EVENT_LISTENERS, [$handler, $handler]); + } + }); + + $customMessage = new CustomMessage("foo"); + + $this->eventBus->dispatch($customMessage); + + $this->assertSame($customMessage, $handler->getLastMessage()); + $this->assertEquals(2, $handler->getInvokeCounter()); + } +} \ No newline at end of file diff --git a/tests/MessageFactoryPluginTest.php b/tests/MessageFactoryPluginTest.php new file mode 100644 index 0000000..a7fe59c --- /dev/null +++ b/tests/MessageFactoryPluginTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 10:31 PM + */ +namespace Prooph\ServiceBusTest; + + +use Prooph\Common\Event\DefaultActionEvent; +use Prooph\Common\Messaging\MessageFactory; +use Prooph\ServiceBus\CommandBus; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\MessageFactoryPlugin; +use Prooph\ServiceBusTest\Mock\DoSomething; +use Prophecy\Argument; + +final class MessageFactoryPluginTest extends TestCase +{ + /** + * @test + */ + function it_turns_a_message_given_as_array_into_a_message_object_using_a_factory() + { + $messageFactory = $this->prophesize(MessageFactory::class); + + $messageFactory->createMessageFromArray("custom-message", Argument::any())->will(function ($args) { + list($messageName, $messageArr) = $args; + + return new DoSomething($messageArr['payload']); + }); + + $factoryPlugin = new MessageFactoryPlugin($messageFactory->reveal()); + + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_INITIALIZE, new CommandBus(), [ + //We provide message as array containing a "message_name" key because only in this case the factory plugin + //gets active + MessageBus::EVENT_PARAM_MESSAGE => [ + 'message_name' => 'custom-message', + 'payload' => ["some data"] + ] + ]); + + $factoryPlugin($actionEvent); + + $message = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE); + + $this->assertInstanceOf(DoSomething::class, $message); + $this->assertEquals(["some data"], $message->payload()); + } +} \ No newline at end of file diff --git a/tests/Mock/CustomMessage.php b/tests/Mock/CustomMessage.php new file mode 100644 index 0000000..192a26d --- /dev/null +++ b/tests/Mock/CustomMessage.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 8:35 PM + */ +namespace Prooph\ServiceBusTest\Mock; + + +final class CustomMessage +{ + private $text; + + public function __construct($text) + { + $this->text = $text; + } + + public function getText() + { + return $this->text; + } +} \ No newline at end of file diff --git a/tests/Mock/DoSomething.php b/tests/Mock/DoSomething.php new file mode 100644 index 0000000..418a240 --- /dev/null +++ b/tests/Mock/DoSomething.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 8:19 PM + */ +namespace Prooph\ServiceBusTest\Mock; + +use Prooph\Common\Messaging\Command; +use Prooph\Common\Messaging\PayloadConstructable; +use Prooph\Common\Messaging\PayloadTrait; + +final class DoSomething extends Command implements PayloadConstructable +{ + use PayloadTrait; +} \ No newline at end of file diff --git a/tests/Mock/ErrorProducer.php b/tests/Mock/ErrorProducer.php new file mode 100644 index 0000000..0f82fd9 --- /dev/null +++ b/tests/Mock/ErrorProducer.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 8:40 PM + */ +namespace Prooph\ServiceBusTest\Mock; + + +final class ErrorProducer +{ + public function throwException($message) + { + throw new \Exception("I can only throw exceptions"); + } +} \ No newline at end of file diff --git a/tests/Mock/FetchSomething.php b/tests/Mock/FetchSomething.php new file mode 100644 index 0000000..e9c0059 --- /dev/null +++ b/tests/Mock/FetchSomething.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 9:55 PM + */ +namespace Prooph\ServiceBusTest\Mock; + + +use Prooph\Common\Messaging\PayloadConstructable; +use Prooph\Common\Messaging\PayloadTrait; +use Prooph\Common\Messaging\Query; + +final class FetchSomething extends Query implements PayloadConstructable +{ + use PayloadTrait; +} \ No newline at end of file diff --git a/tests/Mock/Finder.php b/tests/Mock/Finder.php new file mode 100644 index 0000000..9ecaeab --- /dev/null +++ b/tests/Mock/Finder.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 9:31 PM + */ +namespace Prooph\ServiceBusTest\Mock; + + +final class Finder +{ + private $message; + + private $deferred; + + public function customMessage($message, $deferred) + { + $this->message = $message; + $this->deferred = $deferred; + } + + public function getLastMessage() + { + return $this->message; + } + + public function getLastDeferred() + { + return $this->deferred; + } +} \ No newline at end of file diff --git a/tests/Mock/MessageHandler.php b/tests/Mock/MessageHandler.php new file mode 100644 index 0000000..2d1c0d9 --- /dev/null +++ b/tests/Mock/MessageHandler.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 8:38 PM + */ +namespace Prooph\ServiceBusTest\Mock; + + +final class MessageHandler +{ + private $lastMessage; + + private $invokeCounter = 0; + + public function __invoke($message) + { + $this->lastMessage = $message; + $this->invokeCounter++; + } + + public function handle($message) + { + $this->lastMessage = $message; + $this->invokeCounter++; + } + + public function onCustomMessage($message) + { + $this->lastMessage = $message; + $this->invokeCounter++; + } + + public function getInvokeCounter() + { + return $this->invokeCounter; + } + + public function getLastMessage() + { + return $this->lastMessage; + } +} \ No newline at end of file diff --git a/tests/Mock/SomethingDone.php b/tests/Mock/SomethingDone.php new file mode 100644 index 0000000..d2cc19e --- /dev/null +++ b/tests/Mock/SomethingDone.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 9:46 PM + */ +namespace Prooph\ServiceBusTest\Mock; + + +use Prooph\Common\Messaging\DomainEvent; +use Prooph\Common\Messaging\PayloadConstructable; +use Prooph\Common\Messaging\PayloadTrait; + +final class SomethingDone extends DomainEvent implements PayloadConstructable +{ + use PayloadTrait; +} \ No newline at end of file diff --git a/tests/Plugin/InvokeStrategy/FinderInvokeStrategyTest.php b/tests/Plugin/InvokeStrategy/FinderInvokeStrategyTest.php new file mode 100644 index 0000000..ba2b364 --- /dev/null +++ b/tests/Plugin/InvokeStrategy/FinderInvokeStrategyTest.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 5/23/15 - 6:07 PM + */ +namespace Prooph\ServiceBusTest\Plugin\InvokeStrategy; + +use Prooph\Common\Event\DefaultActionEvent; +use Prooph\ServiceBus\Plugin\InvokeStrategy\FinderInvokeStrategy; +use Prooph\ServiceBus\QueryBus; +use Prooph\ServiceBusTest\Mock\CustomMessage; +use Prooph\ServiceBusTest\Mock\Finder; +use React\Promise\Deferred; + +/** + * Class FinderInvokeStrategyTest + * + * @package Prooph\ServiceBusTest\InvokeStrategy + * @author Alexander Miertsch + */ +final class FinderInvokeStrategyTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var FinderInvokeStrategy + */ + private $finderInvokeStrategy; + + /** + * @var DefaultActionEvent + */ + private $actionEvent; + + protected function setUp() + { + $this->finderInvokeStrategy = new FinderInvokeStrategy(); + + $this->actionEvent = new DefaultActionEvent(QueryBus::EVENT_INVOKE_FINDER, new QueryBus(), [ + QueryBus::EVENT_PARAM_MESSAGE => new CustomMessage('I am a query'), + QueryBus::EVENT_PARAM_MESSAGE_NAME => CustomMessage::class, + QueryBus::EVENT_PARAM_DEFERRED => new Deferred(), + ]); + } + + /** + * @test + */ + function it_invokes_a_finder_which_has_method_named_like_the_query() + { + $finder = new Finder(); + + $this->actionEvent->setParam(QueryBus::EVENT_PARAM_MESSAGE_HANDLER, $finder); + + $invokeStrategy = $this->finderInvokeStrategy; + + $invokeStrategy($this->actionEvent); + + $this->assertSame($this->actionEvent->getParam(QueryBus::EVENT_PARAM_MESSAGE), $finder->getLastMessage()); + $this->assertSame($this->actionEvent->getParam(QueryBus::EVENT_PARAM_DEFERRED), $finder->getLastDeferred()); + } +} \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/InvokeStrategy/HandleCommandStrategyTest.php b/tests/Plugin/InvokeStrategy/HandleCommandStrategyTest.php similarity index 67% rename from tests/Prooph/ServiceBusTest/InvokeStrategy/HandleCommandStrategyTest.php rename to tests/Plugin/InvokeStrategy/HandleCommandStrategyTest.php index 2b52f01..25661af 100644 --- a/tests/Prooph/ServiceBusTest/InvokeStrategy/HandleCommandStrategyTest.php +++ b/tests/Plugin/InvokeStrategy/HandleCommandStrategyTest.php @@ -9,14 +9,13 @@ * Date: 09.03.14 - 21:45 */ -namespace Prooph\ServiceBusTest\InvokeStrategy; +namespace Prooph\ServiceBusTest\Plugin\InvokeStrategy; -use Prooph\ServiceBus\InvokeStrategy\HandleCommandStrategy; -use Prooph\ServiceBus\Message\MessageHeader; +use Prooph\ServiceBus\Plugin\InvokeStrategy\HandleCommandStrategy; +use Prooph\ServiceBusTest\Mock\CustomMessage; use Prooph\ServiceBusTest\Mock\DoSomething; -use Prooph\ServiceBusTest\Mock\HandleCommandHandler; +use Prooph\ServiceBusTest\Mock\MessageHandler; use Prooph\ServiceBusTest\TestCase; -use Rhumsaa\Uuid\Uuid; /** * Class HandleCommandStrategyTest @@ -33,14 +32,14 @@ public function it_invokes_the_handle_command_method_of_the_handler() { $handleCommandStrategy = new HandleCommandStrategy(); - $doSomething = DoSomething::fromData('test payload'); + $doSomething = new CustomMessage("I am a command"); - $handleCommandHandler = new HandleCommandHandler(); + $handleCommandHandler = new MessageHandler(); $this->assertTrue($handleCommandStrategy->canInvoke($handleCommandHandler, $doSomething)); $handleCommandStrategy->invoke($handleCommandHandler, $doSomething); - $this->assertEquals('test payload', $handleCommandHandler->lastCommand()->data()); + $this->assertSame($doSomething, $handleCommandHandler->getLastMessage()); } } diff --git a/tests/Prooph/ServiceBusTest/InvokeStrategy/OnEventStrategyTest.php b/tests/Plugin/InvokeStrategy/OnEventStrategyTest.php similarity index 59% rename from tests/Prooph/ServiceBusTest/InvokeStrategy/OnEventStrategyTest.php rename to tests/Plugin/InvokeStrategy/OnEventStrategyTest.php index 6bf153f..5c3ef7d 100644 --- a/tests/Prooph/ServiceBusTest/InvokeStrategy/OnEventStrategyTest.php +++ b/tests/Plugin/InvokeStrategy/OnEventStrategyTest.php @@ -9,11 +9,11 @@ * Date: 11.03.14 - 21:51 */ -namespace Prooph\ServiceBusTest\InvokeStrategy; +namespace Prooph\ServiceBusTest\Plugin\InvokeStrategy; -use Prooph\ServiceBus\InvokeStrategy\OnEventStrategy; -use Prooph\ServiceBusTest\Mock\OnEventHandler; -use Prooph\ServiceBusTest\Mock\SomethingDone; +use Prooph\ServiceBus\Plugin\InvokeStrategy\OnEventStrategy; +use Prooph\ServiceBusTest\Mock\CustomMessage; +use Prooph\ServiceBusTest\Mock\MessageHandler; use Prooph\ServiceBusTest\TestCase; /** @@ -31,15 +31,15 @@ public function it_invokes_the_on_event_method_of_the_handler() { $onEventStrategy = new OnEventStrategy(); - $somethingDone = SomethingDone::fromData('test payload'); + $customEvent = new CustomMessage("I am an event"); - $onEventHandler = new OnEventHandler(); + $onEventHandler = new MessageHandler(); - $this->assertTrue($onEventStrategy->canInvoke($onEventHandler, $somethingDone)); + $this->assertTrue($onEventStrategy->canInvoke($onEventHandler, $customEvent)); - $onEventStrategy->invoke($onEventHandler, $somethingDone); + $onEventStrategy->invoke($onEventHandler, $customEvent); - $this->assertEquals('test payload', $onEventHandler->lastEvent()->data()); + $this->assertSame($customEvent, $onEventHandler->getLastMessage()); } } \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Router/EventRouterTest.php b/tests/Plugin/Router/EventRouterTest.php similarity index 54% rename from tests/Prooph/ServiceBusTest/Router/EventRouterTest.php rename to tests/Plugin/Router/EventRouterTest.php index 58dd4ca..3065561 100644 --- a/tests/Prooph/ServiceBusTest/Router/EventRouterTest.php +++ b/tests/Plugin/Router/EventRouterTest.php @@ -9,12 +9,12 @@ * Date: 23.09.14 - 20:37 */ -namespace Prooph\ServiceBusTest\Router; +namespace Prooph\ServiceBusTest\Plugin\Router; +use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\Process\EventDispatch; -use Prooph\ServiceBus\Router\EventRouter; -use Prooph\ServiceBusTest\Mock\SomethingDone; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\Router\EventRouter; use Prooph\ServiceBusTest\TestCase; /** @@ -32,15 +32,16 @@ public function it_can_handle_routing_definition_by_chaining_route_to() { $router = new EventRouter(); - $router->route('Prooph\ServiceBusTest\Mock\SomethingDone')->to("SomethingDoneListener"); + $router->route('SomethingDone')->to("SomethingDoneListener"); - $eventDispatch = EventDispatch::initializeWith(SomethingDone::fromData([]), new EventBus()); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', + ]); - $eventDispatch->setName(EventDispatch::ROUTE); - $router->onRouteEvent($eventDispatch); + $router->onRouteEvent($actionEvent); - $this->assertEquals("SomethingDoneListener", $eventDispatch->getEventListeners()[0]); + $this->assertEquals("SomethingDoneListener", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); } /** @@ -50,7 +51,7 @@ public function it_fails_on_routing_a_second_event_before_first_event_is_routed_ { $router = new EventRouter(); - $router->route('Prooph\ServiceBusTest\Mock\SomethingDone'); + $router->route('SomethingDone'); $this->setExpectedException('\Prooph\ServiceBus\Exception\RuntimeException'); @@ -64,7 +65,7 @@ public function it_can_route_a_second_event_after_the_first_one_is_routed_to_at_ { $router = new EventRouter(); - $router->route('Prooph\ServiceBusTest\Mock\SomethingDone')->to('a_listener'); + $router->route('SomethingDone')->to('a_listener'); $router->route('AnotherEvent'); @@ -90,16 +91,16 @@ public function it_fails_on_setting_a_listener_before_an_event_is_set() public function it_takes_a_routing_definition_with_a_single_listener_on_instantiation() { $router = new EventRouter(array( - 'Prooph\ServiceBusTest\Mock\SomethingDone' => 'SomethingDoneListener' + 'SomethingDone' => 'SomethingDoneListener' )); - $eventDispatch = EventDispatch::initializeWith(SomethingDone::fromData([]), new EventBus()); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', + ]); - $eventDispatch->setName(EventDispatch::ROUTE); + $router->onRouteEvent($actionEvent); - $router->onRouteEvent($eventDispatch); - - $this->assertEquals("SomethingDoneListener", $eventDispatch->getEventListeners()[0]); + $this->assertEquals("SomethingDoneListener", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); } /** @@ -108,17 +109,17 @@ public function it_takes_a_routing_definition_with_a_single_listener_on_instanti public function it_takes_a_routing_definition_with_a_multiple_listeners_on_instantiation() { $router = new EventRouter(array( - 'Prooph\ServiceBusTest\Mock\SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'] + 'SomethingDone' => ['SomethingDoneListener1', 'SomethingDoneListener2'] )); - $eventDispatch = EventDispatch::initializeWith(SomethingDone::fromData([]), new EventBus()); - - $eventDispatch->setName(EventDispatch::ROUTE); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'SomethingDone', + ]); - $router->onRouteEvent($eventDispatch); + $router->onRouteEvent($actionEvent); - $this->assertEquals("SomethingDoneListener1", $eventDispatch->getEventListeners()[0]); - $this->assertEquals("SomethingDoneListener2", $eventDispatch->getEventListeners()[1]); + $this->assertEquals("SomethingDoneListener1", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[0]); + $this->assertEquals("SomethingDoneListener2", $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)[1]); } } \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Router/RegexRouterTest.php b/tests/Plugin/Router/RegexRouterTest.php similarity index 62% rename from tests/Prooph/ServiceBusTest/Router/RegexRouterTest.php rename to tests/Plugin/Router/RegexRouterTest.php index 2064b6a..739f2a2 100644 --- a/tests/Prooph/ServiceBusTest/Router/RegexRouterTest.php +++ b/tests/Plugin/Router/RegexRouterTest.php @@ -9,15 +9,13 @@ * Date: 30.10.14 - 22:47 */ -namespace Prooph\ServiceBusTest\Router; +namespace Prooph\ServiceBusTest\Plugin\Router; +use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\CommandBus; use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\Process\CommandDispatch; -use Prooph\ServiceBus\Process\EventDispatch; -use Prooph\ServiceBus\Router\RegexRouter; -use Prooph\ServiceBusTest\Mock\DoSomething; -use Prooph\ServiceBusTest\Mock\SomethingDone; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\Router\RegexRouter; use Prooph\ServiceBusTest\TestCase; /** @@ -37,13 +35,13 @@ public function it_matches_pattern_with_command_name_to_detect_appropriate_handl $regexRouter->route('/^'.preg_quote('Prooph\ServiceBusTest\Mock\Do').'.*/')->to("DoSomethingHandler"); - $commandDispatch = CommandDispatch::initializeWith(DoSomething::fromData([]), new CommandBus()); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'Prooph\ServiceBusTest\Mock\DoSomething', + ]); - $commandDispatch->setName(CommandDispatch::ROUTE); + $regexRouter->onRoute($actionEvent); - $regexRouter->onRoute($commandDispatch); - - $this->assertEquals("DoSomethingHandler", $commandDispatch->getCommandHandler()); + $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); } /** @@ -58,11 +56,11 @@ public function it_does_not_allow_that_two_pattern_matches_with_same_command_nam $this->setExpectedException('\Prooph\ServiceBus\Exception\RuntimeException'); - $commandDispatch = CommandDispatch::initializeWith(DoSomething::fromData([]), new CommandBus()); - - $commandDispatch->setName(CommandDispatch::ROUTE); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'Prooph\ServiceBusTest\Mock\DoSomething', + ]); - $regexRouter->onRoute($commandDispatch); + $regexRouter->onRoute($actionEvent); } /** @@ -75,13 +73,13 @@ public function it_matches_pattern_with_event_name_and_routes_to_multiple_listen $regexRouter->route('/^'.preg_quote('Prooph\ServiceBusTest\Mock\\').'.*Done$/')->to("SomethingDoneListener1"); $regexRouter->route('/^'.preg_quote('Prooph\ServiceBusTest\Mock\\').'.*Done$/')->to("SomethingDoneListener2"); - $eventDispatch = EventDispatch::initializeWith(SomethingDone::fromData([]), new EventBus()); - - $eventDispatch->setName(EventDispatch::ROUTE); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'Prooph\ServiceBusTest\Mock\SomethingDone', + ]); - $regexRouter->onRoute($eventDispatch); + $regexRouter->onRoute($actionEvent); - $this->assertEquals(["SomethingDoneListener1", "SomethingDoneListener2"], $eventDispatch->getEventListeners()->getArrayCopy()); + $this->assertEquals(["SomethingDoneListener1", "SomethingDoneListener2"], $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); } /** @@ -121,21 +119,21 @@ public function it_takes_a_routing_definition_on_instantiation() )); - $commandDispatch = CommandDispatch::initializeWith(DoSomething::fromData([]), new CommandBus()); - - $commandDispatch->setName(CommandDispatch::ROUTE); - - $router->onRoute($commandDispatch); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'Prooph\ServiceBusTest\Mock\DoSomething', + ]); - $this->assertEquals("DoSomethingHandler", $commandDispatch->getCommandHandler()); + $router->onRoute($actionEvent); - $eventDispatch = EventDispatch::initializeWith(SomethingDone::fromData([]), new EventBus()); + $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); - $eventDispatch->setName(EventDispatch::ROUTE); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new EventBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'Prooph\ServiceBusTest\Mock\SomethingDone', + ]); - $router->onRoute($eventDispatch); + $router->onRoute($actionEvent); - $this->assertEquals(["SomethingDoneListener1", "SomethingDoneListener2"], $eventDispatch->getEventListeners()->getArrayCopy()); + $this->assertEquals(["SomethingDoneListener1", "SomethingDoneListener2"], $actionEvent->getParam(EventBus::EVENT_PARAM_EVENT_LISTENERS)); } } \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Router/SingleHandlerRouterTest.php b/tests/Plugin/Router/SingleHandlerRouterTest.php similarity index 65% rename from tests/Prooph/ServiceBusTest/Router/SingleHandlerRouterTest.php rename to tests/Plugin/Router/SingleHandlerRouterTest.php index 432c8b7..2346494 100644 --- a/tests/Prooph/ServiceBusTest/Router/SingleHandlerRouterTest.php +++ b/tests/Plugin/Router/SingleHandlerRouterTest.php @@ -9,10 +9,12 @@ * Date: 14.09.14 - 23:51 */ -namespace Prooph\ServiceBusTest\Router; +namespace Prooph\ServiceBusTest\Plugin\Router; + +use Prooph\Common\Event\DefaultActionEvent; use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\Process\CommandDispatch; -use Prooph\ServiceBus\Router\CommandRouter; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\Router\CommandRouter; use Prooph\ServiceBusTest\Mock\DoSomething; use Prooph\ServiceBusTest\TestCase; @@ -33,13 +35,13 @@ public function it_can_handle_routing_definition_by_chaining_route_to() $router->route('Prooph\ServiceBusTest\Mock\DoSomething')->to("DoSomethingHandler"); - $commandDispatch = CommandDispatch::initializeWith(DoSomething::fromData([]), new CommandBus()); - - $commandDispatch->setName(CommandDispatch::ROUTE); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'Prooph\ServiceBusTest\Mock\DoSomething', + ]); - $router->onRouteMessage($commandDispatch); + $router->onRouteMessage($actionEvent); - $this->assertEquals("DoSomethingHandler", $commandDispatch->getCommandHandler()); + $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); } /** @@ -77,13 +79,13 @@ public function it_takes_a_routing_definition_on_instantiation() 'Prooph\ServiceBusTest\Mock\DoSomething' => 'DoSomethingHandler' )); - $commandDispatch = CommandDispatch::initializeWith(DoSomething::fromData([]), new CommandBus()); - - $commandDispatch->setName(CommandDispatch::ROUTE); + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_ROUTE, new CommandBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_NAME => 'Prooph\ServiceBusTest\Mock\DoSomething', + ]); - $router->onRouteMessage($commandDispatch); + $router->onRouteMessage($actionEvent); - $this->assertEquals("DoSomethingHandler", $commandDispatch->getCommandHandler()); + $this->assertEquals("DoSomethingHandler", $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); } } \ No newline at end of file diff --git a/tests/Plugin/ServiceLocatorPluginTest.php b/tests/Plugin/ServiceLocatorPluginTest.php new file mode 100644 index 0000000..e9a1905 --- /dev/null +++ b/tests/Plugin/ServiceLocatorPluginTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 10:27 PM + */ +namespace Prooph\ServiceBusTest\Plugin; + +use Interop\Container\ContainerInterface; +use Prooph\Common\Event\DefaultActionEvent; +use Prooph\ServiceBus\CommandBus; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\ServiceLocatorPlugin; +use Prooph\ServiceBusTest\Mock\MessageHandler; +use Prooph\ServiceBusTest\TestCase; + +final class ServiceLocatorPluginTest extends TestCase +{ + /** + * @test + */ + function it_locates_a_service_using_the_message_handler_param_of_the_action_event() + { + $handler = new MessageHandler(); + + $container = $this->prophesize(ContainerInterface::class); + + $container->has("custom-handler")->willReturn(true); + + $container->get("custom-handler")->willReturn($handler); + + $locatorPlugin = new ServiceLocatorPlugin($container->reveal()); + + $actionEvent = new DefaultActionEvent(MessageBus::EVENT_LOCATE_HANDLER, new CommandBus(), [ + MessageBus::EVENT_PARAM_MESSAGE_HANDLER => "custom-handler" + ]); + + $locatorPlugin->onLocateMessageHandler($actionEvent); + + $this->assertSame($handler, $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER)); + } +} \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/CommandBusTest.php b/tests/Prooph/ServiceBusTest/CommandBusTest.php deleted file mode 100644 index e68f0e5..0000000 --- a/tests/Prooph/ServiceBusTest/CommandBusTest.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 16.09.14 - 23:57 - */ - -namespace Prooph\ServiceBusTest; - -use Prooph\Common\ServiceLocator\ZF2\Zf2ServiceManagerProxy; -use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\InvokeStrategy\ForwardToRemoteMessageDispatcherStrategy; -use Prooph\ServiceBus\Message\FromRemoteMessageTranslator; -use Prooph\ServiceBus\Message\InMemoryRemoteMessageDispatcher; -use Prooph\ServiceBus\Message\ProophDomainMessageToRemoteMessageTranslator; -use Prooph\ServiceBus\Router\CommandRouter; -use Prooph\ServiceBus\ServiceLocator\ServiceLocatorProxy; -use Prooph\ServiceBusTest\Mock\DoSomething; -use Prooph\ServiceBusTest\Mock\DoSomethingHandler; -use Prooph\ServiceBusTest\Mock\DoSomethingInvokeStrategy; -use Zend\ServiceManager\ServiceManager; - -/** - * Class CommandBusTest - * - * @package Prooph\ServiceBusTest - * @author Alexander Miertsch - */ -class CommandBusTest extends TestCase -{ - /** - * @var CommandBus - */ - protected $commandBus; - - /** - * @var DoSomethingHandler - */ - protected $doSomethingHandler; - - protected function setUp() - { - $this->doSomethingHandler = new DoSomethingHandler(); - - $this->commandBus = new CommandBus(); - - $router = new CommandRouter(); - - //Route the command to a message dispatcher which then dispatches the message on a second bus - $router->route('Prooph\ServiceBusTest\Mock\DoSomething')->to($this->setUpMessageDispatcher()); - - $this->commandBus->utilize($router); - - //Register message forwarder which translates command to message and forward it to the message dispatcher - $this->commandBus->utilize(new ForwardToRemoteMessageDispatcherStrategy(new ProophDomainMessageToRemoteMessageTranslator())); - } - - /** - * @return InMemoryRemoteMessageDispatcher - */ - protected function setUpMessageDispatcher() - { - $commandBus = new CommandBus(); - - //Translate message back to command - $commandBus->utilize(new FromRemoteMessageTranslator()); - - $router = new CommandRouter(); - - $router->route('Prooph\ServiceBusTest\Mock\DoSomething')->to('do_something_handler'); - - $commandBus->utilize($router); - - //Set up a ZF2 ServiceLocator to locate the command handler - //In this scenario it would be easier to route the command directly to the handler instance - //but we want to test the full stack - $sm = new ServiceManager(); - - $sm->setService('do_something_handler', $this->doSomethingHandler); - - $commandBus->utilize(new ServiceLocatorProxy(Zf2ServiceManagerProxy::proxy($sm))); - - //Register appropriate invoke strategy - $commandBus->utilize(new DoSomethingInvokeStrategy()); - - //Set up message dispatcher with a prepared command bus that can dispatch the message to command handler - $messageDispatcher = new InMemoryRemoteMessageDispatcher($commandBus, new EventBus()); - - return $messageDispatcher; - } - - /** - * @test - */ - public function it_forwards_a_command_to_message_dispatcher_and_than_to_handler() - { - $doSomething = DoSomething::fromData('dispatch me'); - - $this->commandBus->dispatch($doSomething); - - $this->assertEquals(array('data' => 'dispatch me'), $this->doSomethingHandler->lastCommand()->payload()); - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/EventBusTest.php b/tests/Prooph/ServiceBusTest/EventBusTest.php deleted file mode 100644 index 85b22b2..0000000 --- a/tests/Prooph/ServiceBusTest/EventBusTest.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 23.09.14 - 20:48 - */ - -namespace Prooph\ServiceBusTest; - -use Prooph\Common\ServiceLocator\ZF2\Zf2ServiceManagerProxy; -use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\InvokeStrategy\ForwardToRemoteMessageDispatcherStrategy; -use Prooph\ServiceBus\Message\FromRemoteMessageTranslator; -use Prooph\ServiceBus\Message\InMemoryRemoteMessageDispatcher; -use Prooph\ServiceBus\Message\ProophDomainMessageToRemoteMessageTranslator; -use Prooph\ServiceBus\Router\EventRouter; -use Prooph\ServiceBus\ServiceLocator\ServiceLocatorProxy; -use Prooph\ServiceBusTest\Mock\SomethingDone; -use Prooph\ServiceBusTest\Mock\SomethingDoneInvokeStrategy; -use Prooph\ServiceBusTest\Mock\SomethingDoneListener; -use Zend\ServiceManager\ServiceManager; - -/** - * Class EventBusTest - * - * @package Prooph\ServiceBusTest - * @author Alexander Miertsch - */ -class EventBusTest extends TestCase -{ - /** - * @var SomethingDoneListener - */ - protected $somethingDoneListener; - - /** - * @var EventBus - */ - protected $eventBus; - - protected function setUp() - { - $this->somethingDoneListener = new SomethingDoneListener(); - - $this->eventBus = new EventBus(); - - $router = new EventRouter(); - - //Route the event to a message dispatcher which then dispatches the message on a second bus - $router->route('Prooph\ServiceBusTest\Mock\SomethingDone')->to($this->setUpMessageDispatcher()); - - $this->eventBus->utilize($router); - - //Register message forwarder which translates event to message and forward it to the message dispatcher - $this->eventBus->utilize(new ForwardToRemoteMessageDispatcherStrategy(new ProophDomainMessageToRemoteMessageTranslator())); - } - - /** - * @return InMemoryRemoteMessageDispatcher - */ - protected function setUpMessageDispatcher() - { - $eventBus = new EventBus(); - - //Translate message back to event - $eventBus->utilize(new FromRemoteMessageTranslator()); - - $router = new EventRouter(); - - $router->route('Prooph\ServiceBusTest\Mock\SomethingDone')->to('something_done_listener'); - - $eventBus->utilize($router); - - //Set up a ZF2 ServiceLocator to locate the event listener - //In this scenario it would be easier to route the event directly to the listener instance - //but we want to test the full stack - $sm = new ServiceManager(); - - $sm->setService('something_done_listener', $this->somethingDoneListener); - - $eventBus->utilize(new ServiceLocatorProxy(Zf2ServiceManagerProxy::proxy($sm))); - - //Register appropriate invoke strategy - $eventBus->utilize(new SomethingDoneInvokeStrategy()); - - //Set up message dispatcher with a prepared command bus that can dispatch the message to command handler - $messageDispatcher = new InMemoryRemoteMessageDispatcher(new CommandBus(), $eventBus); - - return $messageDispatcher; - } - - /** - * @test - */ - public function it_forwards_an_event_to_message_dispatcher_and_than_to_listener() - { - $somethingDone = SomethingDone::fromData('dispatch me'); - - $this->eventBus->dispatch($somethingDone); - - $this->assertEquals(array('data' => 'dispatch me'), $this->somethingDoneListener->lastEvent()->payload()); - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/InvokeStrategy/CallbackStrategyTest.php b/tests/Prooph/ServiceBusTest/InvokeStrategy/CallbackStrategyTest.php deleted file mode 100644 index cb9955d..0000000 --- a/tests/Prooph/ServiceBusTest/InvokeStrategy/CallbackStrategyTest.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 08.03.14 - 22:48 - */ - -namespace Prooph\ServiceBusTest\InvokeStrategy; - -use Prooph\ServiceBus\InvokeStrategy\CallbackStrategy; -use Prooph\ServiceBusTest\Mock\DoSomething; -use Prooph\ServiceBusTest\TestCase; - -/** - * Class CallbackStrategyTest - * - * @package Prooph\ServiceBusTest\InvokeStrategy - * @author Alexander Miertsch - */ -class CallbackStrategyTest extends TestCase -{ - /** - * @test - */ - public function it_can_invoke_a_callable() - { - $callbackStrategy = new CallbackStrategy(); - - $this->assertTrue($callbackStrategy->canInvoke(function () {}, DoSomething::fromData(array()))); - } - - /** - * @test - */ - public function it_invokes_a_callable() - { - $callbackStrategy = new CallbackStrategy(); - - $checkData = ''; - - $callbackStrategy->invoke( - function (DoSomething $aCommand) use (&$checkData) { - $checkData = $aCommand->data(); - }, - DoSomething::fromData('test') - ); - - $this->assertEquals('test', $checkData); - } -} diff --git a/tests/Prooph/ServiceBusTest/InvokeStrategy/FinderInvokeStrategyTest.php b/tests/Prooph/ServiceBusTest/InvokeStrategy/FinderInvokeStrategyTest.php deleted file mode 100644 index 1b751b4..0000000 --- a/tests/Prooph/ServiceBusTest/InvokeStrategy/FinderInvokeStrategyTest.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 5/23/15 - 6:07 PM - */ -namespace Prooph\ServiceBusTest\InvokeStrategy; - -use Prooph\ServiceBus\InvokeStrategy\FinderInvokeStrategy; -use Prooph\ServiceBus\Process\QueryDispatch; -use Prooph\ServiceBus\QueryBus; -use Prooph\ServiceBusTest\Mock\FetchSomething; -use Prooph\ServiceBusTest\Mock\FetchSomethingFinderMock; -use React\Promise\Deferred; - -/** - * Class FinderInvokeStrategyTest - * - * @package Prooph\ServiceBusTest\InvokeStrategy - * @author Alexander Miertsch - */ -final class FinderInvokeStrategyTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var FinderInvokeStrategy - */ - private $finderInvokeStrategy; - - /** - * @var QueryDispatch - */ - private $queryDispatch; - - protected function setUp() - { - $this->finderInvokeStrategy = new FinderInvokeStrategy(); - $this->queryDispatch = QueryDispatch::initializeWith(FetchSomething::fromData('test'), new QueryBus(), new Deferred()); - } - - /** - * @test - */ - function it_invokes_a_finder_which_has_method_named_like_the_query() - { - $fetchSomethingFinder = new FetchSomethingFinderMock(); - - $this->queryDispatch->setFinder($fetchSomethingFinder); - - $this->finderInvokeStrategy->onInvokeFinder($this->queryDispatch); - - $this->assertSame($this->queryDispatch->getQuery(), $fetchSomethingFinder->getLastQuery()); - $this->assertSame($this->queryDispatch->getDeferred(), $fetchSomethingFinder->getLastDeferred()); - } - - /** - * @test - */ - function it_invokes_a_callable_array() - { - $fetchSomethingFinder = new FetchSomethingFinderMock(); - - $finderSpec = [$fetchSomethingFinder, 'fetchSomething']; - - $this->queryDispatch->setFinder($finderSpec); - - $this->finderInvokeStrategy->onInvokeFinder($this->queryDispatch); - - $this->assertSame($this->queryDispatch->getQuery(), $fetchSomethingFinder->getLastQuery()); - $this->assertSame($this->queryDispatch->getDeferred(), $fetchSomethingFinder->getLastDeferred()); - } -} \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Message/ToAndFromMessageTranslatorTest.php b/tests/Prooph/ServiceBusTest/Message/ToAndFromMessageTranslatorTest.php deleted file mode 100644 index 9e4e4d8..0000000 --- a/tests/Prooph/ServiceBusTest/Message/ToAndFromMessageTranslatorTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 11.03.14 - 22:47 - */ - -namespace Prooph\ServiceBusTest\Message; - -use Prooph\Common\Messaging\Command; -use Prooph\Common\Messaging\DomainEvent; -use Prooph\ServiceBus\Message\FromRemoteMessageTranslator; -use Prooph\ServiceBus\Message\ProophDomainMessageToRemoteMessageTranslator; -use Prooph\ServiceBusTest\Mock\DoSomething; -use Prooph\ServiceBusTest\Mock\SomethingDone; -use Prooph\ServiceBusTest\TestCase; - -/** - * Class ToAndFromMessageTranslatorTest - * - * @package Prooph\ServiceBusTest\Message - * @author Alexander Miertsch - */ -class ToAndFromMessageTranslatorTest extends TestCase -{ - /** - * @test - */ - public function it_translates_command_to_and_from_message() - { - $toMessageTranslator = new ProophDomainMessageToRemoteMessageTranslator(); - - $doSomething = DoSomething::fromData('test command'); - - $this->assertTrue($toMessageTranslator->canTranslateToRemoteMessage($doSomething)); - - $message = $toMessageTranslator->translateToRemoteMessage($doSomething); - - $this->assertEquals(array('data' => 'test command'), $message->payload()); - - $fromMessageTranslator = new FromRemoteMessageTranslator(); - - $command = $fromMessageTranslator->translateFromRemoteMessage($message); - - $this->assertInstanceOf(Command::class, $command); - $this->assertEquals($doSomething->messageName(), $command->messageName()); - $this->assertEquals($doSomething->payload(), $command->payload()); - $this->assertEquals($doSomething->uuid()->toString(), $command->uuid()->toString()); - $this->assertEquals($doSomething->version(), $command->version()); - $this->assertEquals($doSomething->createdAt()->format(\DateTime::ISO8601), $command->createdAt()->format(\DateTime::ISO8601)); - } - - /** - * @test - */ - public function it_translates_event_to_and_from_message() - { - $toMessageTranslator = new ProophDomainMessageToRemoteMessageTranslator(); - - $somethingDone = SomethingDone::fromData('test event'); - - $this->assertTrue($toMessageTranslator->canTranslateToRemoteMessage($somethingDone)); - - $message = $toMessageTranslator->translateToRemoteMessage($somethingDone); - - $this->assertEquals(array('data' => 'test event'), $message->payload()); - - $fromMessageTranslator = new FromRemoteMessageTranslator(); - - $event = $fromMessageTranslator->translateFromRemoteMessage($message); - - $this->assertInstanceOf(DomainEvent::class, $event); - $this->assertEquals($somethingDone->messageName(), $event->messageName()); - $this->assertEquals($somethingDone->payload(), $event->payload()); - $this->assertEquals($somethingDone->uuid()->toString(), $event->uuid()->toString()); - $this->assertEquals($somethingDone->version(), $event->version()); - $this->assertEquals($somethingDone->createdAt()->format(\DateTime::ISO8601), $event->createdAt()->format(\DateTime::ISO8601)); - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Mock/DoSomething.php b/tests/Prooph/ServiceBusTest/Mock/DoSomething.php deleted file mode 100644 index d695591..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/DoSomething.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 08.03.14 - 21:16 - */ - -namespace Prooph\ServiceBusTest\Mock; - -use Prooph\Common\Messaging\Command; - - -/** - * Class DoSomething - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -class DoSomething extends Command -{ - /** - * @param string $data - * @return DoSomething - */ - public static function fromData($data) - { - return new static(__CLASS__, array('data' => $data)); - } - - /** - * @return string - */ - public function data() - { - return $this->payload['data']; - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Mock/DoSomethingHandler.php b/tests/Prooph/ServiceBusTest/Mock/DoSomethingHandler.php deleted file mode 100644 index fd8576e..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/DoSomethingHandler.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 09.03.14 - 20:51 - */ - -namespace Prooph\ServiceBusTest\Mock; -use Prooph\Common\Messaging\Command; - -/** - * Class DoSomethingHandler - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -class DoSomethingHandler -{ - /** - * @var Command - */ - private $lastCommand; - - /** - * @param Command $aCommand - */ - public function doSomething(Command $aCommand) - { - $this->lastCommand = $aCommand; - } - - /** - * @return Command - */ - public function lastCommand() - { - return $this->lastCommand; - } -} diff --git a/tests/Prooph/ServiceBusTest/Mock/DoSomethingInvokeStrategy.php b/tests/Prooph/ServiceBusTest/Mock/DoSomethingInvokeStrategy.php deleted file mode 100644 index 4d2e320..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/DoSomethingInvokeStrategy.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 09.03.14 - 20:53 - */ - -namespace Prooph\ServiceBusTest\Mock; - -use Prooph\Common\Messaging\Command; -use Prooph\ServiceBus\InvokeStrategy\AbstractInvokeStrategy; - -/** - * Class DoSomethingInvokeStrategy - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -class DoSomethingInvokeStrategy extends AbstractInvokeStrategy -{ - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - * @return bool - */ - public function canInvoke($aHandler, $aCommandOrEvent) - { - return $aHandler instanceof DoSomethingHandler && $aCommandOrEvent instanceof Command; - } - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - */ - public function invoke($aHandler, $aCommandOrEvent) - { - $aHandler->doSomething($aCommandOrEvent); - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Mock/FetchSomething.php b/tests/Prooph/ServiceBusTest/Mock/FetchSomething.php deleted file mode 100644 index 7704829..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/FetchSomething.php +++ /dev/null @@ -1,38 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 22.05.15 - 22:16 - */ -namespace Prooph\ServiceBusTest\Mock; -use Prooph\Common\Messaging\Query; - -/** - * Class FetchSomething - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -final class FetchSomething extends Query -{ - /** - * @param string $data - * @return FetchSomething - */ - public static function fromData($data) - { - return new static(__CLASS__, array('data' => $data)); - } - - /** - * @return string - */ - public function data() - { - return $this->payload['data']; - } -} \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Mock/FetchSomethingFinderMock.php b/tests/Prooph/ServiceBusTest/Mock/FetchSomethingFinderMock.php deleted file mode 100644 index 87109cf..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/FetchSomethingFinderMock.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 5/23/15 - 6:09 PM - */ -namespace Prooph\ServiceBusTest\Mock; -use Prooph\Common\Messaging\Query; -use React\Promise\Deferred; - -/** - * Class FetchSomethingFinderMock - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -final class FetchSomethingFinderMock -{ - /** - * @var Query - */ - private $lastQuery; - - /** - * @var Deferred - */ - private $lastDeferred; - - public function fetchSomething(FetchSomething $query, Deferred $deferred) - { - $this->lastQuery = $query; - $this->lastDeferred = $deferred; - $this->lastDeferred->resolve($query->data()); - } - - /** - * @return \React\Promise\Deferred - */ - public function getLastDeferred() - { - return $this->lastDeferred; - } - - /** - * @return \Prooph\Common\Messaging\Query - */ - public function getLastQuery() - { - return $this->lastQuery; - } -} \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Mock/HandleCommandHandler.php b/tests/Prooph/ServiceBusTest/Mock/HandleCommandHandler.php deleted file mode 100644 index ab9bf31..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/HandleCommandHandler.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 09.03.14 - 21:47 - */ - -namespace Prooph\ServiceBusTest\Mock; - -/** - * Class HandleCommandHandler - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -class HandleCommandHandler -{ - /** - * @var DoSomething - */ - private $lastCommand; - - /** - * @param DoSomething $aCommand - */ - public function handle(DoSomething $aCommand) - { - $this->lastCommand = $aCommand; - } - - /** - * @return DoSomething - */ - public function lastCommand() - { - return $this->lastCommand; - } -} diff --git a/tests/Prooph/ServiceBusTest/Mock/OnEventHandler.php b/tests/Prooph/ServiceBusTest/Mock/OnEventHandler.php deleted file mode 100644 index 77cd477..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/OnEventHandler.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 11.03.14 - 21:52 - */ - -namespace Prooph\ServiceBusTest\Mock; - -/** - * Class OnEventHandler - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -class OnEventHandler -{ - /** - * @var SomethingDone - */ - private $lastEvent; - - private $eventCount = 0; - - /** - * @param SomethingDone $event - */ - public function onSomethingDone(SomethingDone $event) - { - $this->eventCount++; - $this->lastEvent = $event; - } - - /** - * @return SomethingDone - */ - public function lastEvent() - { - return $this->lastEvent; - } - - public function eventCount() - { - return $this->eventCount; - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Mock/SomethingDone.php b/tests/Prooph/ServiceBusTest/Mock/SomethingDone.php deleted file mode 100644 index 22f0e77..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/SomethingDone.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 11.03.14 - 21:17 - */ - -namespace Prooph\ServiceBusTest\Mock; - -use Prooph\Common\Messaging\DomainEvent; - -/** - * Class SomethingDone - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -class SomethingDone extends DomainEvent -{ - /** - * @param string $dataString - * @return SomethingDone - */ - public static function fromData($dataString) - { - return new self(__CLASS__, array('data' => $dataString)); - } - /** - * @return string - */ - public function data() - { - return $this->payload['data']; - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Mock/SomethingDoneInvokeStrategy.php b/tests/Prooph/ServiceBusTest/Mock/SomethingDoneInvokeStrategy.php deleted file mode 100644 index e796d30..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/SomethingDoneInvokeStrategy.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 11.03.14 - 22:27 - */ - -namespace Prooph\ServiceBusTest\Mock; - -use Prooph\Common\Messaging\DomainEvent; -use Prooph\ServiceBus\InvokeStrategy\AbstractInvokeStrategy; - -/** - * Class SomethingDoneInvokeStrategy - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -class SomethingDoneInvokeStrategy extends AbstractInvokeStrategy -{ - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - * @return bool - */ - public function canInvoke($aHandler, $aCommandOrEvent) - { - return $aHandler instanceof SomethingDoneListener && $aCommandOrEvent instanceof DomainEvent; - } - - /** - * @param mixed $aHandler - * @param mixed $aCommandOrEvent - */ - public function invoke($aHandler, $aCommandOrEvent) - { - $aHandler->somethingDone($aCommandOrEvent); - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Mock/SomethingDoneListener.php b/tests/Prooph/ServiceBusTest/Mock/SomethingDoneListener.php deleted file mode 100644 index b3bfa23..0000000 --- a/tests/Prooph/ServiceBusTest/Mock/SomethingDoneListener.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 11.03.14 - 22:27 - */ - -namespace Prooph\ServiceBusTest\Mock; -use Prooph\Common\Messaging\DomainEvent; - -/** - * Class SomethingDoneListener - * - * @package Prooph\ServiceBusTest\Mock - * @author Alexander Miertsch - */ -class SomethingDoneListener -{ - /** - * @var SomethingDone - */ - private $lastEvent; - - /** - * @param DomainEvent $event - */ - public function somethingDone(DomainEvent $event) - { - $this->lastEvent = $event; - } - - /** - * @return DomainEvent - */ - public function lastEvent() - { - return $this->lastEvent; - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Process/CommandDispatchTest.php b/tests/Prooph/ServiceBusTest/Process/CommandDispatchTest.php deleted file mode 100644 index 93b28ce..0000000 --- a/tests/Prooph/ServiceBusTest/Process/CommandDispatchTest.php +++ /dev/null @@ -1,172 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 14.09.14 - 18:09 - */ - -namespace Prooph\ServiceBusTest\Process; - -use Prooph\Common\Logger\ZF2\PsrZF2Logger; -use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\Exception\RuntimeException; -use Prooph\ServiceBus\Process\CommandDispatch; -use Prooph\ServiceBusTest\TestCase; -use Zend\Log\Logger; - -/** - * Class CommandDispatchTest - * - * @package Prooph\ServiceBusTest\Process - * @author Alexander Miertsch - */ -class CommandDispatchTest extends TestCase -{ - /** - * @test - */ - public function it_is_initialized_with_a_command_and_a_command_bus_as_target() - { - $command = new \ArrayObject(array('name' => 'DoSomething')); - - $commandBus = new CommandBus(); - - $commandDispatch = CommandDispatch::initializeWith($command, $commandBus); - - $this->assertSame($command, $commandDispatch->getCommand()); - $this->assertSame($commandBus, $commandDispatch->getTarget()); - $this->assertEquals(CommandDispatch::INITIALIZE, $commandDispatch->getName()); - } - - /** - * @test - */ - public function it_replaces_command_with_a_new_one() - { - $otherCommand = new \ArrayObject(array('name' => 'DoSomethingElse')); - - $commandDispatch = $this->getNewCommandDispatch(); - - $commandDispatch->setCommand($otherCommand); - - $this->assertSame($otherCommand, $commandDispatch->getCommand()); - } - - /** - * @test - */ - public function it_sets_and_gets_command_name() - { - $commandDispatch = $this->getNewCommandDispatch(); - - $commandDispatch->setCommandName("DoSomething"); - - $this->assertEquals("DoSomething", $commandDispatch->getCommandName()); - } - - /** - * @test - */ - public function it_returns_null_when_command_name_is_not_set() - { - $this->assertNull($this->getNewCommandDispatch()->getCommandName()); - } - - /** - * @test - */ - public function it_only_accepts_a_string_as_command_name() - { - $this->setExpectedException('\InvalidArgumentException'); - - $this->getNewCommandDispatch()->setCommandName(123); - } - - /** - * @test - */ - public function it_sets_and_gets_a_command_handler_string() - { - $commandDispatch = $this->getNewCommandDispatch(); - - $commandDispatch->setCommandHandler("DoSomethingHandler"); - - $this->assertEquals("DoSomethingHandler", $commandDispatch->getCommandHandler()); - } - - /** - * @test - */ - public function it_accepts_an_object_as_command_handler() - { - $commandHandler = new \stdClass(); - - $commandDispatch = $this->getNewCommandDispatch(); - - $commandDispatch->setCommandHandler($commandHandler); - - $this->assertSame($commandHandler, $commandDispatch->getCommandHandler()); - } - - /** - * @test - */ - public function it_accepts_a_callable_as_command_handler() - { - $commandHandlerCallback = function ($command) {}; - - $commandDispatch = $this->getNewCommandDispatch(); - - $commandDispatch->setCommandHandler($commandHandlerCallback); - - $this->assertSame($commandHandlerCallback, $commandDispatch->getCommandHandler()); - } - - /** - * @test - */ - public function it_does_not_accept_a_non_callable_array_as_command_handler() - { - $this->setExpectedException('\InvalidArgumentException'); - - $commandDispatch = $this->getNewCommandDispatch(); - - $commandDispatch->setCommandHandler(array("DoSomethingHandler")); - } - - /** - * @test - * @expectedException RuntimeException - */ - public function it_throws_exception_if_no_is_logger_available() - { - $this->getNewCommandDispatch()->getLogger(); - } - - /** - * @test - */ - public function it_gives_hint_whether_logging_is_enabled_or_not() - { - $commandDispatch = $this->getNewCommandDispatch(); - - $this->assertFalse($commandDispatch->isLoggingEnabled()); - - $commandDispatch->useLogger(new PsrZF2Logger(new Logger())); - - $this->assertTrue($commandDispatch->isLoggingEnabled()); - } - - /** - * @return CommandDispatch - */ - protected function getNewCommandDispatch() - { - return CommandDispatch::initializeWith(new \ArrayObject(array('name' => 'DoSomething')), new CommandBus()); - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Process/EventDispatchTest.php b/tests/Prooph/ServiceBusTest/Process/EventDispatchTest.php deleted file mode 100644 index 18cc0eb..0000000 --- a/tests/Prooph/ServiceBusTest/Process/EventDispatchTest.php +++ /dev/null @@ -1,174 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 23.09.14 - 20:03 - */ - -namespace Prooph\ServiceBusTest\Process; - -use Prooph\Common\Logger\ZF2\PsrZF2Logger; -use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\Process\EventDispatch; -use Prooph\ServiceBusTest\TestCase; -use Zend\Log\Logger; -use Zend\ServiceManager\Exception\RuntimeException; -use Zend\Stdlib\ArrayObject; - -/** - * Class EventDispatchTest - * - * @package Prooph\ServiceBusTest\Process - * @author Alexander Miertsch - */ -class EventDispatchTest extends TestCase -{ - /** - * @test - */ - public function it_is_initialized_with_an_event_and_an_event_bus_as_target() - { - $event = new \ArrayObject(array('name' => 'SomethingDone')); - - $eventBus = new EventBus(); - - $eventDispatch = EventDispatch::initializeWith($event, $eventBus); - - $this->assertSame($event, $eventDispatch->getEvent()); - $this->assertSame($eventBus, $eventDispatch->getTarget()); - $this->assertEquals(EventDispatch::INITIALIZE, $eventDispatch->getName()); - } - - /** - * @test - */ - public function it_replaces_event_with_a_new_one() - { - $otherEvent = new \ArrayObject(array('name' => 'SomethingDifferentDone')); - - $eventDispatch = $this->getNewEventDispatch(); - - $eventDispatch->setEvent($otherEvent); - - $this->assertSame($otherEvent, $eventDispatch->getEvent()); - } - - /** - * @test - */ - public function it_sets_and_gets_event_name() - { - $eventDispatch = $this->getNewEventDispatch(); - - $eventDispatch->setEventName("SomethingDone"); - - $this->assertEquals("SomethingDone", $eventDispatch->getEventName()); - } - - /** - * @test - */ - public function it_returns_null_when_event_name_is_not_set() - { - $this->assertNull($this->getNewEventDispatch()->getEventName()); - } - - /** - * @test - */ - public function it_only_accepts_a_string_as_event_name() - { - $this->setExpectedException('\InvalidArgumentException'); - - $this->getNewEventDispatch()->setEventName(123); - } - - /** - * @test - */ - public function it_sets_and_gets_event_listener_strings() - { - $eventDispatch = $this->getNewEventDispatch(); - - $eventDispatch->setEventListeners(array('SomethingDoneListener1', 'SomethingDoneListener2')); - - $this->assertEquals('SomethingDoneListener1', $eventDispatch->getEventListeners()[0]); - $this->assertEquals('SomethingDoneListener2', $eventDispatch->getEventListeners()[1]); - } - - /** - * @test - */ - public function it_accepts_an_object_as_event_listener() - { - $eventListener = new \stdClass(); - - $eventDispatch = $this->getNewEventDispatch(); - - $eventDispatch->addEventListener($eventListener); - - $this->assertSame($eventListener, $eventDispatch->getEventListeners()[0]); - } - - /** - * @test - */ - public function it_accepts_a_callable_as_event_listener() - { - $evenListenerCallback = function ($event) {}; - - $eventDispatch = $this->getNewEventDispatch(); - - $eventDispatch->addEventListener($evenListenerCallback); - - $this->assertSame($evenListenerCallback, $eventDispatch->getEventListeners()[0]); - } - - /** - * @test - */ - public function it_does_not_accept_a_non_callable_array_as_event_listener() - { - $this->setExpectedException('\InvalidArgumentException'); - - $eventDispatch = $this->getNewEventDispatch(); - - $eventDispatch->addEventListener(array("SomethingDoneListener")); - } - - /** - * @test - * @expectedException RuntimeException - */ - public function it_throws_exception_if_it_is_no_logger_available() - { - $this->assertInstanceOf('Zend\Log\LoggerInterface', $this->getNewEventDispatch()->getLogger()); - } - - /** - * @test - */ - public function it_gives_hint_whether_logging_is_enabled_or_not() - { - $eventDispatch = $this->getNewEventDispatch(); - - $this->assertFalse($eventDispatch->isLoggingEnabled()); - - $eventDispatch->useLogger(new PsrZF2Logger(new Logger())); - - $this->assertTrue($eventDispatch->isLoggingEnabled()); - } - - /** - * @return EventDispatch - */ - protected function getNewEventDispatch() - { - return EventDispatch::initializeWith(new \ArrayObject(array('name' => 'SomethingDone')), new EventBus()); - } -} - \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/Process/QueryDispatchTest.php b/tests/Prooph/ServiceBusTest/Process/QueryDispatchTest.php deleted file mode 100644 index 16cb5da..0000000 --- a/tests/Prooph/ServiceBusTest/Process/QueryDispatchTest.php +++ /dev/null @@ -1,139 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 22.05.15 - 22:16 - */ -namespace Prooph\ServiceBusTest\Process; - -use Prooph\ServiceBus\Process\QueryDispatch; -use Prooph\ServiceBus\QueryBus; -use Prooph\ServiceBusTest\Mock\FetchSomething; -use React\Promise\Deferred; - -/** - * Class QueryDispatchTest - * - * @package Prooph\ServiceBusTest\Process - * @author Alexander Miertsch - */ -class QueryDispatchTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var QueryDispatch - */ - private $queryDispatch; - - /** - * @var FetchSomething - */ - private $query; - - /** - * @var Deferred - */ - private $deferred; - - /** - * @var QueryBus - */ - private $queryBus; - - protected function setUp() - { - $this->query = FetchSomething::fromData('test'); - $this->deferred = new Deferred(); - $this->queryBus = new QueryBus(); - $this->queryDispatch = QueryDispatch::initializeWith($this->query, $this->queryBus, $this->deferred); - } - - /** - * @test - */ - public function it_is_initialized_with_a_query_and_a_query_bus_as_target() - { - $this->assertSame($this->query, $this->queryDispatch->getQuery()); - $this->assertSame($this->queryBus, $this->queryDispatch->getTarget()); - $this->assertEquals(QueryDispatch::INITIALIZE, $this->queryDispatch->getName()); - } - - /** - * @test - */ - public function it_replaces_query_with_a_new_one() - { - $otherQuery = new \ArrayObject(array('name' => 'FetchSomethingElse')); - - $this->queryDispatch->setQuery($otherQuery); - - $this->assertSame($otherQuery, $this->queryDispatch->getQuery()); - } - - /** - * @test - */ - public function it_sets_and_gets_query_name() - { - $this->queryDispatch->setQueryName("FetchSomething"); - - $this->assertEquals("FetchSomething", $this->queryDispatch->getQueryName()); - } - - /** - * @test - */ - public function it_only_accepts_a_string_as_query_name() - { - $this->setExpectedException('\InvalidArgumentException'); - - $this->queryDispatch->setQueryName(123); - } - - /** - * @test - */ - public function it_sets_and_gets_a_finder_string() - { - $this->queryDispatch->setFinder("FetchSomethingFinder"); - - $this->assertEquals("FetchSomethingFinder", $this->queryDispatch->getFinder()); - } - - /** - * @test - */ - public function it_accepts_an_object_as_finder() - { - $finder = new \stdClass(); - - $this->queryDispatch->setFinder($finder); - - $this->assertSame($finder, $this->queryDispatch->getFinder()); - } - - /** - * @test - */ - public function it_accepts_a_callable_as_finder() - { - $finderCallback = function ($query) {}; - - $this->queryDispatch->setFinder($finderCallback); - - $this->assertSame($finderCallback, $this->queryDispatch->getFinder()); - } - - /** - * @test - */ - public function it_does_not_accept_a_non_callable_array_as_finder() - { - $this->setExpectedException('\InvalidArgumentException'); - - $this->queryDispatch->setFinder(array("FetchSomethingFinder")); - } -} \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/QueryBusTest.php b/tests/Prooph/ServiceBusTest/QueryBusTest.php deleted file mode 100644 index 2563ea5..0000000 --- a/tests/Prooph/ServiceBusTest/QueryBusTest.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 5/23/15 - 6:17 PM - */ -namespace Prooph\ServiceBusTest; -use Prooph\ServiceBus\CommandBus; -use Prooph\ServiceBus\EventBus; -use Prooph\ServiceBus\InvokeStrategy\FinderInvokeStrategy; -use Prooph\ServiceBus\InvokeStrategy\ForwardToRemoteMessageDispatcherStrategy; -use Prooph\ServiceBus\InvokeStrategy\ForwardToRemoteQueryDispatcherStrategy; -use Prooph\ServiceBus\Message\FromRemoteMessageTranslator; -use Prooph\ServiceBus\Message\InMemoryRemoteMessageDispatcher; -use Prooph\ServiceBus\Message\ProophDomainMessageToRemoteMessageTranslator; -use Prooph\ServiceBus\QueryBus; -use Prooph\ServiceBus\Router\QueryRouter; -use Prooph\ServiceBusTest\Mock\FetchSomething; -use Prooph\ServiceBusTest\Mock\FetchSomethingFinderMock; - -/** - * Class QueryBusTest - * - * @package Prooph\ServiceBusTest - * @author Alexander Miertsch - */ -final class QueryBusTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var QueryBus - */ - private $queryBus; - - protected function setUp() - { - $this->queryBus = new QueryBus(); - - $queryToDispatcherRouter = new QueryRouter(); - - $finderQueryBus = new QueryBus(); - - $queryToDispatcherRouter->route(FetchSomething::class)->to(new InMemoryRemoteMessageDispatcher( - new CommandBus(), - new EventBus(), - $finderQueryBus - )); - - $this->queryBus->utilize($queryToDispatcherRouter); - - $this->queryBus->utilize(new ForwardToRemoteQueryDispatcherStrategy()); - - $finder = new FetchSomethingFinderMock(); - - $toFinderRouter = new QueryRouter(); - - $toFinderRouter->route(FetchSomething::class)->to($finder); - - $finderQueryBus->utilize($toFinderRouter); - - $finderQueryBus->utilize(new FromRemoteMessageTranslator()); - - $finderQueryBus->utilize(new FinderInvokeStrategy()); - } - - /** - * @test - */ - function it_dispatches_a_query_to_a_remote_query_dispatcher_which_then_resolves_the_deferred_by_dispatching_to_the_finder() - { - $query = FetchSomething::fromData('This should be the result'); - - $promise = $this->queryBus->dispatch($query); - - $result = "wrong result"; - - $promise->done(function ($resolvedResult) use (&$result) { - $result = $resolvedResult; - }); - - $this->assertEquals('This should be the result', $result); - } -} \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/ServiceLocator/ServiceLocatorProxyTest.php b/tests/Prooph/ServiceBusTest/ServiceLocator/ServiceLocatorProxyTest.php deleted file mode 100644 index 50c4f91..0000000 --- a/tests/Prooph/ServiceBusTest/ServiceLocator/ServiceLocatorProxyTest.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - * Date: 23.09.14 - 21:21 - */ - -namespace Prooph\ServiceBusTest\ServiceLocator; - -use Prooph\Common\ServiceLocator\ZF2\Zf2ServiceManagerProxy; -use Prooph\ServiceBus\Process\CommandDispatch; -use Prooph\ServiceBus\Process\EventDispatch; -use Prooph\ServiceBus\ServiceLocator\ServiceLocatorProxy; -use Prooph\ServiceBusTest\TestCase; -use Zend\ServiceManager\Config; -use Zend\ServiceManager\ServiceManager; - -/** - * Class ServiceLocatorProxyTest - * - * @package Prooph\ServiceBusTest\ServiceLocator - * @author Alexander Miertsch - */ -class ServiceLocatorProxyTest extends TestCase -{ - /** - * @var ServiceLocatorProxy - */ - protected $serviceLocatorProxy; - - protected function setUp() - { - $config = new Config(array( - 'invokables' => array( - 'do_something_handler' => 'Prooph\ServiceBusTest\Mock\DoSomethingHandler', - 'something_done_listener' => 'Prooph\ServiceBusTest\Mock\SomethingDoneListener', - ) - )); - - $sm = new ServiceManager($config); - - $this->serviceLocatorProxy = new ServiceLocatorProxy(Zf2ServiceManagerProxy::proxy($sm)); - } - - /** - * @test - */ - public function it_locates_a_command_handler() - { - $commandDispatch = new CommandDispatch(); - - $commandDispatch->setCommandHandler('do_something_handler'); - - $commandDispatch->setName(CommandDispatch::LOCATE_HANDLER); - - $this->serviceLocatorProxy->onLocateCommandHandler($commandDispatch); - - $this->assertInstanceOf('Prooph\ServiceBusTest\Mock\DoSomethingHandler', $commandDispatch->getCommandHandler()); - } - - /** - * @test - */ - public function it_locates_an_event_listener() - { - $eventDispatch = new EventDispatch(); - - $eventDispatch->setCurrentEventListener('something_done_listener'); - - $eventDispatch->setName(EventDispatch::LOCATE_LISTENER); - - $this->serviceLocatorProxy->onLocateEventListener($eventDispatch); - - $this->assertInstanceOf('Prooph\ServiceBusTest\Mock\SomethingDoneListener', $eventDispatch->getCurrentEventListener()); - } -} - \ No newline at end of file diff --git a/tests/QueryBusTest.php b/tests/QueryBusTest.php new file mode 100644 index 0000000..a8a9666 --- /dev/null +++ b/tests/QueryBusTest.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + * Date: 8/2/15 - 8:17 PM + */ +namespace Prooph\ServiceBusTest; + +use Prooph\Common\Event\ActionEvent; +use Prooph\ServiceBus\Exception\ServiceBusException; +use Prooph\ServiceBus\MessageBus; +use Prooph\ServiceBus\Plugin\InvokeStrategy\FinderInvokeStrategy; +use Prooph\ServiceBus\QueryBus; +use Prooph\ServiceBusTest\Mock\CustomMessage; +use Prooph\ServiceBusTest\Mock\DoSomething; +use Prooph\ServiceBusTest\Mock\ErrorProducer; +use Prooph\ServiceBusTest\Mock\FetchSomething; +use Prooph\ServiceBusTest\Mock\Finder; +use Prooph\ServiceBusTest\Mock\MessageHandler; +use React\Promise\Deferred; +use React\Promise\Promise; + +final class QueryBusTest extends TestCase +{ + /** + * @var QueryBus + */ + private $queryBus; + + protected function setUp() + { + $this->queryBus = new QueryBus(); + } + /** + * @test + */ + function it_dispatches_a_message_using_the_default_process() + { + $fetchSomething = new FetchSomething(['filter' => 'todo']); + + $receivedMessage = null; + + $this->queryBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $actionEvent) use (&$receivedMessage) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, function (FetchSomething $fetchSomething, Deferred $deferred) use (&$receivedMessage) { + $deferred->resolve($fetchSomething); + }); + }); + + $promise = $this->queryBus->dispatch($fetchSomething); + + $promise->then(function($result) use (&$receivedMessage) { + $receivedMessage = $result; + }); + + $this->assertSame($fetchSomething, $receivedMessage); + } + + /** + * @test + */ + function it_triggers_all_defined_action_events() + { + $initializeIsTriggered = false; + $detectMessageNameIsTriggered = false; + $routeIsTriggered = false; + $locateHandlerIsTriggered = false; + $invokeFinderIsTriggered = false; + $handleErrorIsTriggered = false; + $finalizeIsTriggered = false; + + //Should always be triggered + $this->queryBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_INITIALIZE, + function (ActionEvent $actionEvent) use (&$initializeIsTriggered) { + $initializeIsTriggered = true; + } + ); + + //Should be triggered because we dispatch a message that does not + //implement Prooph\Common\Messaging\HasMessageName + $this->queryBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_DETECT_MESSAGE_NAME, + function (ActionEvent $actionEvent) use (&$detectMessageNameIsTriggered) { + $detectMessageNameIsTriggered = true; + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_NAME, "custom-message"); + } + ); + + //Should be triggered because we did not provide a message-handler yet + $this->queryBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_ROUTE, + function (ActionEvent $actionEvent) use (&$routeIsTriggered) { + $routeIsTriggered = true; + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === "custom-message") { + //We provide the message handler as a string (service id) to tell the bus to trigger the locate-handler event + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, "error-producer"); + } + } + ); + + //Should be triggered because we provided the message-handler as string (service id) + $this->queryBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_LOCATE_HANDLER, + function (ActionEvent $actionEvent) use (&$locateHandlerIsTriggered) { + $locateHandlerIsTriggered = true; + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER) === "error-producer") { + $actionEvent->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, new ErrorProducer()); + } + } + ); + + //Should be triggered because the message-handler is not callable + $this->queryBus->getActionEventEmitter()->attachListener( + QueryBus::EVENT_INVOKE_FINDER, + function (ActionEvent $actionEvent) use (&$invokeFinderIsTriggered) { + $invokeFinderIsTriggered = true; + $handler = $actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER); + if ($handler instanceof ErrorProducer) { + $handler->throwException($actionEvent->getParam(MessageBus::EVENT_PARAM_MESSAGE)); + } + } + ); + + //Should be triggered because the message-handler threw an exception + $this->queryBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_HANDLE_ERROR, + function (ActionEvent $actionEvent) use (&$handleErrorIsTriggered) { + $handleErrorIsTriggered = true; + + if ($actionEvent->getParam(MessageBus::EVENT_PARAM_EXCEPTION) instanceof \Exception) { + $actionEvent->setParam(MessageBus::EVENT_PARAM_EXCEPTION, null); + } + } + ); + + //Should always be triggered + $this->queryBus->getActionEventEmitter()->attachListener( + MessageBus::EVENT_FINALIZE, + function (ActionEvent $actionEvent) use (&$finalizeIsTriggered) { + $finalizeIsTriggered = true; + } + ); + + $customMessage = new CustomMessage("I have no further meaning"); + + $this->queryBus->dispatch($customMessage); + + $this->assertTrue($initializeIsTriggered); + $this->assertTrue($detectMessageNameIsTriggered); + $this->assertTrue($routeIsTriggered); + $this->assertTrue($locateHandlerIsTriggered); + $this->assertTrue($invokeFinderIsTriggered); + $this->assertTrue($handleErrorIsTriggered); + $this->assertTrue($finalizeIsTriggered); + } + + /** + * @test + */ + function it_uses_the_fqcn_of_the_message_if_message_name_was_not_provided_and_message_does_not_implement_has_message_name() + { + $handler = new Finder(); + + $this->queryBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_ROUTE, function (ActionEvent $e) use ($handler) { + if ($e->getParam(MessageBus::EVENT_PARAM_MESSAGE_NAME) === CustomMessage::class) { + $e->setParam(MessageBus::EVENT_PARAM_MESSAGE_HANDLER, $handler); + } + }); + + $this->queryBus->utilize(new FinderInvokeStrategy()); + + $customMessage = new CustomMessage("foo"); + + $promise = $this->queryBus->dispatch($customMessage); + + $this->assertSame($customMessage, $handler->getLastMessage()); + $this->assertInstanceOf(Promise::class, $promise); + $this->assertInstanceOf(Deferred::class, $handler->getLastDeferred()); + } + + /** + * @test + */ + function it_rejects_the_deferred_with_a_service_bus_exception_if_exception_is_not_handled_by_a_plugin() + { + $exception = null; + + $this->queryBus->getActionEventEmitter()->attachListener(MessageBus::EVENT_INITIALIZE, function () { + throw new \Exception("ka boom"); + }); + + $promise = $this->queryBus->dispatch("throw it"); + + $promise->otherwise(function ($ex) use (&$exception) { + $exception = $ex; + }); + + $this->assertInstanceOf(ServiceBusException::class, $exception); + } +} \ No newline at end of file diff --git a/tests/Prooph/ServiceBusTest/TestCase.php b/tests/TestCase.php similarity index 100% rename from tests/Prooph/ServiceBusTest/TestCase.php rename to tests/TestCase.php diff --git a/tests/phpunit.xml.dist b/tests/phpunit.xml.dist index be97cbb..5c02acb 100644 --- a/tests/phpunit.xml.dist +++ b/tests/phpunit.xml.dist @@ -3,7 +3,7 @@ - ./Prooph/ServiceBusTest + . \ No newline at end of file diff --git a/tests/service-bus-config.php.dist b/tests/service-bus-config.php.dist deleted file mode 100644 index 621b07e..0000000 --- a/tests/service-bus-config.php.dist +++ /dev/null @@ -1,11 +0,0 @@ - array( - 'invokables' => array( - 'file_remover' => 'Prooph\ServiceBusTest\Mock\FileRemover' - ), - ), - \Prooph\ServiceBus\Service\Definition::COMMAND_MAP => array( - 'Prooph\ServiceBusTest\Mock\RemoveFileCommand' => 'file_remover' - ), -); \ No newline at end of file