When building an Event Sourcing system, can we change the past? Can we also forecast “events” in the future?
If we want to provide various projections/visions of things that hapenned over the time, what are the consequences and trade-offs on our code?
All these questions will be answered during this talk. But moreover, I'll tell you our learning story about Bi-temporal Event Sourcing. Yet another illustration of the power of modeling, sketches and concrete examples when we need to understand each others.
8. REQUIREMENTS
—
Understand their former decisions
Fix their perception of the world
retroactively
(Re)produce reports in and from the past
Forecast ”events” in the future
9. REQUIREMENTS
—
Understand their former decisions
Fix their perception of the world
retroactively
(Re)produce reports in and from the past
Forecast ”events” in the future
Strong audit trail
17. Event [ɪˈvent]
Describes a fact, something that have happened.
Usually expressed in a past-tense form.
(e.g.: TalkStarted)
18. public class Fund : EventSourcedAggregate
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Fund(IEnumerable<Event> events)
{
foreach(var evt in events)
{
Apply(evt);
}
}
private Apply(FundRenamed event)
{
this.Name = event.FundNewName;
}
public IEnumerable<Event> Rename(string newName)
{
!// business logic and invariants checks HERE
!// Raise event(s)
return new[] { new FundRenamed(this.Id, newName) };
}
}
Event-Sourced business objects
19. public class Fund : EventSourcedAggregate
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Fund(IEnumerable<Event> events)
{
foreach(var evt in events)
{
Apply(evt);
}
}
private Apply(FundRenamed event)
{
this.Name = event.FundNewName;
}
public IEnumerable<Event> Rename(string newName)
{
!// business logic and invariants checks HERE
!// Raise event(s)
return new[] { new FundRenamed(this.Id, newName) };
}
}
Properties / Members
Event-Sourced business objects
20. public class Fund : EventSourcedAggregate
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Fund(IEnumerable<Event> events)
{
foreach(var evt in events)
{
Apply(evt);
}
}
private Apply(FundRenamed event)
{
this.Name = event.FundNewName;
}
public IEnumerable<Event> Rename(string newName)
{
!// business logic and invariants checks HERE
!// Raise event(s)
return new[] { new FundRenamed(this.Id, newName) };
}
}
Evolution functions
Change state
Event-Sourced business objects
21. public class Fund : EventSourcedAggregate
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Fund(IEnumerable<Event> events)
{
foreach(var evt in events)
{
Apply(evt);
}
}
private Apply(FundRenamed event)
{
this.Name = event.FundNewName;
}
public IEnumerable<Event> Rename(string newName)
{
!// business logic and invariants checks HERE
!// Raise event(s)
return new[] { new FundRenamed(this.Id, newName) };
}
}
Constructor
Change state
Event-Sourced business objects
22. public class Fund : EventSourcedAggregate
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Fund(IEnumerable<Event> events)
{
foreach(var evt in events)
{
Apply(evt);
}
}
private Apply(FundRenamed event)
{
this.Name = event.FundNewName;
}
public IEnumerable<Event> Rename(string newName)
{
!// business logic and invariants checks HERE
!// Raise event(s)
return new[] { new FundRenamed(this.Id, newName) };
}
}
Decision functions
Don’t change state
Event-Sourced business objects
23. public class Fund : EventSourcedAggregate
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Fund(IEnumerable<Event> events)
{
foreach(var evt in events)
{
Apply(evt);
}
}
private Apply(FundRenamed event)
{
this.Name = event.FundNewName;
}
public IEnumerable<Event> Rename(string newName)
{
!// business logic and invariants checks HERE
!// Raise event(s)
return new[] { new FundRenamed(this.Id, newName) };
}
}
Event-Sourced business objects
24. public class Fund : EventSourcedAggregate
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Fund(IEnumerable<Event> events)
{
foreach(var evt in events)
{
Apply(evt);
}
}
private Apply(FundRenamed event)
{
this.Name = event.FundNewName;
}
public IEnumerable<Event> Rename(string newName)
{
!// business logic and invariants checks HERE
!// Raise event(s)
return new[] { new FundRenamed(this.Id, newName) };
}
}
Decision functions
Don’t change state
Event-Sourced business objects
25. Event-Sourced business objects
public class Fund : EventSourcedAggregate
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public Fund(IEnumerable<Event> events)
{
foreach(var evt in events)
{
Apply(evt);
}
}
private Apply(FundRenamed event)
{
this.Name = event.FundNewName;
}
public IEnumerable<Event> Rename(string newName)
{
!// business logic and invariants checks HERE
!// Raise event(s)
return new[] { new FundRenamed(this.Id, newName) };
}
}
Decision functions
Don’t change state
Evolution functions
Change state
30. t
FundCreated
“super yield ”
Jan 1st
2016
Fund
id: b7f3ddfb -2cb5….
Name: super yield
Creation date: Jan 1st
2016
Management Company:
Shares:
Event-Sourcing in motion
32. t
Jan 1st
2016
Jan 30th
2016
FundRenamed
“Yield 2k22”
Apr 1st
2016
Fund
id: b7f3ddfb -2cb5….
Name: Yield 2k22
Creation date: Jan 1st
2016
Management Company:
BNP
Shares:
Event-Sourcing in motion
33. t
Jan 1st
2016
Jan 30th
2016
Apr 1st
2016
ShareAdded
“Part D”
Jun 2nd
2016
Fund
id: b7f3ddfb -2cb5….
Name: Yield 2k22
Creation date: Jan 1st
2016
Management Company:
BNP
Shares:
Part D
Event-Sourcing in motion
34. Fund
id: b7f3ddfb -2cb5….
Name: Yield 2k22
Creation date: Jan 1st
2016
Management Company:
BNP
Shares:
Part D
End of
Stream
>> <<t
Jan 1st
2016
Jan 30th
2016
Apr 1st
2016
Jun 2nd
2016
Event-Sourcing in motion
35. Projected at now
Ready to work
Fund
id: b7f3ddfb -2cb5….
Name: Yield 2k22
Creation date: Jan 1st
2016
Management Company:
BNP
Shares:
Part D
36. The beauty of it
Pick the projection date you want ;-)
37. Project same Fund at Apr 1st 2016
(same stream of events)
Event-Sourcing in motion
39. t
FundCreated
“super yield ”
Jan 1st
2016
Fund
id: b7f3ddfb -2cb5….
Name: super yield
Creation date: Jan 1st
2016
Management Company:
Shares:
Event-Sourcing in motion
45. Commands are used to take decisions and to raise events.
Command
(RenameFund)
Aggregate
(Fund X)
46. Commands are used to take decisions and to raise events.
Command
(RenameFund)
Events
(FundRenamed)
Events are used to restore/change the state of our aggregates.
Aggregate
(Fund X)
50. Challenge #1
- Events are immutable (facts)
- We save them in append-only streams (Event Store)
t
Q: How to retroactively change former mistaken events?
Q: How to insert forgotten events in the past (in the middle of
a stream)?
65. Challenge #2
- Event Store stores “Events” ;-)
- Events are things that have already happened (facts)
t
Q: How to store things that have to happen in an Event Store?
66.
67. Let’s store ”a Command (X) has just been Scheduled” events
77. Challenge #3
- With retroactive events, we have unsorted streams of events
- Some events may hide others
- Past may change at any time. Future too
t
Q: How to implement projections in such environment?
82. 1 Forward-only projections with filters
2 Sort - then - Apply events
3 Rebased Timelines with branches
too complicated
83. 1 Forward-only projections with filters
2 Sort - then - Apply events
3 Rebased Timelines with branches
too complicated
not compliant with bigstreams of events
84. 2 Sort - then - Apply events
3 Rebased Timelines with branches
too complicated
not compliant with bigstreams of events
1 Forward-only projections with filters
85.
86. Challenge #4
How to provide multiple visions of the
same reality?
(operational - audit-trail)
87. Challenge #4
- 1 stream of events per aggregate instance
- We need to provide both an operational mode and an audit mode
t
Q: How to provide various points of view in our reality?
107. As Of - as we should have known
Name:
???
Projection date
Jun 1st 2017
108. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
???
Projection date
Jun 1st 2017
109. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name” “official name”
“patched name”
Name:
???
Projection date
Jun 1st 2017
110. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name”
???
Name:
Projection date
Jun 1st 2017
111. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name”
👍
Apply
Name:
“First name”
-pending-
Projection date
Jun 1st 2017
112. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“official name”“First name”
Projection date
Jun 1st 2017
???
Name:
“First name”
-pending-
113. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
👍
Apply
Name:
“Official name”
-pending-
“official name”“First name”
Projection date
Jun 1st 2017
114. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“Official name”
-pending-
“official name”“First name”
“patched name”
Projection date
Jun 1st 2017
???
115. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
👎
Do NOT
Apply
Name:
“Official name”
-pending-
“official name”“First name”
“patched name”
Projection date
Jun 1st 2017
116. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name” “official name”
“patched name”
Name:
“official name”
Projection date
Jun 1st 2017
118. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
???
Projection date
Apr 1st 2016
119. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name” “official name”
“patched name”
Name:
???
Projection date
Apr 1st 2016
120. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name”
Name:
“First name”
-pending-
Projection date
Apr 1st 2016
???
121. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name”
👍
Apply
Name:
“First name”
-pending-
Projection date
Apr 1st 2016
122. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“First name”
-pending-
“official name”“First name”
Projection date
Apr 1st 2016
???
123. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“First name”
-pending-
“official name”“First name”
👎
Do NOT
Apply
Projection date
Apr 1st 2016
124. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“patched name”
-pending-
“official name”“First name”
“patched name”
Projection date
Apr 1st 2016
???
125. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“patched name”
-pending-
“official name”“First name”
“patched name”
👍
Apply
Projection date
Apr 1st 2016
126. As Of - as we should have known
t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name” “official name”
“patched name”
Name:
“patched name”
Projection date
Apr 1st 2016
128. As At - as we knew (at the time) "
audit-trail use cases
129. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
???
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
130. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name” “official name”
“patched name”
Name:
???
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
131. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name”
Name:
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
???
132. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name”
👍
Apply
Name:
“First name”
-pending-
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
133. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“First name”
-pending-
“official name”“First name”
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
???
134. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“First name”
-pending-
“official name”“First name”
👎
Do NOT
Apply
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
135. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“official name”“First name”
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
“patched name”
Name:
“First name”
-pending-
???
136. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“official name”“First name”
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
👎
Do NOT
Apply
“patched name”
Name:
“First name”
-pending-
137. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name” “official name”
“patched name”
Name:
“First name”
Projection date
Apr 1st 2016
As At - as we knew (at the time) "
139. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
???
Projection date
Jun 1st 2017
As At - as we knew (at the time) "
140. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name” “official name”
“patched name”
Name:
???
As At - as we knew (at the time) "
Projection date
Jun 1st 2017
141. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name”
Name:
As At - as we knew (at the time) "
???
Projection date
Jun 1st 2017
142. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name”
👍
Apply
Name:
“First name”
-pending-
As At - as we knew (at the time) "
Projection date
Jun 1st 2017
143. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“First name”
-pending-
“official name”“First name”
As At - as we knew (at the time) "
???
Projection date
Jun 1st 2017
144. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
Name:
“official name”
-pending-
“official name”“First name”
As At - as we knew (at the time) "
Projection date
Jun 1st 2017
👍
Apply
145. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“official name”“First name”
As At - as we knew (at the time) "
“patched name”
???
Projection date
Jun 1st 2017
Name:
“official name”
-pending-
146. t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“official name”“First name”
As At - as we knew (at the time) "
👎
Do NOT
Apply
“patched name”
Projection date
Jun 1st 2017
Name:
“official name”
-pending-
147. As At - as we knew (at the time) "
Projection date
Jun 1st 2017
Name:
“official name”t
Apr 1st
2016
Jan 1st
2016
Jan 1st
2017
Jun 1st
2017
“First name” “official name”
“patched name”
150. Challenge #5
How to produce a report for March 1st
like if we had made it June 1st?
151. Challenge #5
- We don’t want to suffer from hindsight bias
- We need to understand how we had perceived the world in the past
- We should not see some events that happened after a viewpoint
t
152. Q: How to produce a report for March 1st, like if we had
made it June 1st?
153. Name:
???
t
Q: How to produce a report for March 1st, like if we had
made it June 1st?
???
Feb 2nd
2016
Feb 1st
2016
Feb 28th
2016
March 1st
2016
Feb 3rd
2016
Jul 1st
2016
Jun 1st
2016
154. t
“name 1”
“name 2”
“name 5”
Feb 2nd
2016
Feb 1st
2016
Feb 28th
2016
March 1st
2016
Feb 3rd
2016
Jul 1st
2016
Jun 1st
2016
Q: How to produce a report for March 1st, like if we had
made it June 1st? Name:
???
???
155. t
Feb 2nd
2016
Feb 1st
2016
Feb 28th
2016
March 1st
2016
“name 1”
“name 2”
“name 5”
Feb 3rd
2016
Jul 1st
2016
Jun 1st
2016
Q: How to produce a report for March 1st, like if we had
made it June 1st? Name:
???
Date of the
report
Report
target date
156. t
Feb 2nd
2016
Feb 1st
2016
Feb 28th
2016
March 1st
2016
“name 1”
“name 2”
“name 5”
Feb 3rd
2016
Jul 1st
2016
Jun 1st
2016
Q: How to produce a report for March 1st, like if we had
made it June 1st?
Date of the
report
Report
target date
Apply all events created
before June 1st (viewPoint)
Don’t apply events created
after June 1st (viewPoint)
157. t
Feb 2nd
2016
Feb 1st
2016
Feb 28th
2016
March 1st
2016
“name 5”
Feb 3rd
2016
Jul 1st
2016
Jun 1st
2016
Q: How to produce a report for March 1st, like if we had
made it June 1st?
Date of the
report
Report
target date
Apply all events created
before June 1st (viewPoint)
“name 1”
“name 2”
Don’t apply events created
after June 1st (viewPoint)
158. t
Feb 2nd
2016
Feb 1st
2016
Feb 28th
2016
March 1st
2016
“name 5”
Feb 3rd
2016
Jul 1st
2016
Jun 1st
2016
Q: How to produce a report for March 1st, like if we had
made it June 1st?
Date of the
report
Report
target date
Apply all events created
before June 1st (viewPoint)
“name 1”
“name 2”
Don’t apply events created
after June 1st (viewPoint)
173. Challenge #6
- Events are facts
- Commands are used to take decisions and to raise events
- Command may fail
t
Q: What should we do with ’retroactive’?
174.
175. a) Apply a retroactive command to a aggregate
projected in the past? (may fail)
176. a) Apply a retroactive command to a aggregate
projected in the past? (may fail)
b) Create an event in the past for an aggregate?
177. a) Apply a retroactive command to a aggregate
projected in the past? (may fail)
b) Create an event in the past for an aggregate?
c) The answer C
182. tChallenge #7
- We store instants in our Event Store (createdAt, validFrom timestamps)
- Some events are applied on an entire “day” (e.g. June 8th)
183. t
Q: Which timestamps should we pick to describe a “day”?
Challenge #7
- We store instants in our Event Store (createdAt, validFrom timestamps)
- Some events are applied on an entire “day” (e.g. June 8th)
184. t
Q: Which timestamps should we pick to describe a “day”?
Q: Is UTC enough to store all instants?
Challenge #7
- We store instants in our Event Store (createdAt, validFrom timestamps)
- Some events are applied on an entire “day” (e.g. June 8th)
185. UTC [Utéçé;-)]
Universal Time Coordinated is the primary time standard by which the world
regulates clocks and time. It does not observe daylight saving time.
186. Instant [ˈÇaAriiiv]
Describes “when something happened”. Can then be interpreted in a
particular time zone and calendar system.
e.g.: 2018-05-18T16:40:35Z (Z means UTC, i.e. +00h)
190. UTC (+00)
Jun 8th 2018
00h (+ 03h)
Paris (+02)
Istanbul (+03)
Jun 8th 2018
23h59 (+ 03h)
What does June 8th even mean?
191. UTC (+00)
Jun 8th 2018
00h (+ 03h)
Jun 7th 2018
21h (+ 00h)
Paris (+02)
Istanbul (+03)
Jun 8th 2018
23h59 (+ 03h)
Jun 8th 2018
21h59 (+ 00h)
What does June 8th even mean?
192. UTC (+00)
Paris (+02)
Istanbul (+03)
Jun 8th 2018
00h (+ 02h)
Jun 8th 2018
23h59 (+ 02h)
Jun 8th 2018
23h59 (+ 03h)
Jun 8th 2018
21h59 (+ 00h)
Jun 8th 2018
00h (+ 03h)
Jun 7th 2018
21h (+ 00h)
What does June 8th even mean?
193. UTC (+00)
Paris (+02)
Istanbul (+03)
Jun 8th 2018
00h (+ 02h)
Jun 7th 2018
22h (+ 00h)
Jun 8th 2018
23h59 (+ 02h)
Jun 8th 2018
21h59 (+ 02h)
Jun 8th 2018
23h59 (+ 03h)
Jun 8th 2018
21h59 (+ 00h)
Jun 8th 2018
00h (+ 03h)
Jun 7th 2018
21h (+ 00h)
What does June 8th even mean?
194. UTC (+00)
Paris (+02)
Istanbul (+03)
Jun 7th 2018
22h (+ 00h)
Jun 8th 2018
21h59 (+ 02h)
Jun 8th 2018
21h59 (+ 00h)
Jun 7th 2018
21h (+ 00h)
Event 1 ?
Should event 1 be involved in a June 8th projection?
195. UTC (+00)
Paris (+02)
Istanbul (+03)
Jun 7th 2018
22h (+ 00h)
Jun 8th 2018
21h59 (+ 02h)
Jun 8th 2018
21h59 (+ 00h)
Jun 7th 2018
21h (+ 00h)
Event 1?
only if it’s June 8th - Paris time
197. UTC (+00)
Paris (+02)
Istanbul (+03)
Jun 7th 2018
22h (+ 00h)
Jun 8th 2018
21h59 (+ 02h)
Jun 8th 2018
21h59 (+ 00h)
Jun 7th 2018
21h (+ 00h)
June 8th Paris time !!= June 8th Istanbul time
198. UTC Time is mandatory to store & compare events
UTC (+00)
Paris (+02)
Istanbul (+03)
Jun 7th 2018
22h (+ 00h)
Jun 8th 2018
21h59 (+ 02h)
Jun 8th 2018
21h59 (+ 00h)
Jun 7th 2018
21h (+ 00h)
199. UTC is mandatory but not enough
UTC (+00)
Paris (+02)
Istanbul (+03)
Jun 7th 2018
22h (+ 00h)
Jun 8th 2018
21h59 (+
02h)
Jun 8th 2018
21h59 (+
00h)
Jun 7th 2018
21h (+ 00h)
218. 7 SPEAKERS, 5 TALKS ORIGINAUX DE 15-20 MIN
EN QUOI NOUS INTÉRESSER AUX PROBLÈMES DU
MÉTIER NOUS A RENDU PLUS EFFICACE
LE MARDI 26 JUIN, CHEZ
DOMAIN FROM THE TRENCHES
225. As Of #2 - as we should have known
“Patched name” is not <<discarded>> by the
following FundRenamed event (“official name”)
226. As Of #2 - as we should have known
But then it is replaced by the new one
( “OfficialName”)
227.
228.
229. You want to be ahead of what is done in software?
You understand there are bigger issues than just coding
We are looking for people who are daring, curious, sharing,
but who especially like team work
recruits