Functional Programming
& Event Sourcing
A pair made in heaven
twitter: @rabbitonweb, email: paul.szulc@gmail.com
Functional Programming
● no assignment statements
● no assignment statements
● no variables
● no assignment statements
● no variables
● once given a value, never
change
● no assignment statements
● no variables
● once given a value, never
change
● no side-effects at all
● no assignment statements
● no variables
● once given a value, never
change
● no side-effects at all
“The functional programmer
sounds rather like a mediæval
monk,
“The functional programmer
sounds rather like a mediæval
monk, denying himself the
pleasures of life
“The functional programmer
sounds rather like a mediæval
monk, denying himself the
pleasures of life in the hope
that it will make him virtuous.”
Functional Programming
case class User(id: Long, fn: String, ln: String)
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
}
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
val maybeUser: Option[User] = cache.check(id)
}
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
val maybeUser: Option[User] = cache.check(id)
if (maybeUser.isDefined) {
maybeUser.get
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
val maybeUser: Option[User] = cache.check(id)
if (maybeUser.isDefined) {
maybeUser.get
} else {
val user: User = repo.retrieve(id)
cache.insert(user.id, user)
}
}
}
No concept of ‘time’
f(input): output
input f(input): output
input f(input): output output
input f(input): output output g(input): output
input f(input): output output g(input): output output
input f(input): output output g(input): output output
h = g o f
input f(input): output output g(input): output output
Modularity & composition
h = g o f
input f(input): output output g(input): output output
/**
* This function returns a reversed list
* @param list A list to be reversed
* @return A reversed list
*/
public List<T> reverse(List<t> list) { ??? }
/**
* This function returns a reversed list
* @param list A list to be reversed
* @return A reversed list
*/
public List<T> reverse(List<t> list) { ??? }
/**
* This function returns a reversed list
* @param list A list to be reversed
public List<T> reverse(List<t> list) { ??? }
/**
* This function returns a reversed list
public List<T> reverse(List<t> list) { ??? }
/**
public List<T> reverse(List<t> list) { ??? }
public List<T> reverse(List<t> list) { ??? }
public List<T> reverse(List<t> list) {
return list.sort();
}
def smdfknmsdfp[A](a: A): A = ???
def smdfknmsdfp[A](a: A): A = a
def identity[A](a: A): A = a
def smdfknmsdfp[A](a: A): A = a
def smdfknmsdfp(a: Int): Int = ???
def smdfknmsdfp(a: Int): Int = a
= a + 10
= 10
“Why Functional Programming Matters”
J. Hughes
http://comjnl.oxfordjournals.org/content/32/2/98.
full.pdf
“Why Functional Programming Matters”
J. Hughes, Nov. 1988
http://comjnl.oxfordjournals.org/content/32/2/98.
full.pdf
Soft introduction
“Why Functional Programming Matters”
J. Hughes, Nov. 1988
http://comjnl.oxfordjournals.org/content/32/2/98.
full.pdf
“Program Design by Calculation”
J.N. Oliveira
http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
“Program Design by Calculation”
J.N. Oliveira, Draft
http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
Patterns in FP World
“Program Design by Calculation”
J.N. Oliveira, Draft
http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
Patterns in FP World Math Matters
“Program Design by Calculation”
J.N. Oliveira, Draft
http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
“A lengthy approach to Haskell fundamentals”
http://www.davesquared.net/2012/05/lengthy-
approach-to-haskell.html
Functional Programming
Functional Programming
How to do something
useful?
How to do something
useful?
case class User(id: Long, fn: String, ln: String)
class Cache { def check(id: Long): Option[User] = ??? }
case class UserRepo(cache: Cache) {
def retrieve(id: Long): User = ???
}
class UserFinder(cache: Cache, repo: UserRepo) {
def findUser(id: Long): User = {
val maybeUser: Option[User] = cache.check(id)
if (maybeUser.isDefined) {
maybeUser.get
} else {
val user: User = repo.retrieve(id)
cache.insert(user.id, user)
}
}
}
case class User(id: Long, fn: String, ln: String)
case class User(id: Long, fn: String, ln: String)
class Cache {}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
case None => retrieve(id)(c)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
case None => retrieve(id)(c)
}
}
}
S => (S, A)
State[S, A]
S => (S, A)
State[S, A]
S => (S, A)
.run(S)
State[S, A]
S => (S, A)
.map(A => B): State[S, B]
State[S, A]
S => (S, A)
.flatMap(A => State[S, B]): State[S,B]
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
}
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s)
}
}
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s)
}
}
def check(id: String) =
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s)
}
}
def check(id: String) =
(c: Cache) => (c, c.get(id))
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s)
}
}
def check(id: String) = State[Cache, Option[User]].apply {
(c: Cache) => (c, c.get(id))
}
object State {
def apply[S, A] (f: S => (S,A)): State[S, A] =
new State[S, A] {
def run(s: S) = f(s(
}
}
def check(id: String) = State[Cache, Option[User]]{
(c: Cache) => (c, c.get(id))
}
trait State[S, +A] {
}
trait State[S, +A] {
def run(initial: S): (S, A)
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State {
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
(_, _ )
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
(_, f(a))
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(_, f(a))
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
f(a)
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
val (s, a) = run(s0)
f(a)
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
f(a)
}
}
}
trait State[S, +A] {
def run(initial: S): (S, A)
def map[B](f: A => B): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
(s, f(a))
}
}
def flatMap[B](f: A => State[S,B]): State[S, B] =
State { s0 =>
val (s, a) = run(s0)
f(a).run(s)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ???
def retrieve(id: Long)(cache: Cache): (Cache, User) = ???
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
case None => retrieve(id)(c)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long)(cache: Cache): (Cache, Option[User]) = ???
def retrieve(id: Long)(cache: Cache): (Cache, User) = ???
def findUser(id: Long)(cache: Cache): (Cache, User) = {
val (c, mu) = check(id)(cache)
mu match {
case Some(u) => (c, u)
case None => retrieve(id)(c)
}
}
}
case class User(id: Long, fn: String, ln: String)
class Cache {}
def check(id: Long): State[Cache, Option[User]] = ???
def retrieve(id: Long): State[Cache, User] = ???
def findUser(id: Long): State[Cache, User] = {
for {
maybeUser <- check(id)
user <- maybeUser match {
case Some(u) => State { c => (c, u)}
case None => retrieve(id)
}
} yield (user)
}
Event Sourcing
Event Sourcing
driven by business
Bloggers Conf App
Bloggers Conf App
● Can create an account
Bloggers Conf App
● Can create an account
● List all bloggers already using the app
Bloggers Conf App
● Can create an account
● List all bloggers already using the app
● Mark/unmark other blogger as a friend
Bloggers Conf App
● Can create an account
● List all bloggers already using the app
● Mark/unmark other blogger as a friend
● Mark/unmark other blogger as an enemy
Bloggers Conf App
● Can create an account
● List all bloggers already using the app
● Mark/unmark other blogger as a friend
● Mark/unmark other blogger as an enemy
● Deactivate its account
Bloggers Conf App
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Bloggers Conf App
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Bloggers Conf App
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
Bloggers Conf App
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
The Structure
Structure is not that
important
Structure is not that
important
Changes more often than behaviour
Start thinking about facts
occurring
Start thinking about facts
occurring
Derive structure from them
Blogger
Account
Created
(id=3)
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
Friends
id friend_id
Enemies
id enemy_id
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
Enemies
id enemy_id
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Befriended
Blogger
id=2
Unfriended
Blogger
id=2
Blogger
Account
Created
(id=3)
Befriended
Blogger
id=1
Made
Enemy of
Blogger
id=2
Event Sourcing
driven by business
Event Sourcing
The only model that does not lose data
“Enemy of my enemy is
my friend”
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
4 Tomasz Młynarski T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
4 2
Bloggers
id first_name last_name active
1 Jan Kowalski T
2 Krystian Nowak T
3 Malgorzata Kucharska T
4 Tomasz Młynarski T
5 Monika Jagoda T
Friends
id friend_id
3 1
Enemies
id enemy_id
3 2
4 2
2 5
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
id = 3
id = 2
id = 1
id = 4
id = 5
id = 6
Benefits of Event Sourcing
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
● Fits well with machine learning
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
● Fits well with machine learning
● Preserves history - question not yet asked
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
● Fits well with machine learning
● Preserves history - question not yet asked
● Writing regression tests is easy
Benefits of Event Sourcing
● Ability to go in time and figure out exactly what have
happened
● Scientific measurements over time, compare time
periods
● Built-in audit log
● Enables temporal querying
● Fits well with machine learning
● Preserves history - question not yet asked
● Writing regression tests is easy
● Polyglot data
Drawbacks of Event Sourcing
Drawbacks of Event Sourcing
● Historical record of your bad decisions
Drawbacks of Event Sourcing
● Historical record of your bad decisions
● Handling event duplicates
Drawbacks of Event Sourcing
● Historical record of your bad decisions
● Handling event duplicates
● Data eventually consistent
How to implement it?
Events vs Commands
Journal
Journal
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Initialized(“1”, “Jan”, “Kowalski”
Journal
val id = “”
val firstName: String = “”
val lastName: String = “”
val friends: List[String] =
List()
Initialized(“1”, “Jan”, “Kowalski”
Journal
val id = “1”
val firstName: String =
“Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Befriended(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List()
Befriended(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List(“10”, “31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“31”)
validation
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“31”)
validation
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
validation
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
validation
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Befriended(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Befriended(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Befriended(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Befriended(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] = List
(“10”, “31”)
Befriend(“34”)
ACK
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List(“10”, “31”, “34”)
Befriend(“34”)
Journal
val id = “1”
val firstName: String = “Jan”
val lastName: String =
“Kowalski”
val friends: List[String] =
List(“10”, “31”, “34”)
Let’s see some code!
https://github.
com/rabbitonweb/es_cqrs_example
What we are missing?
What we are missing?
1. Read-model
What we are missing?
1. Read-model
2. Validation
And that’s all folks!
Paweł Szulc
Paweł Szulc
http://rabbitonweb.com
Paweł Szulc
http://rabbitonweb.com
@rabbitonweb
Paweł Szulc
http://rabbitonweb.com
@rabbitonweb
https://github.com/rabbitonweb/
Thank you!

Functional Programming & Event Sourcing - a pair made in heaven

  • 1.
    Functional Programming & EventSourcing A pair made in heaven twitter: @rabbitonweb, email: paul.szulc@gmail.com
  • 2.
  • 3.
  • 4.
    ● no assignmentstatements ● no variables
  • 5.
    ● no assignmentstatements ● no variables ● once given a value, never change
  • 6.
    ● no assignmentstatements ● no variables ● once given a value, never change ● no side-effects at all
  • 7.
    ● no assignmentstatements ● no variables ● once given a value, never change ● no side-effects at all
  • 8.
    “The functional programmer soundsrather like a mediæval monk,
  • 9.
    “The functional programmer soundsrather like a mediæval monk, denying himself the pleasures of life
  • 10.
    “The functional programmer soundsrather like a mediæval monk, denying himself the pleasures of life in the hope that it will make him virtuous.”
  • 11.
  • 12.
    case class User(id:Long, fn: String, ln: String)
  • 13.
    case class User(id:Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? }
  • 14.
    case class User(id:Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? }
  • 15.
    case class User(id:Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { }
  • 16.
    case class User(id:Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { } }
  • 17.
    case class User(id:Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { val maybeUser: Option[User] = cache.check(id) } }
  • 18.
    case class User(id:Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { val maybeUser: Option[User] = cache.check(id) if (maybeUser.isDefined) { maybeUser.get } } }
  • 19.
    case class User(id:Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { val maybeUser: Option[User] = cache.check(id) if (maybeUser.isDefined) { maybeUser.get } else { val user: User = repo.retrieve(id) cache.insert(user.id, user) } } }
  • 20.
    No concept of‘time’
  • 21.
  • 22.
  • 23.
  • 24.
    input f(input): outputoutput g(input): output
  • 25.
    input f(input): outputoutput g(input): output output
  • 26.
    input f(input): outputoutput g(input): output output
  • 27.
    h = go f input f(input): output output g(input): output output
  • 28.
  • 29.
    h = go f input f(input): output output g(input): output output
  • 30.
    /** * This functionreturns a reversed list * @param list A list to be reversed * @return A reversed list */ public List<T> reverse(List<t> list) { ??? }
  • 32.
    /** * This functionreturns a reversed list * @param list A list to be reversed * @return A reversed list */ public List<T> reverse(List<t> list) { ??? }
  • 33.
    /** * This functionreturns a reversed list * @param list A list to be reversed public List<T> reverse(List<t> list) { ??? }
  • 34.
    /** * This functionreturns a reversed list public List<T> reverse(List<t> list) { ??? }
  • 35.
  • 36.
  • 37.
    public List<T> reverse(List<t>list) { return list.sort(); }
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
    def smdfknmsdfp(a: Int):Int = a = a + 10 = 10
  • 44.
    “Why Functional ProgrammingMatters” J. Hughes http://comjnl.oxfordjournals.org/content/32/2/98. full.pdf
  • 45.
    “Why Functional ProgrammingMatters” J. Hughes, Nov. 1988 http://comjnl.oxfordjournals.org/content/32/2/98. full.pdf
  • 46.
    Soft introduction “Why FunctionalProgramming Matters” J. Hughes, Nov. 1988 http://comjnl.oxfordjournals.org/content/32/2/98. full.pdf
  • 47.
    “Program Design byCalculation” J.N. Oliveira http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
  • 48.
    “Program Design byCalculation” J.N. Oliveira, Draft http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
  • 49.
    Patterns in FPWorld “Program Design by Calculation” J.N. Oliveira, Draft http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
  • 50.
    Patterns in FPWorld Math Matters “Program Design by Calculation” J.N. Oliveira, Draft http://www4.di.uminho.pt/~jno/ps/pdbc_part.pdf
  • 51.
    “A lengthy approachto Haskell fundamentals” http://www.davesquared.net/2012/05/lengthy- approach-to-haskell.html
  • 52.
  • 53.
  • 57.
    How to dosomething useful?
  • 58.
    How to dosomething useful?
  • 59.
    case class User(id:Long, fn: String, ln: String) class Cache { def check(id: Long): Option[User] = ??? } case class UserRepo(cache: Cache) { def retrieve(id: Long): User = ??? } class UserFinder(cache: Cache, repo: UserRepo) { def findUser(id: Long): User = { val maybeUser: Option[User] = cache.check(id) if (maybeUser.isDefined) { maybeUser.get } else { val user: User = repo.retrieve(id) cache.insert(user.id, user) } } }
  • 60.
    case class User(id:Long, fn: String, ln: String)
  • 61.
    case class User(id:Long, fn: String, ln: String) class Cache {}
  • 62.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ...
  • 63.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ...
  • 64.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { } }
  • 65.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) } }
  • 66.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) } } }
  • 67.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) case None => retrieve(id)(c) } } }
  • 68.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ... def retrieve(id: Long)(cache: Cache): (Cache, User) = ... def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) case None => retrieve(id)(c) } } }
  • 69.
  • 70.
  • 71.
    State[S, A] S =>(S, A) .run(S)
  • 72.
    State[S, A] S =>(S, A) .map(A => B): State[S, B]
  • 73.
    State[S, A] S =>(S, A) .flatMap(A => State[S, B]): State[S,B]
  • 74.
    object State { defapply[S, A] (f: S => (S,A)): State[S, A] = }
  • 75.
    object State { defapply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s) } }
  • 76.
    object State { defapply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s) } } def check(id: String) =
  • 77.
    object State { defapply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s) } } def check(id: String) = (c: Cache) => (c, c.get(id))
  • 78.
    object State { defapply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s) } } def check(id: String) = State[Cache, Option[User]].apply { (c: Cache) => (c, c.get(id)) }
  • 79.
    object State { defapply[S, A] (f: S => (S,A)): State[S, A] = new State[S, A] { def run(s: S) = f(s( } } def check(id: String) = State[Cache, Option[User]]{ (c: Cache) => (c, c.get(id)) }
  • 80.
  • 81.
    trait State[S, +A]{ def run(initial: S): (S, A) }
  • 82.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = } }
  • 83.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { } } }
  • 84.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => (_, _ ) } } }
  • 85.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => (_, f(a)) } } }
  • 86.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (_, f(a)) } } }
  • 87.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } }
  • 88.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = } }
  • 89.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = f(a) } }
  • 90.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = val (s, a) = run(s0) f(a) } }
  • 91.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = State { s0 => val (s, a) = run(s0) f(a) } } }
  • 92.
    trait State[S, +A]{ def run(initial: S): (S, A) def map[B](f: A => B): State[S, B] = State { s0 => val (s, a) = run(s0) (s, f(a)) } } def flatMap[B](f: A => State[S,B]): State[S, B] = State { s0 => val (s, a) = run(s0) f(a).run(s) } } }
  • 93.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ??? def retrieve(id: Long)(cache: Cache): (Cache, User) = ??? def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) case None => retrieve(id)(c) } } }
  • 94.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long)(cache: Cache): (Cache, Option[User]) = ??? def retrieve(id: Long)(cache: Cache): (Cache, User) = ??? def findUser(id: Long)(cache: Cache): (Cache, User) = { val (c, mu) = check(id)(cache) mu match { case Some(u) => (c, u) case None => retrieve(id)(c) } } }
  • 95.
    case class User(id:Long, fn: String, ln: String) class Cache {} def check(id: Long): State[Cache, Option[User]] = ??? def retrieve(id: Long): State[Cache, User] = ??? def findUser(id: Long): State[Cache, User] = { for { maybeUser <- check(id) user <- maybeUser match { case Some(u) => State { c => (c, u)} case None => retrieve(id) } } yield (user) }
  • 96.
  • 97.
  • 98.
  • 99.
    Bloggers Conf App ●Can create an account
  • 100.
    Bloggers Conf App ●Can create an account ● List all bloggers already using the app
  • 101.
    Bloggers Conf App ●Can create an account ● List all bloggers already using the app ● Mark/unmark other blogger as a friend
  • 102.
    Bloggers Conf App ●Can create an account ● List all bloggers already using the app ● Mark/unmark other blogger as a friend ● Mark/unmark other blogger as an enemy
  • 103.
    Bloggers Conf App ●Can create an account ● List all bloggers already using the app ● Mark/unmark other blogger as a friend ● Mark/unmark other blogger as an enemy ● Deactivate its account
  • 104.
    Bloggers Conf App Bloggers idfirst_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T
  • 105.
    Bloggers Conf App Bloggers idfirst_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1
  • 106.
    Bloggers Conf App Bloggers idfirst_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id 3 2
  • 107.
    Bloggers Conf App Bloggers idfirst_name last_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id 3 2 The Structure
  • 108.
    Structure is notthat important
  • 109.
    Structure is notthat important Changes more often than behaviour
  • 110.
    Start thinking aboutfacts occurring
  • 111.
    Start thinking aboutfacts occurring Derive structure from them
  • 112.
  • 113.
  • 114.
  • 115.
    Blogger Account Created (id=3) Befriended Blogger id=1 Made Enemy of Blogger id=2 Bloggers id first_namelast_name active 1 Jan Kowalski T 2 Krystian Nowak T Friends id friend_id Enemies id enemy_id
  • 116.
    Blogger Account Created (id=3) Befriended Blogger id=1 Made Enemy of Blogger id=2 Bloggers id first_namelast_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id Enemies id enemy_id
  • 117.
    Blogger Account Created (id=3) Befriended Blogger id=1 Made Enemy of Blogger id=2 Bloggers id first_namelast_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id
  • 118.
    Blogger Account Created (id=3) Befriended Blogger id=1 Made Enemy of Blogger id=2 Bloggers id first_namelast_name active 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id 3 2
  • 119.
  • 120.
  • 121.
  • 122.
    Event Sourcing The onlymodel that does not lose data
  • 123.
    “Enemy of myenemy is my friend”
  • 124.
    Bloggers id first_name last_nameactive 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T Friends id friend_id 3 1 Enemies id enemy_id 3 2
  • 125.
    Bloggers id first_name last_nameactive 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T 4 Tomasz Młynarski T Friends id friend_id 3 1 Enemies id enemy_id 3 2 4 2
  • 126.
    Bloggers id first_name last_nameactive 1 Jan Kowalski T 2 Krystian Nowak T 3 Malgorzata Kucharska T 4 Tomasz Młynarski T 5 Monika Jagoda T Friends id friend_id 3 1 Enemies id enemy_id 3 2 4 2 2 5
  • 127.
    id = 3 id= 2 id = 1 id = 4 id = 5 id = 6
  • 128.
    id = 3 id= 2 id = 1 id = 4 id = 5 id = 6
  • 129.
    id = 3 id= 2 id = 1 id = 4 id = 5 id = 6
  • 130.
    id = 3 id= 2 id = 1 id = 4 id = 5 id = 6
  • 131.
    id = 3 id= 2 id = 1 id = 4 id = 5 id = 6
  • 132.
    id = 3 id= 2 id = 1 id = 4 id = 5 id = 6
  • 133.
    id = 3 id= 2 id = 1 id = 4 id = 5 id = 6
  • 134.
    id = 3 id= 2 id = 1 id = 4 id = 5 id = 6
  • 135.
  • 136.
    Benefits of EventSourcing ● Ability to go in time and figure out exactly what have happened
  • 137.
    Benefits of EventSourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods
  • 138.
    Benefits of EventSourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log
  • 139.
    Benefits of EventSourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying
  • 140.
    Benefits of EventSourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying ● Fits well with machine learning
  • 141.
    Benefits of EventSourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying ● Fits well with machine learning ● Preserves history - question not yet asked
  • 142.
    Benefits of EventSourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying ● Fits well with machine learning ● Preserves history - question not yet asked ● Writing regression tests is easy
  • 143.
    Benefits of EventSourcing ● Ability to go in time and figure out exactly what have happened ● Scientific measurements over time, compare time periods ● Built-in audit log ● Enables temporal querying ● Fits well with machine learning ● Preserves history - question not yet asked ● Writing regression tests is easy ● Polyglot data
  • 144.
  • 145.
    Drawbacks of EventSourcing ● Historical record of your bad decisions
  • 146.
    Drawbacks of EventSourcing ● Historical record of your bad decisions ● Handling event duplicates
  • 147.
    Drawbacks of EventSourcing ● Historical record of your bad decisions ● Handling event duplicates ● Data eventually consistent
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
    Journal val id =“” val firstName: String = “” val lastName: String = “” val friends: List[String] = List()
  • 153.
    Journal val id =“” val firstName: String = “” val lastName: String = “” val friends: List[String] = List()
  • 154.
    Journal val id =“” val firstName: String = “” val lastName: String = “” val friends: List[String] = List()
  • 155.
    Journal val id =“” val firstName: String = “” val lastName: String = “” val friends: List[String] = List() Initialized(“1”, “Jan”, “Kowalski”
  • 156.
    Journal val id =“” val firstName: String = “” val lastName: String = “” val friends: List[String] = List() Initialized(“1”, “Jan”, “Kowalski”
  • 157.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List()
  • 158.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List()
  • 159.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List()
  • 160.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List() Befriended(“10”)
  • 161.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List() Befriended(“10”)
  • 162.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List(“10”)
  • 163.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”)
  • 164.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”)
  • 165.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”)
  • 166.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List(“10”, “31”)
  • 167.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”)
  • 168.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“31”)
  • 169.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“31”)
  • 170.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“31”) validation
  • 171.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“31”) validation
  • 172.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”)
  • 173.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”)
  • 174.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”)
  • 175.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) validation
  • 176.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) validation
  • 177.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) Befriended(“34”)
  • 178.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) Befriended(“34”)
  • 179.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) Befriended(“34”)
  • 180.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) Befriended(“34”)
  • 181.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”)
  • 182.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List (“10”, “31”) Befriend(“34”) ACK
  • 183.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List(“10”, “31”, “34”) Befriend(“34”)
  • 184.
    Journal val id =“1” val firstName: String = “Jan” val lastName: String = “Kowalski” val friends: List[String] = List(“10”, “31”, “34”)
  • 185.
    Let’s see somecode! https://github. com/rabbitonweb/es_cqrs_example
  • 186.
    What we aremissing?
  • 187.
    What we aremissing? 1. Read-model
  • 188.
    What we aremissing? 1. Read-model 2. Validation
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.