danielkorytek/messenger-bridge-bundle
Installation:
composer require danielkorytek/messenger-bridge-bundle
Ensure the bundle is enabled in config/bundles.php:
return [
// ...
DanielKorytek\MessengerBridgeBundle\DanielKorytekMessengerBridgeBundle::class => ['all' => true],
];
Configure Routing Key Middleware:
Add the middleware to your messenger.yml under the desired bus (e.g., shared.message.bus):
framework:
messenger:
buses:
shared.message.bus:
middleware:
- messenger.bridge.middleware.routing_key
Define a Custom Routing Key Resolver:
Implement RoutingKeyResolverInterface and register it as a service:
messenger.bridge.routing.app_id_routing_key_resolver:
class: App\Message\Routing\AppIdRoutingKeyResolver
arguments: ['@some.service']
tags: ['container.service']
First Use Case:
Dispatch a message with a routing key (e.g., user.created). The middleware will append the locale (or other logic) dynamically:
$bus->dispatch(new UserCreatedEvent($userId));
Locale-Aware Routing:
Use the RoutingKeyMiddleware to dynamically modify routing keys based on the current locale or app ID:
# Example: Append locale to routing key
messenger.bridge.routing.app_id_routing_key_resolver:
class: DanielKorytek\MessengerBridgeBundle\Message\Routing\RoutingKeyResolver\AppIdRoutingKeyResolver
arguments: ['@request_stack']
Bus-Specific Configuration:
Attach the middleware only to buses handling cross-app messages (e.g., shared.message.bus), not internal buses like command.bus.
Custom Resolver Logic:
Extend AppIdRoutingKeyResolver to implement business-specific rules (e.g., tenant ID, priority flags):
class TenantAwareRoutingKeyResolver implements RoutingKeyResolverInterface
{
public function resolve(string $routingKey, MessageInterface $message): string
{
$tenantId = $message->getTenantId();
return "tenant.$tenantId.$routingKey";
}
}
Integration with Docplanner:
Align routing keys with Docplanner’s messaging standard (e.g., docplanner.user.created). Use the middleware to transform internal keys (e.g., user.created) to Docplanner-compatible formats.
Debugging Routing Keys: Log the resolved routing key in your resolver for debugging:
public function resolve(string $routingKey, MessageInterface $message): string
{
$resolvedKey = "prefix.$routingKey";
\Log::debug("Resolved routing key: {$resolvedKey}", ['message' => $message]);
return $resolvedKey;
}
Symfony Version Compatibility:
Use the correct middleware class based on your Symfony version (e.g., Symf51\RoutingKeyMiddleware for Symfony ≥ 5.1).
Performance: Avoid heavy logic in resolvers. Cache or pre-compute values if needed (e.g., tenant ID from a session).
Middleware Order Matters:
Place messenger.bridge.middleware.routing_key before any middleware that might modify the message (e.g., handlers or serializers). Incorrect ordering can lead to unresolved routing keys.
Symfony Version Mismatch:
Using the wrong middleware class (e.g., RoutingKeyMiddleware for Symfony ≥ 5.1) will cause a ClassNotFoundException. Always check the README for version-specific instructions.
Circular Dependencies:
If your resolver depends on services that aren’t autowired (e.g., RequestStack), ensure they’re properly configured in services.yaml:
messenger.bridge.routing.app_id_routing_key_resolver:
arguments:
$requestStack: '@request_stack'
Routing Key Collisions:
Poorly designed resolvers may generate duplicate or conflicting routing keys (e.g., locale.en.user.created vs. en.user.created). Test with edge cases like empty locales or IDs.
Serialization Issues:
The package relies on Symfony Messenger’s serializer. Ensure your messages implement MessageInterface and are properly annotated for serialization:
#[AsMessage]
class UserCreatedEvent implements MessageInterface
{
// ...
}
Check Resolver Output: Temporarily log the resolved routing key in your resolver to verify transformations:
\Log::debug('Original key:', [$routingKey]);
\Log::debug('Resolved key:', [$resolvedKey]);
Middleware Debugging: Enable Symfony’s messenger debug mode to inspect the middleware stack:
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'App\Message\UserCreatedEvent': async
buses:
shared.message.bus:
middleware:
- messenger.bridge.middleware.routing_key
- messenger.debug.middleware # Add this for debugging
Transport-Level Inspection:
Use a transport like doctrine or redis with logging enabled to verify messages are dispatched with the correct routing key.
Custom Middleware:
Extend RoutingKeyMiddleware to add pre/post-processing logic:
class CustomRoutingKeyMiddleware extends RoutingKeyMiddleware
{
public function handle(MessageInterface $message, callable $next): void
{
// Pre-processing
$resolvedKey = $this->resolver->resolve($message->getRoutingKey(), $message);
$message->setRoutingKey($resolvedKey);
// Call next middleware
$next($message);
}
}
Dynamic Resolver Selection: Use parameter bags or DI to switch resolvers based on environment or message type:
messenger.bridge.routing_key_resolver:
class: App\Message\Routing\DynamicRoutingKeyResolver
arguments:
- '%kernel.environment%'
Event Listeners:
Attach listeners to MESSENGER_MESSAGE_SENT to log or validate routing keys:
$eventDispatcher->addListener(
MessengerEvents::MESSAGE_SENT,
fn (MessageSentEvent $event) => \Log::info('Sent message with key:', [$event->getMessage()->getRoutingKey()])
);
Testing: Mock the resolver in tests to avoid real dependencies:
$resolver = $this->createMock(RoutingKeyResolverInterface::class);
$resolver->method('resolve')->willReturn('test.key');
$middleware = new RoutingKeyMiddleware($resolver);
How can I help you explore Laravel packages today?