Full Invoker Chain

A production-ready invoker chain with tracing, metrics, rate limiting, idempotency, retry, circuit breaker, and DLQ.

Source: examples/02_invoker_chain.go

Event with All Interfaces

type PaymentProcessed struct {
    PaymentID string  `json:"payment_id"`
    UserID    string  `json:"user_id"`
    Amount    float64 `json:"amount"`
    Currency  string  `json:"currency"`
}

func (e PaymentProcessed) Name() string           { return "payment.processed" }
func (e PaymentProcessed) Version() int            { return 1 }
func (e PaymentProcessed) IdempotencyKey() string  { return "payment:" + e.PaymentID }
func (e PaymentProcessed) RateLimitKey() string    { return "user:" + e.UserID }

Building the Chain

chain := invoker.NewChain(
    // 1. Tracing - creates spans for each handler
    telemetry.NewTracerInvoker(tracer),

    // 2. Metrics - records latency and success/failure
    invoker.NewMetrics(metrics),

    // 3. Rate Limiting - prevents abuse
    invoker.NewRateLimiter(rateLimitStore, invoker.RateLimitConfig{
        Rate:   10,
        Period: time.Minute,
        Burst:  15,
    }, metrics),

    // 4. Idempotency - prevents duplicate processing
    invoker.NewIdempotency(
        invoker.NewMemoryIdempotencyStore(),
        invoker.IdempotencyConfig{ProcessingTTL: 5 * time.Minute},
        metrics,
    ),

    // 5. Retry - handles transient failures
    invoker.NewRetry(invoker.RetryPolicy{
        MaxAttempts: 3,
        BaseDelay:   100 * time.Millisecond,
        MaxDelay:    5 * time.Second,
    }, metrics),

    // 6. Circuit Breaker - prevents cascade failures
    invoker.NewCircuitBreaker(invoker.CircuitBreakerConfig{
        FailureThreshold: 5,
        SuccessThreshold: 2,
        OpenTimeout:      30 * time.Second,
    }, metrics),

    // 7. DLQ - handles terminal failures
    invoker.NewDLQ(dlqPublisher, metrics),
)

Handler with Error Classification

bus.Subscribe(ctx, "payment.processed", func(ctx context.Context, evt event.Event) error {
    payment := evt.(PaymentProcessed)

    if payment.Amount > 10000 {
        // Terminal error → DLQ
        return invoker.PermanentError{
            Err: errors.New("amount exceeds limit, requires manual review"),
        }
    }

    if payment.Amount < 0 {
        // Retryable error → will be retried
        return invoker.RetryableError{
            Err: errors.New("negative amount, possible race condition"),
        }
    }

    return nil
}, event.WithHandlerName("update-balance"))

Back to top

Copyright © 2025 Isaque de Souza Barbosa. Distributed under the MIT License.