Skip to main content

MessengerActorRef

Location-transparent ActorRef backed by a Symfony Messenger SenderInterface; tell() publishes to a broker transport while remaining byte-identical to a local actor send.

What it does

MessengerActorRef<T> implements ActorRef<T> so actor code that already knows how to tell() a local ref can publish to AMQP, Redis, or any other Messenger transport without changes. The ref is fully decoupled from Symfony Framework and symfony/console.

ask() supports broker request/reply when the ref is constructed with an AskSupport instance (the sixth constructor parameter, or the askSupport: named argument to MessengerBridge::producer()). Without AskSupport, ask() throws UnsupportedOperationException immediately.

Observability: when an Observability instance is provided (the fourth constructor parameter), each tell() is wrapped in a messenger.send Producer span and the nexus.messenger.messages.sent counter is incremented. Telemetry errors are swallowed so they never interrupt the send.

PSR-14 events: when an EventDispatcherInterface is provided (the fifth parameter), a MessagePublished event is dispatched after every successful send.

Trace context: when observability is enabled, tell() injects a TraceContextStamp onto the envelope before forwarding it to the sender. The consumer can extract this stamp to open a child span that continues the originating trace.

The idiomatic way to obtain an instance is MessengerBridge::producer(), which accepts the same parameters and returns a correctly typed ref.

Constructor

use Monadial\Nexus\Messenger\Producer\MessengerActorRef;
use Monadial\Nexus\Observability\Observability;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Messenger\Transport\Sender\SenderInterface;

new MessengerActorRef(
sender: SenderInterface $sender,
senderName: string $senderName,
sourcePath: ?ActorPath $sourcePath = null,
observability: Observability $observability = new NoopObservability(),
events: ?EventDispatcherInterface $events = null,
askSupport: ?AskSupport $askSupport = null,
);
ParameterTypeDefaultDescription
$senderSenderInterfaceMessenger transport or bus sender to publish to.
$senderNamestringLogical name; used in the synthetic actor path /messenger/<name> and in span/event attributes.
$sourcePath?ActorPathnullWhen set, attaches a SourceActorPathStamp to every outbound envelope for provenance tracking.
$observabilityObservabilityNoopObservabilityOTel instrumentation. Pass the Observability from the actor system to enable spans and counters.
$events?EventDispatcherInterfacenullPSR-14 dispatcher. When set, a MessagePublished event is dispatched after each successful send.
$askSupport?AskSupportnullEnables ask(). Build via MessengerBridge::askSupport() and pass here. When null, ask() throws UnsupportedOperationException.

Methods

MethodReturnsDescription
tell(object $message): voidvoidPublish a message to the transport. When observability is enabled, emits a messenger.send Producer span and increments nexus.messenger.messages.sent. Dispatches MessagePublished if an event dispatcher is configured.
ask(object $message, Duration $timeout): FutureFuture<R>When AskSupport is configured: publishes the envelope with CorrelationIdStamp + ReplyToStamp, registers a future slot, and returns a Future that resolves when the reply arrives. Throws UnsupportedOperationException if AskSupport is not configured, or AskCapacityExceededException if the pending registry is at capacity. Must be called inside a fiber — use ->await() to block until the reply or timeout.
path(): ActorPathActorPathReturns the synthetic path /messenger/<senderName>. Not resolvable from the actor system.
isAlive(): boolboolAlways returns true — liveness is delegated to the transport layer.

Example

src/bootstrap.php
use Monadial\Nexus\Messenger\MessengerBridge;

// Preferred: use the bridge factory
$orders = MessengerBridge::producer($transport, 'orders-out');
$orders->tell(new OrderPlaced('A-42'));

// With observability and PSR-14 events
$orders = MessengerBridge::producer(
sender: $transport,
name: 'orders-out',
sourcePath: null,
observability: $observability,
events: $eventDispatcher,
);
$orders->tell(new OrderPlaced('A-42'));
// → emits messenger.send span
// → increments nexus.messenger.messages.sent{nexus.message.type="OrderPlaced"}
// → dispatches MessagePublished($message, 'orders-out')

// With ask/reply
$askSupport = MessengerBridge::askSupport($system, $channelFactory);
$orders = MessengerBridge::producer($transport, 'orders-out', askSupport: $askSupport);
// Inside a fiber:
/** @var Pong $reply */
$reply = $orders->ask(new Ping('hello'), Duration::seconds(5))->await();
// → emits messenger.ask span
// → increments nexus.messenger.asks.sent
// → dispatches AskStarted($message, $correlationId)

Messages sent via tell() must carry #[MessageType] (enforced by the nexus-psalm plugin when sending to a MessengerActorRef).

Full API reference

Full class and method signatures

See also