Event Sourcing
Stop fussing with infrastructure.
Start solving business problems.
Event Table
Entity Table
Event Table
Entity Table
Event Table
Entity Table
class EventStore
{
  void Add(intentityId, intexpectedVersion, IEnumerable<DomainEvent> events)
  List<DomainEven...
class MarkDefectAsFixedCommand
{
intDefectId;
}
class MarkDefectAsFixedCommandHandler : ICommandHandler<MarkDefectAsFix...
class Defect : Entity
{
boolisFixed;
 void MarkAsFixed()
  {
    if(isFixed)
      throw new InvalidOperationException();
...
abstract class Entity
{
intEntityId;
intCurrentVersion;
  List<DomainEvent> unsavedEvents;
  List<DomainEvent>...
class Repository
{
  void Save<TEntity>(TEntity entity)
  {
varunsavedEvents = entity.GetUnsavedEvents();
eventStore...
class EventStore
{
  void Add(intentityId, intexpectedVersion, IEnumerable<DomainEvent> events)
  {
    Begin
      ...
class MarkDefectAsFixedCommand
{
intDefectId;
}
class MarkDefectAsFixedCommandHandler : ICommandHandler<MarkDefectAsFix...
class Repository
{
  void Save<TEntity>(TEntity entity)
  {
    ...
  }
TEntityGetById<TEntity>(intentityId)
 ...
class EventStore
{
  void Add(intentityId, intexpectedVersion, IEnumerable<DomainEvent> events)
  {
...
  }
  List&l...
class Repository
{
  void Save<TEntity>(TEntity entity)
  {
    ...
  }
TEntityGetById<TEntity>(intentityId)
 ...
abstract class Entity
{
intEntityId;
intCurrentVersion;
  List<DomainEvent> unsavedEvents;
  List<DomainEvent>...
class Defect : Entity
{
boolisFixed;
intdefectId;
  void MarkAsFixed()
  {
    if(isFixed)
      throw new InvalidOperatio...
class Defect : Entity
{
boolisFixed;
intdefectId;
  void MarkAsFixed()
  {
    if(isFixed)
      throw new InvalidOperatio...
abstract class Entity
{
  List<DomainEvent> unsavedEvents;
  List<DomainEvent> GetUnsavedEvents()
  {
    retu...
Queries
DefectList Table
class DefectListQueryHandler : IQueryHandler<DefectListQueryRequest>
{
IEnumerable Query(DefectList...
class Repository
{
  void Save<TEntity>(TEntity entity)
  {
varunsavedEvents = entity.GetUnsavedEvents();
eventStore...
class DefectWasFixedDenormalizer : IDenormalizer<DefectWasFixedEvent>
{
void Denormalize(DefectWasFixedEvent event)
...
Snapshots
7
6
5
4
3
2
1
6
5
snap
4
3
2
1
class Defect : Entity, ISnapshotable
{
boolisFixed;
intfixedByUserId;
DefectSnapshotISnapshotable.TakeSnapshot()
  {
   re...
class Repository
{
  void Save<TEntity>(TEntity entity)
  {
    ...
  }
TEntityGetById<TEntity>(intentityId)
 ...
Event Versioning
class MarkDefectAsFixedCommand
{
intDefectId;
intFixedByUserId;
}
class MarkDefectAsFixedCommandHandler : ICommandHandler&...
class Defect : Entity
{
boolisFixed;
intfixedByUserId;
  void MarkAsFixed(intfixedByUserId)
  {
    if(isFixed)
      thro...
abstract class Entity
{
  ...
DomainEventConvertEventToCurrentVersion(DomainEventoldEvent)
 {
    // find and execute any ...
True Separation of Domain Model from Schema!
class Defect : Entity
{
DefectStatecurrentState;
  void MarkAsFixed(intfixedByUserId)
  {
     Raise(new DefectWasFixedEve...
Testing
[TestClass]
class DefectTests : EntityTest<Defect>
{
  [TestMethod]
  void MarkAsFixedRaisesEvent()
  {
var defect =...
Concurrency
class EventStore
{
  void Add(intentityId, intexpectedVersion, IEnumerable<DomainEvent> events)
  {
    Begin
      ...
Event Table
Event Table
Event Table
Event Table
Event Table
Event Table
Contraindications
More Information
Greg Young
Jonathan Oliver
UdiDahan
CQRS Info
NCQRS Framework
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Event sourcing
Upcoming SlideShare
Loading in …5
×

Event sourcing

2,003 views

Published on

event sourcing made easy, presented at community events http://goo.gl/VguQi

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,003
On SlideShare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
58
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

Event sourcing

  1. 1. Event Sourcing Stop fussing with infrastructure. Start solving business problems.
  2. 2. Event Table Entity Table
  3. 3. Event Table Entity Table
  4. 4. Event Table Entity Table
  5. 5. class EventStore { void Add(intentityId, intexpectedVersion, IEnumerable<DomainEvent> events) List<DomainEvent> GetAllEvents<TEntity>(intentityId) }
  6. 6. class MarkDefectAsFixedCommand { intDefectId; } class MarkDefectAsFixedCommandHandler : ICommandHandler<MarkDefectAsFixed> { void Handle(MarkDefectAsFixedCommand command) { var defect = // TODO get from the event store defect.MarkAsFixed(); repository.Save(defect); } }
  7. 7. class Defect : Entity { boolisFixed; void MarkAsFixed() { if(isFixed) throw new InvalidOperationException(); isFixed = true; Raise(new DefectWasFixedEvent(this.EntityId)); } } class DefectWasFixedEvent : DomainEvent { intDefectId; }
  8. 8. abstract class Entity { intEntityId; intCurrentVersion; List<DomainEvent> unsavedEvents; List<DomainEvent> GetUnsavedEvents() { return unsavedEvents; } void Raise(DomainEvent event) { unsavedEvents.Add(event); } void ReplayEvents(IEnumerable<DomainEvent> events) { foreach(var event in events) Handle(event) } }
  9. 9. class Repository { void Save<TEntity>(TEntity entity) { varunsavedEvents = entity.GetUnsavedEvents(); eventStore.Add(entity.EntityId, entity.CurrentVersion, unsavedEvents); } TEntityGetById<TEntity>(int id) { vareventsToReplay = eventStore.GetAllEvents<TEntity>(id) var entity = new TEntity(); entity.ReplayEvents(eventsToReplay); return entity; } }
  10. 10. class EventStore { void Add(intentityId, intexpectedVersion, IEnumerable<DomainEvent> events) { Begin version = SELECT CurrentVersion FROM Entity WHERE EntityId = @(entityId) if version is null INSERT INTO Entity CurrentVersion = 0 end if(version != @(expectedVersion) raise concurrency problem foreach event insert event with incremented version number update entity with last version number End Transaction } List<DomainEvent> GetAllEvents<TEntity>(intentityId) { SELECT * FROM Events WHERE EntityId = @(entityId) ORDER BY Version } }
  11. 11. class MarkDefectAsFixedCommand { intDefectId; } class MarkDefectAsFixedCommandHandler : ICommandHandler<MarkDefectAsFixed> { void Handle(MarkDefectAsFixedCommand command) { var defect = repository.GetById<Defect>(command.DefectId); defect.MarkAsFixed(); repository.Save(defect); } }
  12. 12. class Repository { void Save<TEntity>(TEntity entity) { ... } TEntityGetById<TEntity>(intentityId) { vareventsToReplay = eventStore.GetAllEvents<TEntity>(entityId); // TODO rebuild entity from those events } }
  13. 13. class EventStore { void Add(intentityId, intexpectedVersion, IEnumerable<DomainEvent> events) { ... } List<DomainEvent> GetAllEvents<TEntity>(intentityId) { SELECT * FROM Events WHERE EntityId = @(entityId) ORDER BY Version } }
  14. 14. class Repository { void Save<TEntity>(TEntity entity) { ... } TEntityGetById<TEntity>(intentityId) { vareventsToReplay = eventStore.GetAllEvents<TEntity>(entityId); var entity = new TEntity(); entity.ReplayEvents(eventsToReplay); return entity; } }
  15. 15. abstract class Entity { intEntityId; intCurrentVersion; List<DomainEvent> unsavedEvents; List<DomainEvent> GetUnsavedEvents() { return unsavedEvents; } void Raise(DomainEvent event) { unsavedEvents.Add(event); } void ReplayEvents(IEnumerable<DomainEvent> events) { foreach(var event in events) Handle(event) } }
  16. 16. class Defect : Entity { boolisFixed; intdefectId; void MarkAsFixed() { if(isFixed) throw new InvalidOperationException(); isFixed = true; Raise(new DefectWasFixedEvent(defectId)); } void OnDefectWasFixed(DefectWasFixedEvent event) { isFixed = true; } }
  17. 17. class Defect : Entity { boolisFixed; intdefectId; void MarkAsFixed() { if(isFixed) throw new InvalidOperationException(); Raise(new DefectWasFixedEvent(defectId)); } void OnDefectWasFixed(DefectWasFixedEvent event) { isFixed = true; } }
  18. 18. abstract class Entity { List<DomainEvent> unsavedEvents; List<DomainEvent> GetUnsavedEvents() { return unsavedEvents; } void Handle(DomainEvent event) { // event handler retrieval/invocation } void Raise(DomainEvent event) { Handle(event); unsavedEvents.Add(event); } void ReplayEvents(IEnumerable<DomainEvent> events) { foreach(var event in events) Handle(event) } }
  19. 19. Queries
  20. 20. DefectList Table class DefectListQueryHandler : IQueryHandler<DefectListQueryRequest> { IEnumerable Query(DefectListQueryRequest request) { return SELECT * FROM DefectList } }
  21. 21. class Repository { void Save<TEntity>(TEntity entity) { varunsavedEvents = entity.GetUnsavedEvents(); eventStore.Add(entity.EntityId, entity.CurrentVersion, unsavedEvents); // publish events “externally” } TEntityGetById<TEntity>(int id) { ... } }
  22. 22. class DefectWasFixedDenormalizer : IDenormalizer<DefectWasFixedEvent> { void Denormalize(DefectWasFixedEvent event) { UPDATE DefectList SET Status = ‘Fixed’ //The actual string, not a lookup! WHERE DefectId = @(event.DefectId) } }
  23. 23. Snapshots
  24. 24. 7 6 5 4 3 2 1
  25. 25. 6 5 snap 4 3 2 1
  26. 26. class Defect : Entity, ISnapshotable { boolisFixed; intfixedByUserId; DefectSnapshotISnapshotable.TakeSnapshot() { return new DefectSnapshot { IsFixed = isFixed, FixedByUserId = fixedByUserId }; } void ISnapshotable.RebuildFromSnapshot(DefectSnapshot snapshot) { isFixed = snapshot.IsFixed; fixedByUserId = snapshot.FixedByUserId; } ... }
  27. 27. class Repository { void Save<TEntity>(TEntity entity) { ... } TEntityGetById<TEntity>(intentityId) { var entity = new TEntity(); varcurrentSnapshot = eventStore.GetCurrentSnapshot<TEntity>(entityId); entity.RebuildFromSnapshot(currentSnapshot); vareventsAfterSnapshot = eventStore.GetEventsAfter<TEntity>( entityId, currentSnapshot.EntityVersion); entity.ReplayEvents(eventsToReplay); return entity; } }
  28. 28. Event Versioning
  29. 29. class MarkDefectAsFixedCommand { intDefectId; intFixedByUserId; } class MarkDefectAsFixedCommandHandler : ICommandHandler<MarkDefectAsFixed> { void Handle(MarkDefectAsFixedCommand command) { var defect = repository.GetById<Defect>(command.DefectId); defect.MarkAsFixed(command.FixedByUserId); repository.Save(defect); } }
  30. 30. class Defect : Entity { boolisFixed; intfixedByUserId; void MarkAsFixed(intfixedByUserId) { if(isFixed) throw new InvalidOperationException(); Raise(new DefectWasFixedEvent_v2(defectId, fixedByUserId)); } void OnDefectWasFixed(DefectWasFixedEvent_v2 event) { isFixed = true; fixedByUserId = event.FixedByUserId; } } class DefectWasFixedEvent_v2 : DomainEvent { intDefectId; intFixedByUserId; }
  31. 31. abstract class Entity { ... DomainEventConvertEventToCurrentVersion(DomainEventoldEvent) { // find and execute any converters that exist for this event type } void ReplayEvents(IEnumerable<DomainEvent> events) { foreach(var event in events) Handle(ConvertEventToCurrentVersion(event)) } } class MarkAsFixedDomainEventConverter_v1_to_v2 : IDomainEventConverter<MarkAsFixedDomainEvent, MarkAsFixedDomainEvent_v2> { MarkAsFixedDomainEvent_v2 Convert(MarkAsFixedDomainEventoldEvent) { return new DefectWasFixedEvent_v2(oldEvent.DefectId, UnknownUserId); } }
  32. 32. True Separation of Domain Model from Schema!
  33. 33. class Defect : Entity { DefectStatecurrentState; void MarkAsFixed(intfixedByUserId) { Raise(new DefectWasFixedEvent_v2(defectId, fixedByUserId)); } void OnDefectWasFixed(DefectWasFixedEvent_v2 event) { currentState.PromoteToFixed(fixedByUserId); } } class DefectState { void PromoteToFixed(intfixedByUserId) { ... } }
  34. 34. Testing
  35. 35. [TestClass] class DefectTests : EntityTest<Defect> { [TestMethod] void MarkAsFixedRaisesEvent() { var defect = new Defect(); defect.MarkAsFixed(); AssertEventIsRaised<DefectWasFixedEvent>(defect); } [TestMethod] [ExpectedException(typeof(InvalidOperationException)] void CannotMarkADefectFixedTwice() { var defect = new Defect(); defect.MarkAsFixed(); defect.MarkAsFixed(); } } abstract class EntityTest<TEntity> { void AssertEventIsRaised<TDomainEvent>(TEntity entity) { Assert.IsNotEmpty(entity.GetUnsavedEvents().OfType<TDomainEvent>()); } }
  36. 36. Concurrency
  37. 37. class EventStore { void Add(intentityId, intexpectedVersion, IEnumerable<DomainEvent> events) { Begin version = SELECT CurrentVersion FROM Entity WHERE EntityId = @(entityId) if version is null INSERT INTO Entity CurrentVersion = 0 end if(version != @(expectedVersion) raise concurrency problem foreach event insert event with incremented version number update entity with last version number End Transaction } List<DomainEvent> GetAllEvents<TEntity>(intentityId) { SELECT * FROM Events WHERE EntityId = @(entityId) ORDER BY Version } }
  38. 38. Event Table
  39. 39. Event Table
  40. 40. Event Table
  41. 41. Event Table
  42. 42. Event Table
  43. 43. Event Table
  44. 44. Contraindications
  45. 45. More Information Greg Young Jonathan Oliver UdiDahan CQRS Info NCQRS Framework

×