namespace MS.Microservice.Messaging;
/// 跨边界传播的业务事实;Id 和 UTC 发生时间在首次产生时确定,重试和重放保持不变。
public interface IIntegrationEvent
{
Guid Id { get; }
DateTimeOffset OccurredAtUtc { get; }
}
/// 向当前工作单元暂存事件,不直接承诺网络发送成功。
public interface IIntegrationEventPublisher
{
/// Stages an immutable message in the active unit of work, without sending it to a broker.
ValueTask EnqueueAsync(IIntegrationEvent message, CancellationToken cancellationToken = default);
}
/// 业务变更与出站消息的原子提交入口,由最外层调用拥有事务。
/// Handler 不应自行提交,否则消费完成标记和后续消息可能落在不同事务中。
public interface IUnitOfWork
{
/// Executes and commits one business operation. Nested calls participate in the outer transaction.
Task ExecuteAsync(Func> operation, CancellationToken cancellationToken = default);
}
public static class UnitOfWorkExtensions
{
public static async Task ExecuteAsync(this IUnitOfWork unitOfWork,
Func operation, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(operation);
await unitOfWork.ExecuteAsync(async token => { await operation(token); return true; }, cancellationToken);
}
}
public sealed record MessageContext(Guid MessageId, string Consumer, string? CorrelationId = null,
string? TraceParent = null, string? TraceState = null);
/// 业务消费契约;适配器提供上下文与事务,业务不依赖原生信封或消息表。
public interface IIntegrationEventHandler where T : IIntegrationEvent
{
Task HandleAsync(T message, MessageContext context, CancellationToken cancellationToken);
}
public enum ReplayResult { Accepted, NotFound, InvalidState }
public sealed record FailedMessage(string FailureId, Guid MessageId, string ContractName, int ContractVersion,
string? Consumer, DateTimeOffset? FailedAtUtc, string ErrorCode);
/// 失败消息的最小共同运维能力;标识由 Provider 管理,不能按某一实现的表结构解析。
public interface IFailedMessageOperations
{
Task> ListAsync(int limit = 100, CancellationToken cancellationToken = default);
Task ReplayAsync(string failureId, CancellationToken cancellationToken = default);
}
public enum MessagingProvider { SelfManaged, Wolverine }
public sealed class MessageContractException(string message, Exception? innerException = null)
: Exception(message, innerException);
/// A business handler can reject a message permanently; infrastructure failures remain retryable.
public sealed class PermanentMessageException(string errorCode) : Exception(errorCode);