-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Messenger] Define multiple buses from the framework.messenger.buses
configuration
#26864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
IMHO, the I wonder if we shouldn't even remove the DI tag or just keep it for the default bus if |
I tend to prefer DI config over semantic middleware config, thus keep the tag. Concerning attributes i'd say Last but not least, i think 1 (default) bus should be configured minimum. Alternatively it could wire pre-defined middlewares on a per-case basis, e.g. framework:
messenger:
buses:
events:
tolerate_no_handler: true |
That's how I started it. The problem with this approach is how to place a middleware between/before/after the ones that are configured with the tags (and need priorities). I don't see any harm in being able to configure the priority, especially that when you don't, the configuration syntax is super easy. (i.e. by default, it's all middlewares priority 0, in the order in which you've put them in the configuration).
It's actually convenient to do so within the code base to simplify the handling of either a middleware is only applied to a given set of buses or all of them, hence me doing this way here. I don't mind changing it but I don't have a strong argument in doing so tbh.
That's the case if you don't configure your owns. If you do, then I'd argue it's now up to you :) |
Right, my bad. I missed the default bus entry in config :)
IMHO it's more pure :) but no strong opinion either. About You have to configure a service anyway, having to specify it here again is double bookkeeping, also this is not the place to infer new service definitions or so. IMHO :) Having a single way of doing things is nice, and given you need to define the service i'd say tag it as a middleware as well, if needed. SO we should either favor the tag or |
That's why I'd suggest removing the tag 😄 |
A tag seems more flexible though :) considering third parties who want to register a middleware. Or put different what about the default middlewares currently? |
That's the point to me: it's only a userland config. No third party implied. A third party could just help registering a default service to use, or provide a factory.
They can just be configured as default values for the middlewares node. |
Well.. i was considering exactly that, register/tag a middleware to capture any message dispatched and pass it to a service. |
I think that your conversation is a good example of why we need to keep both configurations. @ogizanagi As far as I understand, your concern is that it can introduce some complexity about knowing where/how the middlewares are configuration: I appreciate that but I don't believe this will be the case as it is basically a question of how you, as a developer, use these configuration mechanisms. Though, if we see a large proportion of issues coming that are related to that, we can later decide which one to deprecate I guess. |
Hello all, |
At this point im 👍 for keeping both configs (DI+semantic). Please dont remove tagging middlewares, it makes it real hard to hook in and basically defeats the feature as decorating the bus at this point is probably easier 😂
It's probably the bundle's intend to enable the middleware on all buses. Otherwise it should allow you to configure the bus service to use. I.e. How to easily unregister the middleware from one bus? seems highly hypothetical. Also we never had this debate with bundles tagging e.g. |
I've rebased the pull-request so it contains the new middleware tag. |
@ogizanagi What if you could enable/disable tags from the |
If we drop tags, "bundles" could use the prependConfiguration to have some sort of automated configuration. |
Im really curious what others think about @ogizanagi's statement:
I tend to disagree with that. Why is a bus middleware "userland-config" and a kernel event listener not? |
So, we've talked a bit on Slack with @sroze today. Here's mostly what I shared with him about my point of view: Here is how I see a typical configuration would look like with semantic config only and removing the DI tag: framework:
messenger:
buses:
commands:
middlewares:
- logging # fwb alias
- validation # fwb alias
# DoctrineBundle transaction middleware.
# This demonstrates support of factories we could provide, i.e the doctrine_transaction_middleware
# is an abstract service the FrameworkBundle would decline with following arguments
# (here "default" being the manager name to use. Only simple scalar arguments, i.e no services references)
- doctrine_transaction_middleware: [default]
# Third-parties would just have to provide a service id (or factories for most custom needs) and document usage.
# No automatic middleware registering, the user opt-in explicitly for each bus.
- msg_php.console_message_subscriber_middleware
# userland middleware service id
- app.messenger.my_custom_middleware
- message_handler # alias
events:
middlewares:
- logging
- tolerate_no_handler # fwb alias
- message_handler where Everything is configured in one place and explicit. Nothing behind the scene. No need for priority. No need for third-parties to register middlewares themselves (just to provide and document middleware services ids/factories). Supporting both config & DI tag means decentralizing the middleware stack, struggling with priorities (even if the debug command would help) and allowing third-parties to register middleware automagically (which I really don't think is useful). Even the one legit use-case I see within the framework can be solved differently: the Finally, I used Tactician for more than 2 years. This is how the middlewares are registered by the bundle (but simpler, no factory support). Never had to complain and I'm not aware of Anyway, if a majority still prefers keeping the DI tag, then I'd be in favor of no semantic config at all. Supporting both just seems confusing and pointless to me. :) |
ok, more thinking about it and decorating instead sounds reasonable, if not better :) in my case it's also about capturing dispatch messages like data collector does. Config looks good 👍 perhaps denote built-in aliases (non service ids i guess) with |
To me, fwb known aliases would always win and are unlikely to collide with actual middlewares' ids, but yes, I guess a way to denote built-in aliases would be nice :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for dropping the tag, not worth the confusion it could cause IMHO.
return; | ||
} | ||
|
||
$busesNames = $container->getParameter('messenger.buses_names'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the parameter should be removed once used, no need to expose user config
@@ -1448,6 +1449,26 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder | |||
|
|||
$loader->load('messenger.xml'); | |||
|
|||
$container->setParameter('messenger.buses_names', array_keys($config['buses'])); | |||
foreach ($config['buses'] as $name => $bus) { | |||
$container->setDefinition($busId = 'messenger.bus.'.$name, (new Definition(MessageBus::class, array(array())))->addTag('messenger.bus')->setPublic(true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about keeping them private? IMHO there is no reason to not use proper dependency injection for controllers as of 3.4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd put an abstract messenger.bus
definition in xml to be extended here also, helps for discoverability (e.g. with phpstorm plugin that links to definitions)
} | ||
|
||
$container->getDefinition($middleware['service'])->addTag('messenger.bus_middleware', array( | ||
'bus' => $name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be on one line
foreach ($config['buses'] as $name => $bus) { | ||
$container->setDefinition($busId = 'messenger.bus.'.$name, (new Definition(MessageBus::class, array(array())))->addTag('messenger.bus')->setPublic(true)); | ||
|
||
if ($name == $config['default_bus']) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better use strict comparison
foreach ($container->findTaggedServiceIds('messenger.bus_middleware') as $serviceId => $tags) { | ||
foreach ($tags as $tag) { | ||
$priority = $tag['priority'] ?? 0; | ||
$buses = isset($tag['bus']) ? explode(',', $tag['bus']) : $busesNames; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not fond of the special syntax, AFAIK we are used to require adding as much tags as needed e.g.
App\SomeMiddleware:
tags:
- { name: "messenger.bus_middleware", bus: "query" }
- { name: "messenger.bus_middleware", bus: "command" }
62b4115
to
35e4323
Compare
if (!$container->has($middleware['service'])) { | ||
$container->setDefinition($middleware['service'], new Definition($middleware['service'])); | ||
} | ||
$container->setDefinition( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so in user-land you need a compiler pass, find all buses by tag (messenger.bus
), and decorate each one.
or is it better to decorate call_message_handler
once?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say it depends on your use-case. Both approaches would work.
I've just started to test this PR on a project. It works well. It is not easy to configure but it is flexible. With "easy" I mean: It would be nice to have:
Im not sure that is a realistic because whatever a "command_bus" is for me it might not be the same for you. So Im +1 with the config. I get an error though:
If I change my config to the following, it will work:
|
You are right, my example in the original pull-request description wasn't accurate anymore (updated now). The reason you had this exception is because it tried to create an instance of the middleware for this bus based on the parent definition that you define. In the example you mentioned, you could even: - 'messenger.middleware.tolerate_no_handler'
+ tolerate_no_handler Regarding the |
Hey guys! Sorry to come late after so much work.
I actually think this is a huge problem. Imagine a user wants to add a new middleware: they setup their config, set middleware to I'm not sure of the best solution - I think the argument around tags is valid, and the fact that experience with TacticianBundle has validated their approach a bit. Actually, one solution would be to be more like TacticianBundle: have zero default middleware out-of-the-box. Then, in the recipe, include the 3 middleware in |
@@ -1052,6 +1053,21 @@ function ($a) { | |||
->end() | |||
->end() | |||
->end() | |||
->scalarNode('default_bus')->defaultValue('default')->end() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we default this to null, then in the extension:
- If there is 1 extension, just use it
- If there are multiple, throw a clear exception that the
default_bus
key needs to be defined?
I could see a situation where someone sees:
The default bus named "default" is not defined. Define it or change the default bus name.
And thinking, where did default come from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I like that idea 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed it this way.
This means we can't auto-activate the messenger when we know it's installed (i.e. the What about having the following
As a developer, you can disable the "default middlewares" with: buses:
[name]:
default_middlewares: false |
I've pushed 3 commits:
|
|
||
$defaultMiddlewares = array('before' => array('logging'), 'after' => array('route_messages', 'call_message_handler')); | ||
if ($config['middlewares']['validation']['enabled']) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
at this point..shouldnt we let users this configure as a regular middleware, per bus, if needed? Extra configs feels a bit too much config IMHO. Perhaps try to prepend either way like 'logging'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or put different, it's a confusing config as it relies on default_middlewares
also.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one special middleware handling is indeed a little bit weird. Is there any special reason when you would not want this middleware? Could it ever cause issues? My instinct is to always include it when default_middlewares
is enabled. If you really don't want it for some reason, you can set that config to false (unless there's a somewhat common use-case for not wanting this middleware).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to opt-in to a validation middleware.. im not sure validating messages by default makes sense.
Also it's not like adding - validation
to your middlewares (per bus) is "bad config" or so IMHO :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any special reason when you would not want this middleware?
Yes, when the component is not included.
I tend to opt-in to a validation middleware.. im not sure validating messages by default makes sense.
Let's do this. So it is explicit and removes some extra configuration in there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than one small comment, yea, I like this approach. I can't think of any other part of the framework that has this exact problem, and so, the solution is also a bit unique. But, it's both practical (works immediately for the 90%) and extensible.
|
||
$defaultMiddlewares = array('before' => array('logging'), 'after' => array('route_messages', 'call_message_handler')); | ||
if ($config['middlewares']['validation']['enabled']) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one special middleware handling is indeed a little bit weird. Is there any special reason when you would not want this middleware? Could it ever cause issues? My instinct is to always include it when default_middlewares
is enabled. If you really don't want it for some reason, you can set that config to false (unless there's a somewhat common use-case for not wanting this middleware).
Ping @symfony/deciders! I'm 👍 for this approach, and it's a big PR, so we need to get it merged in soon. The most important aspect is how middleware are added. The final decision about that is explained very well here: #26864 (comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now we have the messenger.bus
tag instead of tagging middlewares.. from a vendor perspective im leaning to wire my own bus, thus tag it. Given that i think it should work like any other bus that comes from config.
There's no framework-way of asking for an event or command bus, and i want to avoid configuration for that on my side. So i'll autowire the default bus to be used for commands, but then i still need one for events. Alternatively i could wire a one-size-fits-all bus by injecting all tagged buses, and break on first successful bus handling a message.
} | ||
|
||
if ('messenger.middleware.validation' === $messengerMiddlewareId && !$container->has('validator')) { | ||
throw new RuntimeException('The Validation middleware is only available when the Validator component is installed and enabled. Try running "composer require symfony/validator".'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be nice if we can improve such error messages at the DI service level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a wider question within core. That's the best way I found to keep the great DX while using this middleware without the Validator component. I would keep it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #27004
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, this still belongs to the Fwb extension where we have all the keys to detect if validation is installed and enabled. The messenger.middleware.validation
is really specific to the Fwb wiring, so the pass alone doesn't have to deal with this unless strictly necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the point that this has little to do in here. Moved to the FrameworkBundle 👍
} | ||
|
||
if ($container->hasDefinition('messenger.data_collector')) { | ||
$this->registerBusToCollector($container, $busId, $tags[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we iterate $tags
as well or throw in case of >1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe there is a use-case for a bus to be tagged multiple times 🤔
$container->setAlias(MessageBusInterface::class, $busId); | ||
} | ||
|
||
$container->setDefinition( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should/can we process this at the compile pass level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, very good idea! So we add it only when we need it 💃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 With only minor comments. Thanks for considering what we discussed on Slack.
I'm not found of the default middlewares vs always using explicit configuration but I guess that's a good tradeoff.
'default_middlewares' => false, | ||
'middlewares' => array( | ||
'route_messages', | ||
'Symfony\\Component\\Messenger\\Middleware\\TolerateNoHandler', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit misleading reading the tests and seing this FQCN while the actual middleware exists and has a service id that should be used (or the shortcut), not the FQCN. Is it really relevant to test a FQCN here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point; changed.
{ | ||
private $traceableBuses = array(); | ||
|
||
public function registerBus(string $name, TraceableMessageBus $bus) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not injected in the constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to match with what already exists like the CacheDataCollector
for example.
$container->getParameterBag()->remove($busMiddlewaresParameter); | ||
} | ||
|
||
if ($container->getParameter('kernel.debug') && $container->hasDefinition('messenger.data_collector')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the $container->getParameter('kernel.debug')
check useful, or $container->hasDefinition('messenger.data_collector')
would be enough?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, we can remove the $container->getParameter('kernel.debug')
now.
} | ||
|
||
if ('messenger.middleware.validation' === $messengerMiddlewareId && !$container->has('validator')) { | ||
throw new RuntimeException('The Validation middleware is only available when the Validator component is installed and enabled. Try running "composer require symfony/validator".'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO, this still belongs to the Fwb extension where we have all the keys to detect if validation is installed and enabled. The messenger.middleware.validation
is really specific to the Fwb wiring, so the pass alone doesn't have to deal with this unless strictly necessary.
$config['default_bus'] = array_keys($config['buses'])[0]; | ||
} | ||
|
||
$defaultMiddlewares = array('before' => array('logging'), 'after' => array('route_messages', 'call_message_handler')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't validation
supposed to be in defaultMiddlewares['before']
if the component is enabled, as described in #26864 (comment) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #26864 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, let's stick with it :)
Repushed the last commit to make AppVeyor green again 💚 |
Let's merge! Thank you all for your feedback 🎉 |
…messenger.buses` configuration (sroze) This PR was squashed before being merged into the 4.1-dev branch (closes #26864). Discussion ---------- [Messenger] Define multiple buses from the `framework.messenger.buses` configuration | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #26652 | License | MIT | Doc PR | symfony/symfony-docs#9617 Not everybody will benefit from having only one bus, especially with the CQRS-like usages. While keeping the extremely use of use of the single bus, this PR has the following: - Create multiple buses from the YAML configuration - Tag middleware only a specific buses - Register middlewares from the YAML configuration Even if it's visible in the PR's tests, here's how it will look like, for a completely full-customised version: ```yaml framework: messenger: default_bus: commands buses: commands: ~ events: middlewares: - validation - route_messages - "Your\\Middleware\\Service" - call_message_handler ``` A few things to note: 1. The YAML configuration creates `messenger.bus.[name]` services for the buses. 2. The YAML configuration for middleware just adds tags to the corresponding middlewares. 3. If the middleware definition does not exists, it creates it. (without any magic on the arguments though, if it isn't auto-wirable, well... "your problem") 4. In the PR, there is this "TolerateNoHandler" middleware that is a great example for event buses Commits ------- e5deb84 [Messenger] Define multiple buses from the `framework.messenger.buses` configuration
…izanagi) This PR was squashed before being merged into the 4.1 branch (closes #27128). Discussion ---------- [Messenger] Middleware factories support in config | Q | A | ------------- | --- | Branch? | master <!-- see below --> | Bug fix? | no | New feature? | yes <!-- don't forget to update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | N/A <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | todo Following #26864, this would allow to configure easily the middlewares by using an abstract factory definition to which are provided simple arguments (just scalars, no services references). For instance, here is how the DoctrineBundle would benefit from such a feature (also solving the wiring of the `DoctrineTransactionMiddleware` reverted in #26684): ```yaml framework: messenger: buses: default: middleware: - logger - doctrine_transaction_middleware: ['entity_manager_name'] ``` where `doctrine_transaction_middleware` would be an abstract factory definition provided by the doctrine bundle: ```yml services: doctrine.orm.messenger.middleware_factory.transaction: class: Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddlewareFactory arguments: ['@doctrine'] doctrine_transaction_middleware: class: Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware factory: ['@doctrine.orm.messenger.middleware_factory.transaction', 'createMiddleware'] abstract: true # the default arguments to use when none provided from config. # i.e: # middlewares: # - doctrine_transaction_middleware: ~ arguments: ['default'] ``` and is interpreted as: ```yml buses: default: middleware: - id: logger arguments: { } - id: doctrine_transaction_middleware arguments: - entity_manager_name default_middleware: true ``` --- <details> <summary>Here is the whole config reference with these changes: </summary> ```yaml # Messenger configuration messenger: enabled: true routing: # Prototype message_class: senders: [] serializer: enabled: true format: json context: # Prototype name: ~ encoder: messenger.transport.serializer decoder: messenger.transport.serializer adapters: # Prototype name: dsn: ~ options: [] default_bus: null buses: # Prototype name: default_middleware: true middleware: # Prototype - id: ~ # Required arguments: [] ``` </details> Commits ------- f5ef421 [Messenger] Middleware factories support in config
…izanagi) This PR was squashed before being merged into the 4.1 branch (closes #27128). Discussion ---------- [Messenger] Middleware factories support in config | Q | A | ------------- | --- | Branch? | master <!-- see below --> | Bug fix? | no | New feature? | yes <!-- don't forget to update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | N/A <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | todo Following symfony/symfony#26864, this would allow to configure easily the middlewares by using an abstract factory definition to which are provided simple arguments (just scalars, no services references). For instance, here is how the DoctrineBundle would benefit from such a feature (also solving the wiring of the `DoctrineTransactionMiddleware` reverted in symfony/symfony#26684): ```yaml framework: messenger: buses: default: middleware: - logger - doctrine_transaction_middleware: ['entity_manager_name'] ``` where `doctrine_transaction_middleware` would be an abstract factory definition provided by the doctrine bundle: ```yml services: doctrine.orm.messenger.middleware_factory.transaction: class: Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddlewareFactory arguments: ['@doctrine'] doctrine_transaction_middleware: class: Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware factory: ['@doctrine.orm.messenger.middleware_factory.transaction', 'createMiddleware'] abstract: true # the default arguments to use when none provided from config. # i.e: # middlewares: # - doctrine_transaction_middleware: ~ arguments: ['default'] ``` and is interpreted as: ```yml buses: default: middleware: - id: logger arguments: { } - id: doctrine_transaction_middleware arguments: - entity_manager_name default_middleware: true ``` --- <details> <summary>Here is the whole config reference with these changes: </summary> ```yaml # Messenger configuration messenger: enabled: true routing: # Prototype message_class: senders: [] serializer: enabled: true format: json context: # Prototype name: ~ encoder: messenger.transport.serializer decoder: messenger.transport.serializer adapters: # Prototype name: dsn: ~ options: [] default_bus: null buses: # Prototype name: default_middleware: true middleware: # Prototype - id: ~ # Required arguments: [] ``` </details> Commits ------- f5ef421474 [Messenger] Middleware factories support in config
…izanagi) This PR was squashed before being merged into the 4.1 branch (closes #27128). Discussion ---------- [Messenger] Middleware factories support in config | Q | A | ------------- | --- | Branch? | master <!-- see below --> | Bug fix? | no | New feature? | yes <!-- don't forget to update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | N/A <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | todo Following symfony/symfony#26864, this would allow to configure easily the middlewares by using an abstract factory definition to which are provided simple arguments (just scalars, no services references). For instance, here is how the DoctrineBundle would benefit from such a feature (also solving the wiring of the `DoctrineTransactionMiddleware` reverted in symfony/symfony#26684): ```yaml framework: messenger: buses: default: middleware: - logger - doctrine_transaction_middleware: ['entity_manager_name'] ``` where `doctrine_transaction_middleware` would be an abstract factory definition provided by the doctrine bundle: ```yml services: doctrine.orm.messenger.middleware_factory.transaction: class: Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddlewareFactory arguments: ['@doctrine'] doctrine_transaction_middleware: class: Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware factory: ['@doctrine.orm.messenger.middleware_factory.transaction', 'createMiddleware'] abstract: true # the default arguments to use when none provided from config. # i.e: # middlewares: # - doctrine_transaction_middleware: ~ arguments: ['default'] ``` and is interpreted as: ```yml buses: default: middleware: - id: logger arguments: { } - id: doctrine_transaction_middleware arguments: - entity_manager_name default_middleware: true ``` --- <details> <summary>Here is the whole config reference with these changes: </summary> ```yaml # Messenger configuration messenger: enabled: true routing: # Prototype message_class: senders: [] serializer: enabled: true format: json context: # Prototype name: ~ encoder: messenger.transport.serializer decoder: messenger.transport.serializer adapters: # Prototype name: dsn: ~ options: [] default_bus: null buses: # Prototype name: default_middleware: true middleware: # Prototype - id: ~ # Required arguments: [] ``` </details> Commits ------- f5ef421474 [Messenger] Middleware factories support in config
…izanagi) This PR was squashed before being merged into the 4.1 branch (closes #27128). Discussion ---------- [Messenger] Middleware factories support in config | Q | A | ------------- | --- | Branch? | master <!-- see below --> | Bug fix? | no | New feature? | yes <!-- don't forget to update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | N/A <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | todo Following symfony/symfony#26864, this would allow to configure easily the middlewares by using an abstract factory definition to which are provided simple arguments (just scalars, no services references). For instance, here is how the DoctrineBundle would benefit from such a feature (also solving the wiring of the `DoctrineTransactionMiddleware` reverted in symfony/symfony#26684): ```yaml framework: messenger: buses: default: middleware: - logger - doctrine_transaction_middleware: ['entity_manager_name'] ``` where `doctrine_transaction_middleware` would be an abstract factory definition provided by the doctrine bundle: ```yml services: doctrine.orm.messenger.middleware_factory.transaction: class: Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddlewareFactory arguments: ['@doctrine'] doctrine_transaction_middleware: class: Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware factory: ['@doctrine.orm.messenger.middleware_factory.transaction', 'createMiddleware'] abstract: true # the default arguments to use when none provided from config. # i.e: # middlewares: # - doctrine_transaction_middleware: ~ arguments: ['default'] ``` and is interpreted as: ```yml buses: default: middleware: - id: logger arguments: { } - id: doctrine_transaction_middleware arguments: - entity_manager_name default_middleware: true ``` --- <details> <summary>Here is the whole config reference with these changes: </summary> ```yaml # Messenger configuration messenger: enabled: true routing: # Prototype message_class: senders: [] serializer: enabled: true format: json context: # Prototype name: ~ encoder: messenger.transport.serializer decoder: messenger.transport.serializer adapters: # Prototype name: dsn: ~ options: [] default_bus: null buses: # Prototype name: default_middleware: true middleware: # Prototype - id: ~ # Required arguments: [] ``` </details> Commits ------- f5ef421474 [Messenger] Middleware factories support in config
Not everybody will benefit from having only one bus, especially with the CQRS-like usages. While keeping the extremely use of use of the single bus, this PR has the following:
Even if it's visible in the PR's tests, here's how it will look like, for a completely full-customised version:
A few things to note:
messenger.bus.[name]
services for the buses.