2026-05-12 19:34:47 +08:00

46 lines
1.4 KiB
Go

package integration
import (
"context"
"errors"
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
)
// CompositeOutboxPublisher fans one committed room outbox event to every
// asynchronous consumer. Consumers must be idempotent by event_id because a
// partial failure retries the full fanout on the next worker pass.
type CompositeOutboxPublisher struct {
publishers []OutboxPublisher
}
// NewCompositeOutboxPublisher removes nil publishers and returns a no-op when
// no asynchronous consumers are configured.
func NewCompositeOutboxPublisher(publishers ...OutboxPublisher) OutboxPublisher {
filtered := make([]OutboxPublisher, 0, len(publishers))
for _, publisher := range publishers {
if publisher != nil {
filtered = append(filtered, publisher)
}
}
if len(filtered) == 0 {
return NewNoopOutboxPublisher()
}
return &CompositeOutboxPublisher{publishers: filtered}
}
// PublishOutboxEvent calls every configured consumer before returning. Returning
// a joined error keeps the event retryable until every consumer has accepted it.
func (p *CompositeOutboxPublisher) PublishOutboxEvent(ctx context.Context, envelope *roomeventsv1.EventEnvelope) error {
if p == nil || len(p.publishers) == 0 {
return nil
}
var joined error
for _, publisher := range p.publishers {
if err := publisher.PublishOutboxEvent(ctx, envelope); err != nil {
joined = errors.Join(joined, err)
}
}
return joined
}