Skip to content

re #21 add univeros/messaging — Symfony Messenger bridge#75

Merged
tonydspaniard merged 4 commits into
masterfrom
feat/21-messaging
May 27, 2026
Merged

re #21 add univeros/messaging — Symfony Messenger bridge#75
tonydspaniard merged 4 commits into
masterfrom
feat/21-messaging

Conversation

@tonydspaniard
Copy link
Copy Markdown
Member

@tonydspaniard tonydspaniard commented May 27, 2026

Closes #21.

Summary

Adds the univeros/messaging sub-package — a thin MessageBus + worker bridge over Symfony Messenger, wired through the framework's container, with attribute-driven handler discovery. Extends the scaffolder with a queue: block so a YAML spec emits a message DTO + handler stub + handler test alongside the HTTP artifacts.

What's in the package

  • Contracts: MessageBusInterface (dispatch(object, Stamp[]): Envelope) and a marker HandlerInterface. Application code only ever imports the framework contract, never Symfony\Component\Messenger\*.
  • Handlers via attribute: #[AsHandler(MessageClass::class)] on any class with an __invoke method is enough — no getHandledMessages() ceremony. AttributeHandlerDiscoverer scans handlerPaths at boot (mirroring Altair\Cli\Discovery\AttributeCommandDiscoverer's tokenizer-first approach so non-handler files are never autoloaded).
  • Transports from env: MESSENGER_TRANSPORT_DEFAULT=…, MESSENGER_TRANSPORT_HIGH=…, MESSENGER_FAILURE_TRANSPORT=…, MESSENGER_ROUTING="App\Msg\Foo:default,App\Msg\Bar:default|high". Sync + InMemory always available; Redis / Doctrine / AMQP / Beanstalkd detected reflectively if their bridge packages are installed.
  • Workers: bin/altair worker [--transports=…] [--time-limit=N] [--memory-limit=128M] [--limit=N] with PCNTL SIGTERM/SIGINT for graceful drain.
  • Failure flow: bin/altair worker:show-failed lists envelopes in the failure transport; worker:retry-failed --limit=N strips the SentToFailureTransportStamp and re-dispatches.
  • Scaffolder extension: queue: block in a spec emits app/Messages/<Name>.php (readonly DTO), app/Messages/<Name>Handler.php (#[AsHandler]-decorated stub), and tests/Messages/<Name>HandlerTest.php (golden test) in the same bin/altair spec:scaffold pass that already produces the HTTP artifacts.

The wrap exists for vendor neutrality, a consistent DI story (handlers resolved through Altair\Container), and a single source of truth for the wire format + the async contract via the spec.

Architecture notes

  • The bus is a Symfony MessageBus with two middlewares — SendMessageMiddleware (routing-aware) then ContainerHandlerMiddleware (a tagged subtype of HandleMessageMiddleware with our HandlerLocator).
  • HandlerLocator resolves handler instances through Altair\Container and filters by fromTransport / priority before yielding HandlerDescriptors.
  • The bus → transports → SyncTransportFactory → bus construction cycle is broken via LazyBus, which resolves the bus at first dispatch() time. This lets sync routing coexist with async transports without manual ordering.

Host-application boot is required

The framework's bin/altair only wires CLI discovery (CliConfiguration); it does not apply MessengerConfiguration. Invoking bin/altair worker from a fresh framework checkout fails with TransportSettings is not instantiable — the same caveat that applies to bin/altair db:migrate (which needs CycleOrmConfiguration applied first). The host application is expected to ship its own entry script that applies the configurations it uses, then hands off to Altair\Cli\Application::run(). See docs/packages/messaging.md#running-workers for the boot snippet.

Tests

  • 30 messaging tests under tests/Messaging/: attribute reading, registry priority/transport filtering, discoverer scan, handler locator, message bus wrap, transport settings env parsing, configuration container wiring, sync dispatch, async InMemory → worker → handle integration, and the optional LoggingMiddleware.
  • 6 scaffold tests for the queue: block: parser, validator (positive + negative cases), and the three emitters.
  • composer cs clean. composer stan no errors. vendor/bin/phpunit tests/Messaging tests/Scaffold tests/Cli: 133 / 0 / 21 (tests / failures / pre-existing deprecations in Altair\Container). Full suite has 6 pre-existing environmental failures (MongoDB / PDO session tests that need infrastructure) — none introduced by this change.

Test plan (verified locally)

  • composer install after pulling the branch — clean
  • composer cs clean, composer stan no errors
  • vendor/bin/phpunit tests/Messaging tests/Scaffold tests/Cli — 133 tests pass
  • bin/altair list shows worker, worker:retry-failed, worker:show-failed; bin/altair worker --help prints expected usage
  • Add a spec with a queue: block, run bin/altair spec:scaffold path/to/foo.yaml --dry-runapp/Messages/SendWelcomeEmail.php, app/Messages/SendWelcomeEmailHandler.php, and tests/Messages/SendWelcomeEmailHandlerTest.php are listed alongside the HTTP artifacts
  • bin/altair manifest:generate --check exits 0
  • (requires a host application) Boot MessengerConfiguration in an app entry script, then MESSENGER_TRANSPORT_DEFAULT=in-memory:// bin/altair worker --limit=1 consumes from the in-memory transport. Covered by tests/Messaging/Integration/DispatchAndConsumeTest.php.

Caveat for reviewers

composer qa reports 6 pre-existing test errors (5 MongoSessionHandler, 1 PdoSessionHandler) and 67 deprecation notices from Altair\Container constructors that pre-date this branch. CS and PHPStan are clean.

Antonio Ramirez added 4 commits May 27, 2026 06:31
Wraps Symfony Messenger behind framework-owned MessageBusInterface,
discovers handlers via #[AsHandler], wires transports from env, and
ships worker/retry/show CLI commands. Scaffolder gains a queue: block.
Builds on the WIP messaging scaffold:

- bin/altair includes src/Altair/Messaging/Cli so worker / worker:retry-failed /
  worker:show-failed commands are picked up out of the box.
- Scaffold gains a queue: block: Parser/Validator parse and check it, three new
  emitters (MessageEmitter, HandlerEmitter, HandlerTestEmitter) produce a
  readonly DTO + #[AsHandler] handler stub + golden PHPUnit test, and
  EmissionPlan wires them.
- MessengerConfiguration breaks the bus/transport construction cycle via a
  LazyBus so SyncTransportFactory can be registered safely.
- Adds the messaging package manifest under .agent/packages/messaging.md and
  the human-facing guide at docs/packages/messaging.md; updates docs/README,
  AGENT.md (sub-package layout) and CLAUDE.md (messaging section) accordingly.
- Tests: 30 messaging tests (attribute, registry, discoverer, handler locator,
  message bus, transport settings, middleware, configuration wiring, sync +
  async InMemory integration) plus three scaffold queue emitter tests and
  parser/validator coverage.

composer qa green: cs clean, phpstan no errors, 86/86 tests pass in the
scaffold + messaging suites.
Caught while running the PR test plan: `bin/altair worker` invoked straight
from the framework checkout exits with
"TransportSettings is not instantiable" because the framework's bin/altair
only wires CLI discovery (CliConfiguration), not domain configurations. The
same is true of `bin/altair db:migrate`. Documents the constraint and shows a
host entry-point that applies MessengerConfiguration before running the
Application.
@tonydspaniard tonydspaniard merged commit bf10b5e into master May 27, 2026
3 checks passed
@tonydspaniard tonydspaniard deleted the feat/21-messaging branch May 27, 2026 04:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

univeros/messaging — Symfony Messenger bridge with scaffold + worker

1 participant