using Kbx.Shared.Integrations.Generated; namespace Kbx.Shared.Integrations; public sealed class KbxOutboxIntegrationDispatcher( IKbxIntegrationAdapterRegistry adapters, KbxIntegrationAttemptRepository attempts, KbxPollyIntegrationPipeline pipelineFactory, KbxIntegrationOperationsProjector operations) { public async Task DispatchAsync(KbxIntegrationMessage message, int durableAttempt, CancellationToken ct) { if (!KbxIntegrationCatalog.All.TryGetValue(message.IntegrationId, out var definition)) return new(false, KbxIntegrationFailureKind.Permanent, "INTEGRATION_NOT_REGISTERED", message.IntegrationId); var attemptId = await attempts.StartAsync(message, durableAttempt, ct); var pipeline = pipelineFactory.Build( definition.ShortRetryAttempts, TimeSpan.FromMilliseconds(definition.ShortRetryBaseDelayMs), TimeSpan.FromMilliseconds(definition.TimeoutMs), definition.CircuitFailureRatio, TimeSpan.FromSeconds(definition.CircuitSamplingSeconds), definition.CircuitMinimumThroughput, TimeSpan.FromSeconds(definition.CircuitBreakSeconds)); KbxIntegrationAttemptResult result; try { var adapter = adapters.GetRequired(message.IntegrationId); result = await pipeline.ExecuteAsync(async token => await adapter.SendAsync(message, token), ct); } catch (Exception error) { result = new(false, KbxIntegrationFailureClassifier.Classify(error), error.GetType().Name, "외부 연계 호출이 완료되지 않았습니다."); } // A transient result is persisted and delegated to Hangfire for durable retry. var state = result.Succeeded ? "delivered" : result.FailureKind == KbxIntegrationFailureKind.Transient ? "retrying" : "failed"; var next = state == "retrying" ? KbxDurableIntegrationRetryPlanner.NextAttemptAt(definition, durableAttempt, DateTimeOffset.UtcNow) : null; await attempts.CompleteAsync(attemptId, result, state, next, ct); if (result.Succeeded) await operations.OnDeliveredAsync(message, definition, ct); else if (result.FailureKind == KbxIntegrationFailureKind.Permanent && definition.TerminalAction == "operations-exception") await operations.OnPermanentFailureAsync(message, definition, result, ct); return result; } }