Broadcast train events across processes
Source Trax.Effect.Broadcaster.RabbitMQ/, Trax.Effect.Broadcaster.SignalR/
Workers and a UI host share a RabbitMQ exchange, and the UI host adds a SignalR sink so browsers see runs live, wherever they ran. What transports and sinks are is on Broadcaster sinks.
1. Publish from the workers
using Trax.Effect.Broadcaster.RabbitMQ.Extensions;
using Trax.Effect.Data.Postgres.Extensions;
using Trax.Effect.Extensions;
builder.Services.AddTrax(trax =>
trax.AddEffects(effects =>
effects
.UsePostgres(connectionString)
.UseBroadcaster(b => b.UseRabbitMq(rabbitMqConnectionString))));A worker has no UI and registers no sink beyond the broadcaster's own publish hook.
2. Receive and rebroadcast on the hub host
using Trax.Effect.Broadcaster.RabbitMQ.Extensions;
using Trax.Effect.Broadcaster.SignalR.Extensions;
using Trax.Effect.Data.Postgres.Extensions;
using Trax.Effect.Extensions;
builder.Services.AddSignalR();
builder.Services.AddTrax(trax =>
trax.AddEffects(effects =>
effects
.UsePostgres(connectionString)
.UseBroadcaster(b => b
.UseRabbitMq(rabbitMqConnectionString)
.UseSignalRHub(opts => opts.OnlyForEvents("Completed", "Failed")))));
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapTraxTrainEventHub(hub => hub.RequireAuthorization("TraxEvents"));The hub subscribes to the exchange and rebroadcasts each matching event to the browsers its posture admits (MapTraxTrainEventHub: Authorization). Trains the hub host runs itself reach the SignalR sink directly, without a transport hop. The TraxEvents policy is yours to define with AddAuthorization.
3. Check it
Run a train on a worker and watch a connected client receive its Completed or Failed event. The SignalR Broadcaster sample is a single-process version with a browser page; the Energy Hub sample is the multi-process case, with GraphQL subscriptions in place of SignalR.