Eventi del dominio di fuoco dopo il completamento della transazione

5

Sto tentando di implementare un sistema di eventi di dominio che attiva i suoi eventi solo quando l'unità di lavoro associata viene accettata correttamente.

Il motivo principale per cui voglio farlo è perché ho altri sottosistemi che leggono il database che si aspetta che tali modifiche siano in atto quando gestiscono l'evento.

Inoltre, non voglio che i gestori di eventi facciano il loro lavoro (invia email, ecc.) se il commit fallisce.

Il sistema è costituito da più applicazioni ASP.NET (WebForms, MVC e WebAPI).

public class Order
{
    private Payment _payment;

    public int ID { get; private set; }

    public decimal Amount { get; private set; }

    public void PayUsing(IPaymentProcessor processor)
    {
        if (_payment != null)
        {
            throw new InvalidOperationException(
                "You can't pay for an order twice");
        }

        // the Process method may also raise domain events
        _payment = processor.Process(Amount);

        // Raise saves the event into a static Queue<T>
        DomainEvents.Raise(new OrderPaidEvent(this));
    }
}

public class OrderFulfillmentService
{
    private readonly IUnitOfWorkFactory _unitOfWorkFactory;
    private readonly IPaymentProcessor _paymentProcessor;

    public OrderFulfillmentService(
        IUnitOfWorkFactory unitOfWorkFactory, 
        IPaymentProcessor paymentProcessor)
    {
        _unitOfWorkFactory = unitOfWorkFactory;
        _paymentProcessor = paymentProcessor;
    }

    public void Fulfill(int orderId)
    {
        using (var unitOfWork = _unitOfWorkFactory.Create())
        {
            var order = unitOfWork.OrdersRepository.GetById(orderId);

            order.PayUsing(_paymentProcessor);

            unitOfWork.Commit();

            // I only want the events to be raised if the Commit succeeds               
        }
    }
}

Con l'implementazione corrente, è problematico perché in questo momento il dominio che si innalza deve metterlo in una coda in quanto non deve sparare immediatamente. La coda è statica e causa problemi ai sistemi multiutente. Non desidero accidentalmente generare eventi per altri utenti, il che rappresenta un problema preciso dato che tutti gli utenti accedono alla stessa coda.

Questa è la lista di soluzioni che ho esaminato e perché non funzioneranno nel mio scenario

  1. Avere una classe nonDomainRaiser non modificata che viene passata nel metodo PayUsing .

    • Il passaggio a DomainRaiser è molto difficile da eseguire dagli oggetti dominio, a volte gli eventi possono essere generati modificando le proprietà e il passaggio di questo oggetto è complicato.
  2. Tutte le entità contengono un'enumerazione Events letta dalla chiamata UnitOfWork.Commit come discusso su link

    • Tracciare tutte le singole entità eventualmente modificate è molto difficile, sto scaricando questo rilevamento delle modifiche sull'ORM. Anche questo raggruppa gli oggetti del dominio.
  3. Uso di HttpContext.Current.Items per memorizzare eventi specifici dell'utente

    • Questo è attualmente il miglior suggerimento, tuttavia, non è possibile effettuare un test unitario e blocca il mio dominio all'utilizzo di asp.net, che in futuro ho in programma di rilasciare un'applicazione per desktop.

La mia domanda è: come faccio a fare la coda e a inviare questi eventi in un ambiente multi-utente in modo affidabile, tenendo conto del fatto che voglio solo lanciare eventi se l'unità di lavoro complessiva ha successo?

    
posta Matthew 03.09.2014 - 22:54
fonte

1 risposta

2

Innanzitutto, assicurerei che ogni entità abbia un riferimento a DomainRaiser . Sarebbe meglio se questo fosse impostato quando l'entità viene creata o materializzata nel repository. Ogni contesto utente / richiesta avrebbe la sua istanza che sarebbe poi iniettata in tutte le entità, che sono lavorate dal contesto. Non so quale implementazione di Repository / UnitOfWork stai usando, quindi potrebbe essere impossibile. Ma penso che potrebbe essere automatizzato se puoi dare alle tue entità un'interfaccia che può essere chiamata durante la costruzione.

Questo risolve anche il tuo secondo problema di eseguire solo gli eventi se UnitOfWork ha esito positivo. Se DomainRaiser e UnitOfWork hanno una singola istanza in un contesto, possono interagire tra loro, il che renderebbe semplice questa chiamata al metodo.

L'ultima cosa che mi viene in mente è che puoi separare alcuni eventi in due parti: prepare e execute . La preparazione verrà chiamata nello stesso UnitOfWork di quando è stato generato l'evento. Ciò consentirebbe di leggere i dati rilevanti per l'evento, senza attendere il commit. L'esecuzione verrebbe quindi chiamata all'esterno con un flag se UnitOfWork è stato eseguito correttamente e utilizzerà i dati preparati.

    
risposta data 04.09.2014 - 08:47
fonte