Implementação da EventSourced Saga

Eu escrevi um Agregado de Origem de Eventos e agora implementei uma Saga de Origem de Eventos ... Percebi que os dois são semelhantes e criei um objeto de origem de eventos como uma classe base da qual ambos derivam.

Eu vi uma demonstração aquihttp://blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/ mas acha que pode haver um problema, pois os comandos podem ser perdidos no caso de uma falha no processo, pois o envio de comandos está fora da transação de gravação?

public void Save(ISaga saga)
{
    var events = saga.GetUncommittedEvents();
    eventStore.Write(new UncommittedEventStream
    {
        Id = saga.Id,
        Type = saga.GetType(),
        Events = events,
        ExpectedVersion = saga.Version - events.Count
    });

    foreach (var message in saga.GetUndispatchedMessages())
        bus.Send(message); // can be done in different ways

    saga.ClearUncommittedEvents();
    saga.ClearUndispatchedMessages();
}

Em vez disso, estou usando o EventStore de Greg Young e, quando salvo um EventSourcedObject (um agregado ou uma saga), a sequência é a seguinte:

O repositório obtém uma lista dos novos MutatingEvents.Grava-os para transmitir.O EventStore dispara novos eventos quando os fluxos são gravados e confirmados no fluxo.Ouvimos os eventos da EventStore e os tratamos no EventHandlers.

Estou implementando os dois aspectos de uma saga:

Para absorvereventos, Que podeEstado de transição, que por sua vez podeemitir comandos.Para ter umalarme onde em algum momento no futuro (através de um serviço de timer externo) poderemos ser chamados de volta).

Questões

Como eu entendomanipuladores de eventos não devem emitir comandos (o que acontece se o comando falhar?) - mas estou bem com o exposto acima, já que a Saga é a coisa real que controla a criação de comandos (em reação a eventos) por meio dissoevento proxy e qualquer falha no envio de comandos pode ser tratada externamente (no EventHandler externo que lida comCommandEmittedFromSaga e reenvia se o comando falhar)?

Ou eu esqueço de agrupar eventos e armazenar arquivos nativosCommands eEvents no mesmo fluxo (misturado com uma mensagem de classe base - a Saga consumiria comandos e eventos, um agregado consumiria apenas eventos)?

Algum outro material de referência na rede para implementação de Sagas originadas por eventos? Qualquer coisa contra a qual eu possa sanear minhas idéias?

Algum código de plano de fundo está abaixo.

Saga emite um comando para Executar (envolvido em um evento CommandEmittedFromSaga)

O comando abaixo é agrupado dentro do evento:

public class CommandEmittedFromSaga : Event
{
    public readonly Command Command;
    public readonly Identity SagaIdentity;
    public readonly Type SagaType;

    public CommandEmittedFromSaga(Identity sagaIdentity, Type sagaType, Command command)
    {
        Command = command;
        SagaType = sagaType;
        SagaIdentity = sagaIdentity;
    }
}

Saga solicita um retorno de chamada em algum momento no futuro (evento AlarmRequestedBySaga)

A solicitação de retorno de alarme é encerrada em um evento e será disparada de volta para a Saga após o horário solicitado:

public class AlarmRequestedBySaga : Event
{
    public readonly Event Event;
    public readonly DateTime FireOn;
    public readonly Identity Identity;
    public readonly Type SagaType;

    public AlarmRequestedBySaga(Identity identity, Type sagaType, Event @event, DateTime fireOn)
    {
        Identity = identity;
        SagaType = sagaType;
        Event = @event;
        FireOn = fireOn;
    }
}

Como alternativa, posso armazenar comandos e eventos no mesmo fluxo do tipo base Message

public abstract class EventSourcedSaga
{
    protected EventSourcedSaga() { }

    protected EventSourcedSaga(Identity id, IEnumerable<Message> messages)
    {
        Identity = id;

        if (messages == null) throw new ArgumentNullException(nameof(messages));

        var count = 0;

        foreach (var message in messages)
        {
            var ev = message as Event;
            var command = message as Command;

            if(ev != null) Transition(ev);
            else if(command != null) _messages.Add(command);
            else throw new Exception($"Unsupported message type {message.GetType()}");

            count++;
        }

        if (count == 0)
            throw new ArgumentException("No messages provided");

        // All we need to know is the original number of events this
        // entity has had applied at time of construction.
        _unmutatedVersion = count;
        _constructing = false;
    }

    readonly IEventDispatchStrategy _dispatcher = new EventDispatchByReflectionStrategy("When");
    readonly List<Message> _messages = new List<Message>();
    readonly int _unmutatedVersion;
    private readonly bool _constructing = true;
    public readonly Identity Identity;

    public IList<Message> GetMessages()
    {
        return _messages.ToArray();
    }

    public void Transition(Event e)
    {
        _messages.Add(e);
        _dispatcher.Dispatch(this, e);
    }

    protected void SendCommand(Command c)
    {
        // Don't add a command whilst we are in the constructor. Message
        // state transition during construction must not generate new
        // commands, as those command will already be in the message list.
        if (_constructing) return;

        _messages.Add(c);
    }

    public int UnmutatedVersion() => _unmutatedVersion;
}

questionAnswers(1)

yourAnswerToTheQuestion