Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bernard Bundle Bridge Laravel Package

simple-bus/bernard-bundle-bridge

Bridge between SimpleBus and BernardBundle that lets Symfony applications dispatch commands and events through Bernard queues. It wires message buses to asynchronous transports so handlers can run in background workers, with configuration for routing messages to queues.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is thin glue: it connects SimpleBus/AsynchronousBundle (which decides which messages should be published asynchronously and how to serialize them) to BernardBundle (which decides where they are stored/transported — Doctrine, SQS, IronMQ, Redis, MongoDB, etc.). It is not a message bus and not a queue; you need both sides already installed and configured.

Minimal bootstrap on a Symfony 2/3 application:

composer require simple-bus/bernard-bundle-bridge
// app/AppKernel.php
new Bernard\BernardBundle\BernardBundle(),
new SimpleBus\AsynchronousBundle\SimpleBusAsynchronousBundle(),
new SimpleBus\BernardBundleBridge\BernardBundleBridge(), // verify the exact class name in src/
# app/config/config.yml
bernard:
    driver: doctrine   # or sqs, redis, ironmq, mongodb, ...
    doctrine: { connection: default }

simple_bus_bernard_bridge:
    queue_name: simple_bus

Then mark commands/events as asynchronous using AsynchronousBundle's configuration (its documented simple_bus_asynchronous block lists the command/event classes that should be published rather than handled inline). Anything routed as "async" is handed to the bridge, serialized by SimpleBus, and pushed to the Bernard queue you named.

First use case to prove the wiring: a single slow command (send a newsletter, generate a report). Dispatch it through the command bus from a controller. It should return immediately, a row/message should appear in the Bernard transport, and the message should be handled by:

app/console bernard:consume simple_bus

Where to look first, in this order:

  1. DependencyInjection/Configuration.php — the only authoritative source for config keys (queue_name, serializer service, etc.).
  2. Resources/config/services.xml — service IDs, the bernard.consumer tag, and how the producer decorates SimpleBus's publisher. This tells you what to inject and what to override.
  3. src/ — you will find a producer (a SimpleBus Publisher implementation calling Bernard's Producer), a consumer (a Bernard Consumer implementation calling SimpleBus's message handler), and a queue-name resolver.
  4. SimpleBus/AsynchronousBundle's README, for the boundary between "the bridge" and "the bundle that decides what is async".

A caveat before adopting: the package is archived, last released 2017, effectively unmaintained, with 2 stars. Use it only to keep an existing Symfony 2/3 application running; for anything new, treat it as a migration target (Symfony Messenger is the modern equivalent).

Implementation Patterns

One queue, or route by type. The default configuration uses a single fixed queue (a fixed queue-name resolver). That is fine for a small app: every async command and event lands in simple_bus and one worker pool handles them. When a slow event class starts starving your commands, replace the fixed resolver with a custom implementation that inspects the message and returns a queue name per type, then run one bernard:consume <queue> process per queue. This is the primary extension point.

Producer side: publish through the bus, not the queue. Do not inject Bernard's producer into controllers. Dispatch through the normal command/event bus and let AsynchronousBundle + the bridge decide whether it is handled inline or published. Keeps sync/async a configuration concern and makes it trivial to flip a message back to synchronous for debugging. If you must publish manually, inject SimpleBus's asynchronous publisher service (find its ID in services.xml) so serialization and queue resolution stay consistent.

Consumer side: run it as a long-lived daemon. bernard:consume <queue> is a worker. Run it under supervisor/systemd with --no-debug in production, and size your pool to the transport. For Doctrine transports, that means multiple bernard:consume processes against the same table with an appropriate visibility/lock timeout; for SQS/Redis, watch prefetch and visibility timeouts. Always bound the work: a handler that takes longer than the visibility timeout will be redelivered and processed twice.

