V13-FE-011: finalize search list layout slice

This commit is contained in:
2026-08-09 02:57:26 +09:00
parent 9efd202e76
commit 6422cb2b13
984 changed files with 120811 additions and 1498 deletions
@@ -0,0 +1,46 @@
using Kbx.Shared.Integrations.Generated;
namespace Kbx.Shared.Integrations;
public sealed class KbxOutboxIntegrationDispatcher(
IKbxIntegrationAdapterRegistry adapters,
KbxIntegrationAttemptRepository attempts,
KbxPollyIntegrationPipeline pipelineFactory,
KbxIntegrationOperationsProjector operations)
{
public async Task<KbxIntegrationAttemptResult> 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;
}
}