├── CHANGELOG.md ├── src ├── Bus │ ├── AsynchronousEventBus.php │ └── AsynchronousCommandBus.php ├── Resources │ └── config │ │ ├── asynchronous_events_logging.yml │ │ ├── asynchronous_commands_logging.yml │ │ ├── asynchronous_serialization.yml │ │ ├── asynchronous_events.yml │ │ └── asynchronous_commands.yml ├── DependencyInjection │ ├── Compiler │ │ └── CollectAsynchronousEventNames.php │ ├── Configuration.php │ └── SimpleBusAsynchronousExtension.php └── SimpleBusAsynchronousBundle.php ├── couscous.yml ├── README.md ├── LICENSE └── composer.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | ## [Unreleased][unreleased] 4 | 5 | -------------------------------------------------------------------------------- /src/Bus/AsynchronousEventBus.php: -------------------------------------------------------------------------------- 1 | hasDefinition($serviceId)) { 20 | return; 21 | } 22 | 23 | $names = []; 24 | $this->collectServiceIds( 25 | $container, 26 | 'asynchronous_event_subscriber', 27 | 'subscribes_to', 28 | function ($key) use (&$names) { 29 | $names[] = $key; 30 | } 31 | ); 32 | 33 | $container->getDefinition($serviceId)->replaceArgument(2, array_unique($names)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-bus/asynchronous-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Symfony bundle for using SimpleBus/Asynchronous", 5 | "keywords": [ 6 | "asynchronous", 7 | "message", 8 | "message bus", 9 | "event bus", 10 | "command bus" 11 | ], 12 | "homepage": "http://github.com/SimpleBus/AsynchronousBundle", 13 | "license": "MIT", 14 | "authors": [ 15 | { 16 | "name": "Cliff Odijk", 17 | "email": "cliff@jcid.nl" 18 | }, 19 | { 20 | "name": "Ruud Kamphuis", 21 | "homepage": "https://github.com/ruudk" 22 | }, 23 | { 24 | "name": "Matthias Noback", 25 | "email": "matthiasnoback@gmail.com", 26 | "homepage": "http://php-and-symfony.matthiasnoback.nl" 27 | } 28 | ], 29 | "require": { 30 | "php": "^8.0", 31 | "simple-bus/asynchronous": "^6.0", 32 | "simple-bus/message-bus": "^6.0", 33 | "simple-bus/symfony-bridge": "^6.0", 34 | "symfony/config": "^5.4 || ^6.0", 35 | "symfony/dependency-injection": "^5.4 || ^6.0", 36 | "symfony/framework-bundle": "^5.4 || ^6.0" 37 | }, 38 | "conflict": { 39 | "monolog/monolog": "<1.26.1 || >=2.0,<2.3.0" 40 | }, 41 | "require-dev": { 42 | "ergebnis/composer-normalize": "^2.11", 43 | "matthiasnoback/symfony-dependency-injection-test": "^4.3", 44 | "phpunit/phpunit": "^9.5.5", 45 | "symfony/monolog-bundle": "^3.4", 46 | "symfony/phpunit-bridge": "^6.0" 47 | }, 48 | "suggest": { 49 | "symfony/monolog-bundle": "For logging messages" 50 | }, 51 | "config": { 52 | "sort-packages": true 53 | }, 54 | "autoload": { 55 | "psr-4": { 56 | "SimpleBus\\AsynchronousBundle\\": "src" 57 | } 58 | }, 59 | "autoload-dev": { 60 | "psr-4": { 61 | "SimpleBus\\AsynchronousBundle\\Tests\\": "tests" 62 | } 63 | }, 64 | "minimum-stability": "dev", 65 | "prefer-stable": true 66 | } 67 | -------------------------------------------------------------------------------- /src/SimpleBusAsynchronousBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass( 25 | new ConfigureMiddlewares('simple_bus.asynchronous.command_bus', 'asynchronous_command_bus_middleware') 26 | ); 27 | $container->addCompilerPass( 28 | new AutoRegister('asynchronous_command_handler', 'handles'), 29 | PassConfig::TYPE_BEFORE_OPTIMIZATION, 30 | 10 31 | ); 32 | $container->addCompilerPass( 33 | new RegisterHandlers( 34 | 'simple_bus.asynchronous.command_bus.command_handler_map', 35 | 'simple_bus.asynchronous.command_bus.command_handler_service_locator', 36 | 'asynchronous_command_handler', 37 | 'handles' 38 | ) 39 | ); 40 | 41 | $container->addCompilerPass( 42 | new ConfigureMiddlewares('simple_bus.asynchronous.event_bus', 'asynchronous_event_bus_middleware') 43 | ); 44 | $container->addCompilerPass( 45 | new AutoRegister('asynchronous_event_subscriber', 'subscribes_to'), 46 | PassConfig::TYPE_BEFORE_OPTIMIZATION, 47 | 10 48 | ); 49 | $container->addCompilerPass( 50 | new RegisterSubscribers( 51 | 'simple_bus.asynchronous.event_bus.event_subscribers_collection', 52 | 'simple_bus.asynchronous.event_bus.event_subscribers_service_locator', 53 | 'asynchronous_event_subscriber', 54 | 'subscribes_to' 55 | ) 56 | ); 57 | 58 | $container->addCompilerPass(new CollectAsynchronousEventNames()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 15 | } 16 | 17 | public function getConfigTreeBuilder(): TreeBuilder 18 | { 19 | $treeBuilder = new TreeBuilder($this->alias); 20 | $rootNode = $treeBuilder->getRootNode(); 21 | 22 | $rootNode 23 | ->children() 24 | ->scalarNode('object_serializer_service_id') 25 | ->info('Service id of an instance of ObjectSerializer') 26 | ->isRequired() 27 | ->end() 28 | ->arrayNode('commands') 29 | ->canBeEnabled() 30 | ->children() 31 | ->scalarNode('publisher_service_id') 32 | ->info('Service id of an instance of Publisher') 33 | ->isRequired() 34 | ->end() 35 | ->arrayNode('logging') 36 | ->canBeEnabled() 37 | ->end() 38 | ->end() 39 | ->end() 40 | ->arrayNode('events') 41 | ->canBeEnabled() 42 | ->children() 43 | ->arrayNode('strategy') 44 | ->addDefaultsIfNotSet() 45 | ->beforeNormalization() 46 | ->ifInArray(['always', 'predefined']) 47 | ->then(function ($v) { 48 | $map = [ 49 | 'always' => 'simple_bus.asynchronous.always_publishes_messages_middleware', 50 | 'predefined' => 'simple_bus.asynchronous.publishes_predefined_messages_middleware', 51 | ]; 52 | 53 | return [ 54 | 'strategy_service_id' => $map[$v], 55 | ]; 56 | }) 57 | ->end() 58 | ->info('What strategy to use to publish messages') 59 | ->children() 60 | ->scalarNode('strategy_service_id') 61 | ->defaultValue('simple_bus.asynchronous.always_publishes_messages_middleware') 62 | ->end() 63 | ->end() 64 | ->end() 65 | ->scalarNode('publisher_service_id') 66 | ->info('Service id of an instance of Publisher') 67 | ->isRequired() 68 | ->end() 69 | ->arrayNode('logging') 70 | ->canBeEnabled() 71 | ->end() 72 | ->end() 73 | ->end() 74 | ->end(); 75 | 76 | return $treeBuilder; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Resources/config/asynchronous_events.yml: -------------------------------------------------------------------------------- 1 | services: 2 | SimpleBus\AsynchronousBundle\Bus\AsynchronousEventBus: 3 | alias: simple_bus.asynchronous.event_bus 4 | 5 | asynchronous_event_bus: 6 | alias: simple_bus.asynchronous.event_bus 7 | public: true 8 | 9 | simple_bus.asynchronous.event_bus: 10 | class: SimpleBus\AsynchronousBundle\Bus\AsynchronousEventBus 11 | public: false 12 | tags: 13 | - { name: message_bus, type: event, middleware_tag: asynchronous_event_bus_middleware } 14 | 15 | simple_bus.asynchronous.always_publishes_messages_middleware: 16 | class: SimpleBus\Asynchronous\MessageBus\AlwaysPublishesMessages 17 | public: false 18 | arguments: 19 | - '@simple_bus.asynchronous.event_publisher' 20 | 21 | simple_bus.asynchronous.publishes_predefined_messages_middleware: 22 | class: SimpleBus\Asynchronous\MessageBus\PublishesPredefinedMessages 23 | public: false 24 | arguments: 25 | - '@simple_bus.asynchronous.event_publisher' 26 | - '@simple_bus.event_bus.event_name_resolver' 27 | - [] 28 | 29 | simple_bus.asynchronous.event_bus.finishes_message_before_handling_next_middleware: 30 | class: SimpleBus\Message\Bus\Middleware\FinishesHandlingMessageBeforeHandlingNext 31 | public: false 32 | tags: 33 | - { name: asynchronous_event_bus_middleware, priority: 1000 } 34 | 35 | simple_bus.asynchronous.event_bus.notifies_message_subscribers_middleware: 36 | class: SimpleBus\Message\Subscriber\NotifiesMessageSubscribersMiddleware 37 | public: false 38 | arguments: 39 | - '@simple_bus.asynchronous.event_bus.event_subscribers_resolver' 40 | tags: 41 | - { name: asynchronous_event_bus_middleware, priority: -1000 } 42 | 43 | simple_bus.asynchronous.event_bus.callable_resolver: 44 | class: SimpleBus\Message\CallableResolver\ServiceLocatorAwareCallableResolver 45 | public: false 46 | arguments: 47 | - ['@simple_bus.asynchronous.event_bus.event_subscribers_service_locator', 'get'] 48 | 49 | simple_bus.asynchronous.event_bus.event_subscribers_service_locator: 50 | class: Symfony\Component\DependencyInjection\ServiceLocator 51 | tags: ['container.service_locator'] 52 | arguments: 53 | # collection of command handler service ids, will be provided by the RegisterHandlers compiler pass 54 | - [] 55 | 56 | simple_bus.asynchronous.event_bus.event_subscribers_collection: 57 | class: SimpleBus\Message\CallableResolver\CallableCollection 58 | public: false 59 | arguments: 60 | # collection of event subscriber services ids, will be provided by the RegisterSubscribers compiler pas 61 | - [] 62 | - '@simple_bus.asynchronous.event_bus.callable_resolver' 63 | 64 | simple_bus.asynchronous.event_bus.event_subscribers_resolver: 65 | class: SimpleBus\Message\Subscriber\Resolver\NameBasedMessageSubscriberResolver 66 | public: false 67 | arguments: 68 | - '@simple_bus.asynchronous.event_bus.event_name_resolver' 69 | - '@simple_bus.asynchronous.event_bus.event_subscribers_collection' 70 | 71 | simple_bus.asynchronous.standard_serialized_event_envelope_consumer: 72 | class: SimpleBus\Asynchronous\Consumer\StandardSerializedEnvelopeConsumer 73 | public: false 74 | arguments: 75 | - '@simple_bus.asynchronous.message_serializer' 76 | - '@simple_bus.asynchronous.event_bus' 77 | -------------------------------------------------------------------------------- /src/Resources/config/asynchronous_commands.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | simple_bus.asynchronous.command_bus.unhandled_messages_log_level: debug 3 | 4 | services: 5 | SimpleBus\AsynchronousBundle\Bus\AsynchronousCommandBus: 6 | alias: simple_bus.asynchronous.command_bus 7 | 8 | asynchronous_command_bus: 9 | alias: simple_bus.asynchronous.command_bus 10 | public: true 11 | 12 | simple_bus.asynchronous.command_bus: 13 | class: SimpleBus\AsynchronousBundle\Bus\AsynchronousCommandBus 14 | public: false 15 | tags: 16 | - { name: message_bus, type: command, middleware_tag: asynchronous_command_bus_middleware } 17 | 18 | simple_bus.asynchronous.command_bus.delegates_to_message_handler_middleware: 19 | class: SimpleBus\Message\Handler\DelegatesToMessageHandlerMiddleware 20 | public: false 21 | arguments: 22 | - '@simple_bus.asynchronous.command_bus.command_handler_resolver' 23 | tags: 24 | - { name: asynchronous_command_bus_middleware, priority: -1000 } 25 | 26 | simple_bus.asynchronous.command_bus.callable_resolver: 27 | class: SimpleBus\Message\CallableResolver\ServiceLocatorAwareCallableResolver 28 | public: false 29 | arguments: 30 | - ['@simple_bus.asynchronous.command_bus.command_handler_service_locator', 'get'] 31 | 32 | simple_bus.asynchronous.command_bus.command_handler_service_locator: 33 | class: Symfony\Component\DependencyInjection\ServiceLocator 34 | tags: ['container.service_locator'] 35 | arguments: 36 | # collection of command handler service ids, will be provided by the RegisterHandlers compiler pass 37 | - [] 38 | 39 | simple_bus.asynchronous.command_bus.command_handler_map: 40 | class: SimpleBus\Message\CallableResolver\CallableMap 41 | public: false 42 | arguments: 43 | # collection of command handler service ids, will be provided by the RegisterHandlers compiler pass 44 | - [] 45 | - '@simple_bus.asynchronous.command_bus.callable_resolver' 46 | 47 | simple_bus.asynchronous.command_bus.command_handler_resolver: 48 | class: SimpleBus\Message\Handler\Resolver\NameBasedMessageHandlerResolver 49 | public: false 50 | arguments: 51 | - '@simple_bus.asynchronous.command_bus.command_name_resolver' 52 | - '@simple_bus.asynchronous.command_bus.command_handler_map' 53 | 54 | simple_bus.asynchronous.command_bus.finishes_command_before_handling_next_middleware: 55 | class: SimpleBus\Message\Bus\Middleware\FinishesHandlingMessageBeforeHandlingNext 56 | public: false 57 | tags: 58 | - { name: asynchronous_command_bus_middleware, priority: 1000 } 59 | 60 | simple_bus.asynchronous.command_bus.publishes_unhandled_commands_middleware: 61 | class: SimpleBus\Asynchronous\MessageBus\PublishesUnhandledMessages 62 | arguments: 63 | - '@simple_bus.asynchronous.command_publisher' 64 | - '@logger' 65 | - '%simple_bus.asynchronous.command_bus.unhandled_messages_log_level%' 66 | tags: 67 | - { name: command_bus_middleware, priority: -999 } 68 | - { name: monolog.logger, channel: command_bus } 69 | 70 | simple_bus.asynchronous.standard_serialized_command_envelope_consumer: 71 | class: SimpleBus\Asynchronous\Consumer\StandardSerializedEnvelopeConsumer 72 | public: false 73 | arguments: 74 | - '@simple_bus.asynchronous.message_serializer' 75 | - '@simple_bus.asynchronous.command_bus' 76 | -------------------------------------------------------------------------------- /src/DependencyInjection/SimpleBusAsynchronousExtension.php: -------------------------------------------------------------------------------- 1 | alias = $alias; 19 | } 20 | 21 | public function getAlias(): string 22 | { 23 | return $this->alias; 24 | } 25 | 26 | /** 27 | * @param mixed[] $config 28 | */ 29 | public function getConfiguration(array $config, ContainerBuilder $container): Configuration 30 | { 31 | return new Configuration($this->alias); 32 | } 33 | 34 | /** 35 | * @param mixed[] $mergedConfig 36 | */ 37 | protected function loadInternal(array $mergedConfig, ContainerBuilder $container): void 38 | { 39 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 40 | 41 | $loader->load('asynchronous_serialization.yml'); 42 | $container->setAlias( 43 | 'simple_bus.asynchronous.object_serializer', 44 | $mergedConfig['object_serializer_service_id'] 45 | ); 46 | 47 | if ($mergedConfig['commands']['enabled']) { 48 | $this->loadAsynchronousCommandBus($mergedConfig['commands'], $container, $loader); 49 | } 50 | 51 | if ($mergedConfig['events']['enabled']) { 52 | $this->loadAsynchronousEventBus($mergedConfig['events'], $container, $loader); 53 | } 54 | } 55 | 56 | private function requireBundle(string $bundleName, ContainerBuilder $container): void 57 | { 58 | $enabledBundles = (array) $container->getParameter('kernel.bundles'); 59 | if (!isset($enabledBundles[$bundleName])) { 60 | throw new LogicException(sprintf('You need to enable "%s" as well', $bundleName)); 61 | } 62 | } 63 | 64 | /** 65 | * @param mixed[] $config 66 | */ 67 | private function loadAsynchronousCommandBus(array $config, ContainerBuilder $container, LoaderInterface $loader): void 68 | { 69 | $this->requireBundle('SimpleBusCommandBusBundle', $container); 70 | $loader->load('asynchronous_commands.yml'); 71 | 72 | $container->setAlias( 73 | 'simple_bus.asynchronous.command_bus.command_name_resolver', 74 | 'simple_bus.command_bus.command_name_resolver' 75 | ); 76 | 77 | $container->setAlias( 78 | 'simple_bus.asynchronous.command_publisher', 79 | $config['publisher_service_id'] 80 | ); 81 | 82 | if ($config['logging']['enabled']) { 83 | $loader->load('asynchronous_commands_logging.yml'); 84 | } 85 | } 86 | 87 | /** 88 | * @param mixed[] $config 89 | */ 90 | private function loadAsynchronousEventBus(array $config, ContainerBuilder $container, LoaderInterface $loader): void 91 | { 92 | $this->requireBundle('SimpleBusEventBusBundle', $container); 93 | $loader->load('asynchronous_events.yml'); 94 | 95 | $container->setAlias( 96 | 'simple_bus.asynchronous.event_bus.event_name_resolver', 97 | 'simple_bus.event_bus.event_name_resolver' 98 | ); 99 | 100 | $container->setAlias( 101 | 'simple_bus.asynchronous.event_publisher', 102 | $config['publisher_service_id'] 103 | ); 104 | 105 | if ($config['logging']['enabled']) { 106 | $loader->load('asynchronous_events_logging.yml'); 107 | } 108 | 109 | $eventMiddleware = $config['strategy']['strategy_service_id']; 110 | 111 | // insert before the middleware that actually notifies a message subscriber of the message 112 | $container->getDefinition($eventMiddleware)->addTag('event_bus_middleware', ['priority' => 0]); 113 | } 114 | } 115 | --------------------------------------------------------------------------------