Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Domain Events - czyli jak radzić sobie z rzeczywistością!

Domain Events - czyli jak radzić sobie z rzeczywistością!

Matt Stasch

April 11, 2016
Tweet

More Decks by Matt Stasch

Other Decks in Programming

Transcript

  1. ?

  2. NOW

  3. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { // Validation... var

    caseToBePaid = _caseDao.GetById(id); // It was not so simple... ;) caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); _mmeGateway.RegisterPayment(/*...*/); if (paymentDetails.CardType == CardType.Credit) { // Message generation... _mailGateway.Send(/*...*/); } /* Send to PDA... */ }
  4. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { // Validation... var

    caseToBePaid = _caseDao.GetById(id); // It was not so simple... ;) caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); _mmeGateway.RegisterPayment(/*...*/); if (paymentDetails.CardType == CardType.Credit) { // Message generation... _mailGateway.Send(/*...*/); } /* Send to PDA... */ }
  5. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { // Validation... var

    caseToBePaid = _caseDao.GetById(id); // It was not so simple... ;) caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); _eventBus.Raise(new CardPaymentRegisteredEvent { Id = id, Details = paymentDetails }); }
  6. public class MmePaymentsHandler : IEventHandler<CardPaymentRegisteredEvent> { private MMEGateway _mmeGateway; /*

    ctor(MMEGateway Gateway)... */ public void Handle(CardPaymentRegisteredEvent @event) { if (@event.Details.CardType == CardType.Credit) { _mmeGateway.Send(/*...*/); } } }
  7. public class MmePaymentsHandler : IEventHandler<CardPaymentRegisteredEvent> { private MMEGateway _mmeGateway; /*

    ctor(MMEGateway Gateway)... */ public void Handle(CardPaymentRegisteredEvent @event) { if (@event.Details.CardType == CardType.Credit) { _mmeGateway.Send(/*...*/); } } }
  8. public class MmePaymentsHandler : IEventHandler<CardPaymentRegisteredEvent> { private MMEGateway _mmeGateway; /*

    ctor(MMEGateway Gateway)... */ public void Handle(CardPaymentRegisteredEvent @event) { if (@event.Details.CardType == CardType.Credit) { _mmeGateway.Send(/*...*/); } } }
  9. Nie wiem ilu Was jest, kiedy i w jakiej kolejności

    wykonacie pracę oraz jaki będzie jej wynik
  10. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { var caseToBePaid =

    _caseDao.GetById(id); caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); if (caseToBePaid.OutstandingBalance.IsZero) { caseToBePaid.MarkAsPaid(); } /* MME */ /* Mail */ /* Send to PDA... */ }
  11. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { var caseToBePaid =

    _caseDao.GetById(id); caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); if (caseToBePaid.OutstandingBalance.IsZero) { caseToBePaid.MarkAsPaid(); } /* MME */ /* Mail */ /* Send to PDA... */ }
  12. Nie wiem ilu Was jest, kiedy i w jakiej kolejności

    wykonacie pracę oraz jaki będzie jej wynik #3
  13. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { var caseToBePaid =

    _caseDao.GetById(id); // It was not so simple... ;) caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); if (caseToBePaid.OutstandingBalance.IsZero) { _eventBus.Raise(new CasePaidEvent { Id = id }); } _eventBus.Raise(new CardPaymentRegisteredEvent { Id = id, Details = paymentDetails }); } public void RegisterCashPayment(EntityId id /*...*/)
  14. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { var caseToBePaid =

    _caseDao.GetById(id); // It was not so simple... ;) caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); if (caseToBePaid.OutstandingBalance.IsZero) { _eventBus.Raise(new CasePaidEvent { Id = id }); } _eventBus.Raise(new CardPaymentRegisteredEvent { Id = id, Details = paymentDetails }); } public void RegisterCashPayment(EntityId id /*...*/)
  15. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { var caseToBePaid =

    _caseDao.GetById(id); // It was not so simple... ;) caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); if (caseToBePaid.OutstandingBalance.IsZero) { _eventBus.Raise(new CasePaidEvent { Id = id }); } _eventBus.Raise(new CardPaymentRegisteredEvent { Id = id, Details = paymentDetails }); } public void RegisterCashPayment(EntityId id /*...*/)
  16. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { var caseToBePaid =

    _caseDao.GetById(id); // It was not so simple... ;) caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); if (caseToBePaid.OutstandingBalance.IsZero) { _eventBus.Raise(new CasePaidEvent { Id = id }); } _eventBus.Raise(new CardPaymentRegisteredEvent { Id = id, Details = paymentDetails }); } public void RegisterCashPayment(EntityId id /*...*/) Utrata kontroli
  17. public void RegisterCardPayment(EntityId id, CardPayment paymentDetails) { var caseToBePaid =

    _caseDao.GetById(id); // It was not so simple... ;) caseToBePaid.Payments.Add(paymentDetails); caseToBePaid.RefreshOutstandingBalance(); _eventBus.Raise(new CardPaymentRegisteredEvent { Id = id, Details = paymentDetails }); } public void RegisterCashPayment(EntityId id /*...*/)
  18. public void AddGood(EntityId id, Good good) { var @case =

    _caseDao.GetById(id); @case.AddGood(good); _mmeGateway.Send(/*...*/ @case.Balance); /* Other stuff */ } #5
  19. public void AddGood(EntityId id, Good good) { var @case =

    _caseDao.GetById(id); @case.AddGood(good); _mmeGateway.Send(/*...*/ @case.Balance); /* Other stuff */ } #5
  20. #5 public class GoodAddedEvent : IEvent { public EntityId Id

    { get; set; } public DateTimeOffset RaisedAt { get; set; } public Good Good { get; set; } public Money Balance { get; set; } }
  21. #5 public class GoodAddedEvent : IEvent { public EntityId Id

    { get; set; } public DateTimeOffset RaisedAt { get; set; } public Good Good { get; set; } public Money Balance { get; set; } }
  22. public void AddGood(EntityId id, Good good) { var @case =

    _caseDao.GetById(id); @case.AddGood(good); /* Raise Event */ _mailGateway.Send(/*...*/ @case.OutstandingBalance); /* Other stuff */ } #5
  23. ReCap • Fakt, a nie życzenie! • Obiekt zdarzenia powinien

    być mały • Nie polegaj na skutkach ubocznych swojej implementacji • Jedno zdarzenie ma jedno źródło • Nie modeluj zdarzenia dla jednego handlera • Nie używajcie EF-a.
  24. Refactoring  Powiadomienia(Maile, SMSy)  Integracje z zew. systemami o

    ile nie są krytyczne*  Eager Data Derivation  Generowanie dokumentów/raportów  Unieważnianie :D Cache
  25. public interface IEvent { int Id { get; set; }

    Type EntityType { get; set; } } public interface IEventHandler<TEvent> where TEvent : IEvent { void Handle(TEvent @event); }
  26. private void ApplyEvents() { var processed = new List<IEvent>(); for

    (;;) { var events = ChangeTracker.Entries<IEventSource>() .SelectMany(x => x.Entity.Events) .Except(processed) .ToList(); if (!events.Any()) break; foreach (var @event in events) { _eventDispatcher.Apply(@event); processed.Add(@event); } } } public override int SaveChanges() { ApplyEvents(); base.SaveChanges(); }
  27. private void ApplyEvents() { var processed = new List<IEvent>(); for

    (;;) { var events = ChangeTracker.Entries<IEventSource>() .SelectMany(x => x.Entity.Events) .Except(processed) .ToList(); if (!events.Any()) break; foreach (var @event in events) { _eventDispatcher.Apply(@event); processed.Add(@event); } } } public override int SaveChanges() { ApplyEvents(); base.SaveChanges(); }
  28. private void ApplyEvents() { var processed = new List<IEvent>(); for

    (;;) { var events = ChangeTracker.Entries<IEventSource>() .SelectMany(x => x.Entity.Events) .Except(processed) .ToList(); if (!events.Any()) break; foreach (var @event in events) { _eventDispatcher.Apply(@event); processed.Add(@event); } } } public override int SaveChanges() { ApplyEvents(); base.SaveChanges(); }
  29. private void ApplyEvents() { var processed = new List<IEvent>(); for

    (;;) { var events = ChangeTracker.Entries<IEventSource>() .SelectMany(x => x.Entity.Events) .Except(processed) .ToList(); if (!events.Any()) break; foreach (var @event in events) { _eventDispatcher.Apply(@event); processed.Add(@event); } } } public override int SaveChanges() { ApplyEvents(); base.SaveChanges(); }
  30. private void ApplyEvents() { var processed = new List<IEvent>(); for

    (;;) { var events = ChangeTracker.Entries<IEventSource>() .SelectMany(x => x.Entity.Events) .Except(processed) .ToList(); if (!events.Any()) break; foreach (var @event in events) { _eventDispatcher.Apply(@event); processed.Add(@event); } } } public override int SaveChanges() { ApplyEvents(); base.SaveChanges(); }
  31. public class EventDispatcher : IEventDispatcher { /*...*/ public void Apply(IEvent

    @event) { // ... Call generic ApplyTyped } protected void ApplyTyped<TEvent>(TEvent @event) where TEvent : IEvent { var handlers = _scope.Resolve<IEnumerable<IEventHandler<TEvent>>>(); foreach (var eventHandler in handlers) { eventHandler.Handle(@event); } } }
  32. public class EventDispatcher : IEventDispatcher { /*...*/ public void Apply(IEvent

    @event) { // ... Call generic ApplyTyped } protected void ApplyTyped<TEvent>(TEvent @event) where TEvent : IEvent { var handlers = _scope.Resolve<IEnumerable<IEventHandler<TEvent>>>(); foreach (var eventHandler in handlers) { eventHandler.Handle(@event); } } }
  33. public void Apply(IEvent @event) { try { applyTypedMethod.MakeGenericMethod(@event.GetType()) .Invoke(this, new

    object[] { @event }); } catch (TargetInvocationException ex) { ex.InnerException.PreserveStackTrace(); throw ex.InnerException; } }
  34. public class AgreementSubmitted : IEvent { public DateTime EffectiveDate {

    get; set; } public string Note { get; set; } public int Id { get; set; } public Type EntityType { get; set; } public AgreementSubmitted(int id, DateTime effectiveDate, string note) { Id = id; EffectiveDate = effectiveDate; Note = note; } }
  35. public class EventPersister<TEvent> : IEventHandler<TEvent> where TEvent : IEvent {

    private readonly IEventStore _eventStore; public void Handle(TEvent @event) { _eventStore.Save(new EventEnvelope<TEvent>(@event)); } }
  36. SELECT * FROM EventLog WHERE EntityType = 'NS.Case' AND Id

    = 302049 ORDER BY RaisedAt Id EntityType RaisedAt RaisedBy EventType Content 302049 Domain.Case 2016-01-01 20:54:43.104 56 Events.Case.Created {…} 302049 Domain.Case 2016-01-01 20:55:43.105 56 Events.Case.CardPaymentRegistered {…} 302049 Domain.Case 2016-03-01 15:01:13.554 76 Events.Case.GoodsCollected {…} 302049 Domain.Case 2016-03-01 15:01:13.555 1 Events.Case.Paid {} 302050 Domain.Case 2016-03-01 15:01:13.556 1 Events.Case.Closed {}
  37. SELECT * FROM EventLog WHERE EntityType = 'NS.Case' AND Id

    = 302049 ORDER BY RaisedAt Id EntityType RaisedAt RaisedBy EventType Content 302049 Domain.Case 2016-01-01 20:54:43.104 56 Events.Case.Created {…} 302049 Domain.Case 2016-01-01 20:55:43.105 56 Events.Case.CardPaymentRegistered {…} 302049 Domain.Case 2016-03-01 15:01:13.554 76 Events.Case.GoodsCollected {…} 302049 Domain.Case 2016-03-01 15:01:13.555 1 Events.Case.Paid {} 302050 Domain.Case 2016-03-01 15:01:13.556 1 Events.Case.Closed {}