Keep messages small and self-describing. The message is serialized, stored, possibly retried days later, and deserialized by a different process running a possibly different deploy. Send scalars and IDs; re-load entities from the database inside the handler. Add an explicit idempotency key to messages whose handlers touch money, email, or external APIs, and enforce it with a unique constraint or a processed-messages table — you are on an at-least-once transport.

Custom serializer / metadata. SimpleBus's serialization layer needs class metadata (JMS or Symfony serializer with the appropriate mapping). If you send domain objects with nested graphs, you will fight the serializer; prefer DTOs. If you need a different format, implement the bridge's serializer interface and point the bundle at your service. Remember both ends must agree: the producer serializes, the consumer deserializes, so change both in the same deploy.

Testing pattern. Unit-test handlers directly — they are plain callables and should not depend on the bus or the queue. For integration, swap the asynchronous publisher for SimpleBus's synchronous/in-memory implementation (or configure the message as sync) so the test exercises the handler without a worker. For a full round-trip, run Bernard with an in-memory or filesystem transport and consume from the test itself.

Migration sketch. Mapping the concepts to Symfony Messenger clarifies what you actually rely on: the bridge producer becomes a bus dispatch with a routing rule (routing: { 'App\Message\Foo': async }), bernard:consume <queue> becomes messenger:consume async, the queue-name resolver becomes the transport/routing map, and the serializer becomes Messenger's serializer. Handlers move from SimpleBus handler registration to #[AsMessageHandler]/messenger.message_handler tags.

Gotchas and Tips

  • Archived and Symfony 2/3-era. No Symfony 4+/Flex support, no PHP 7.2+ testing, no security updates. Expect manual service wiring if you try to use it on a modern kernel. Do not put it on a new project; budget the Messenger migration instead.
  • Bernard itself is abandoned. The bridge is only as healthy as the transport beneath it. Doctrine transport means a database table you must create/migrate; SQS/Redis/IronMQ have their own operational quirks. Check your chosen driver before blaming the bridge.
  • The consumer is only registered for the configured queue. If you change simple_bus_bernard_bridge.queue_name but still run the old worker command, messages pile up in a queue nobody consumes. Dump the container (debug:container / container:debug) and confirm which bernard.consumer tagged services exist and for which queue names.
  • Serialization failures are silent-ish and painful. A queued payload with a renamed class, a removed property, or a changed constructor will fail at deserialization time — inside a worker, long after the code that produced it. Keep messages as stable DTOs with explicit versions, and log deserialization exceptions with the raw payload. Treat message classes as a public API.
  • private properties and getters. Depending on the serializer in use, private state may not round-trip without mapping/accessor configuration. Verify by asserting serialize(deserialize(x)) == x for each message class in a test — this catches most production surprises cheaply.
  • No built-in retry/DLQ. Failure semantics come from the transport. Decide what happens when a handler throws: log-and-ack (lose the message), nack/requeue (risk poison-message loops), or catch-and-persist to a failed-messages table. There is no messenger:failed equivalent here — you build it.
  • Ordering and duplicates. At-least-once delivery plus multiple workers means handlers must be idempotent and you cannot assume per-entity ordering across workers. If ordering matters, route those messages to a dedicated queue and run a single worker on it.
  • Sync/async double handling. If a message is marked async in AsynchronousBundle but something in the pipeline also handles it inline (a listener, a manual dispatch, a test config), it runs twice. Audit the AsynchronousBundle config when you add a new message type.
  • Debugging checklist. (1) Did the message reach the transport — inspect the Doctrine table / queue dashboard. (2) Is a worker running for that exact queue name. (3) Does the worker log show a deserialization or handler exception. (4) Is the handler registered on the bus the consumer uses (command bus vs event bus are separate). (5) Is the message class mapped by the serializer.
  • Extension points worth knowing. Queue-name resolver (routing), serializer (wire format), consumer (dispatch strategy / middleware such as logging or transaction wrapping), and the publisher decorator (for tracing, metrics, or "publish after commit"). These are the four seams; anything you want to change day-to-day lives at one of them.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package