Learn how to build Reactive Systems with Akka. Examples in both Java 8 and Scala.
Abstract:
The demands and expectations for applications have changed dramatically in recent years. Applications today are deployed on a wide range of infrastructure; from mobile devices up to thousands of nodes running in the cloud—all powered by multi-core processors. They need to be rich and collaborative, have a real-time feel with millisecond response time and should never stop running. Additionally, modern applications are a mashup of external services that need to be consumed and composed to provide the features at hand. We are seeing a new type of applications emerging to address these new challenges—these are being called Reactive Applications.
In this talk we will introduce you to Akka and discuss how it can help you deliver on the four key traits of Reactive; Responsive, Resilient, Elastic and Message-Driven. We will start with the basics of Akka and work our way towards some of its more advanced modules such as Akka Cluster and Akka Persistence—all driven through code and practical examples.
3. 3
Apps in the 60s-90s
were written for
Apps today
are written for
4. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines
5. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
6. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors
7. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
8. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM
9. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
10. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk
11. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
12. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
Slow networks
13. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
Slow networks Fast networks
14. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
Slow networks Fast networks
Few concurrent users
15. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
Slow networks Fast networks
Few concurrent users Lots of concurrent users
16. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
Slow networks Fast networks
Few concurrent users Lots of concurrent users
Small data sets
17. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
Slow networks Fast networks
Few concurrent users Lots of concurrent users
Small data sets Large data sets
18. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
Slow networks Fast networks
Few concurrent users Lots of concurrent users
Small data sets Large data sets
Latency in seconds
19. 3
Apps in the 60s-90s
were written for
Apps today
are written for
Single machines Clusters of machines
Single core processors Multicore processors
Expensive RAM Cheap RAM
Expensive disk Cheap disk
Slow networks Fast networks
Few concurrent users Lots of concurrent users
Small data sets Large data sets
Latency in seconds Latency in milliseconds
25. Responsive
• Real-time, engaging, rich and collaborative
• Create an open and ongoing dialog with users
• More efficient workflow; inspires a feeling of connectedness
• Fully Reactive enabling push instead of pull
7
“The move to these technologies is already paying off.
Response times are down for processor intensive code–such as image
and PDF generation–by around 75%.”
Brian Pugh, VP of Engineering, Lucid Software
27. Message-Driven
• Loosely coupled architecture, easier to extend, maintain, evolve
• Asynchronous and non-blocking
• Concurrent by design, immutable state
• Lower latency and higher throughput
9
“Clearly, the goal is to do these operations concurrently and
non-blocking, so that entire blocks of seats or sections are not locked.
We’re able to find and allocate seats under load in less than 20ms
without trying very hard to achieve it.”
Andrew Headrick, Platform Architect, Ticketfly
30. 11
The Actor Model
A computational model that embodies:
31. 11
The Actor Model
A computational model that embodies:
✓ Processing
32. 11
The Actor Model
A computational model that embodies:
✓ Processing
✓ Storage
33. 11
The Actor Model
A computational model that embodies:
✓ Processing
✓ Storage
✓ Communication
34. A computational model that embodies:
✓ Processing
✓ Storage
✓ Communication
Supports 3 axioms—when an Actor receives a message it can:
11
The Actor Model
35. A computational model that embodies:
✓ Processing
✓ Storage
✓ Communication
Supports 3 axioms—when an Actor receives a message it can:
1. Create new Actors
11
The Actor Model
36. A computational model that embodies:
✓ Processing
✓ Storage
✓ Communication
Supports 3 axioms—when an Actor receives a message it can:
1. Create new Actors
2. Send messages to Actors it knows
11
The Actor Model
37. A computational model that embodies:
✓ Processing
✓ Storage
✓ Communication
Supports 3 axioms—when an Actor receives a message it can:
1. Create new Actors
2. Send messages to Actors it knows
3. Designate how it should handle the next message it receives
11
The Actor Model
38. The essence of an actor
from Akka’s perspective
0. DEFINE
1. CREATE
2. SEND
3. BECOME
4. SUPERVISE
12
39. public class Greeting implements Serializable {
public final String who;
public Greeting(String who) { this.who = who; }
}
!
public class Greeter extends AbstractActor {{
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchAny(unknown -> {
println(“Unknown message " + unknown);
}).build());
}}
0. DEFINE
X
40. public class Greeting implements Serializable {
public final String who;
public Greeting(String who) { this.who = who; }
}
!
public class Greeter extends AbstractActor {{
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchAny(unknown -> {
println(“Unknown message " + unknown);
}).build());
}}
0. DEFINE
X
Define the message(s) the Actor
should be able to respond to
41. public class Greeting implements Serializable {
public final String who;
public Greeting(String who) { this.who = who; }
}
!
public class Greeter extends AbstractActor {{
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchAny(unknown -> {
println(“Unknown message " + unknown);
}).build());
}}
0. DEFINE
X
Define the message(s) the Actor
should be able to respond to
Define the Actor class
42. public class Greeting implements Serializable {
public final String who;
public Greeting(String who) { this.who = who; }
}
!
public class Greeter extends AbstractActor {{
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchAny(unknown -> {
println(“Unknown message " + unknown);
}).build());
}}
0. DEFINE
X
Define the message(s) the Actor
should be able to respond to
Define the Actor class
Define the Actor’s behavior
44. 1. CREATE
Create an Actor system
ActorSystem system = ActorSystem.create("MySystem");
!
ActorRef greeter =
system.actorOf(Props.create(Greeter.class), “greeter");
45. 1. CREATE
Create an Actor system
Actor configuration
ActorSystem system = ActorSystem.create("MySystem");
!
ActorRef greeter =
system.actorOf(Props.create(Greeter.class), “greeter");
46. 1. CREATE
Create an Actor system
Actor configuration
ActorSystem system = ActorSystem.create("MySystem");
!
ActorRef greeter =
system.actorOf(Props.create(Greeter.class), “greeter");
Give it a name
47. 1. CREATE
Create an Actor system
ActorSystem system = ActorSystem.create("MySystem");
!
ActorRef greeter =
system.actorOf(Props.create(Greeter.class), “greeter");
Give it a name
Create the Actor
Actor configuration
48. 1. CREATE
Create an Actor system
ActorSystem system = ActorSystem.create("MySystem");
!
ActorRef greeter =
system.actorOf(Props.create(Greeter.class), “greeter");
Give it a name
You get an ActorRef back
Create the Actor
Actor configuration
49. 0. DEFINE
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
13
case Greeting(who) => log.info(s"Hello ${who}")
}
}
50. 0. DEFINE
13
Define the message(s) the Actor
should be able to respond to
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) => log.info(s"Hello ${who}")
}
}
51. 0. DEFINE
13
Define the message(s) the Actor
should be able to respond to
Define the Actor class
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) => log.info(s"Hello ${who}")
}
}
52. 0. DEFINE
13
Define the message(s) the Actor
should be able to respond to
Define the Actor class
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) => log.info(s"Hello ${who}")
}
}
Define the Actor’s behavior
53. 1. CREATE
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) => log.info("Hello " + who)
}
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
54. 1. CREATE
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
Create an Actor system
case Greeting(who) => log.info("Hello " + who)
}
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
55. 1. CREATE
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
Create an Actor system
case Greeting(who) => log.info("Hello " }
Actor configuration
+ who)
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
56. 1. CREATE
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) => log.info("Hello " + who)
}
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
Give it a name
Create an Actor system
Actor configuration
57. 1. CREATE
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
Create an Actor system
case Greeting(who) => log.info("Hello " }
Actor configuration
+ who)
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
Give it a name
Create the Actor
58. 1. CREATE
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
Create an Actor system
case Greeting(who) => log.info("Hello " }
Actor configuration
+ who)
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
Give it a name
You get an ActorRef bCarcekate the Actor
73. Name resolution—like a file-system
A
Foo Bar
B
C
B
E
A
D
C
/Foo
/Foo/A
Guardian System Actor
74. Name resolution—like a file-system
A
Foo Bar
B
C
B
E
A
D
C
/Foo
/Foo/A
/Foo/A/B
Guardian System Actor
75. Name resolution—like a file-system
A
Foo Bar
B
C
B
E
A
D
C
/Foo
/Foo/A
/Foo/A/B
/Foo/A/D
Guardian System Actor
76. 2. SEND
X
greeter.tell(new Greeting("Charlie Parker”), sender);
77. 2. SEND
X
greeter.tell(new Greeting("Charlie Parker”), sender);
Send the message asynchronously
78. 2. SEND
X
Pass in the sender ActorRef
greeter.tell(new Greeting("Charlie Parker”), sender);
Send the message asynchronously
79. Bring it together
X
public class Greeting implements Serializable {
public final String who;
public Greeting(String who) { this.who = who; }
}
public class Greeter extends AbstractActor {{
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchAny(unknown -> {
println(“Unknown message " + unknown);
}).build());
}
}}
!
ActorSystem system = ActorSystem.create("MySystem");
ActorRef greeter = system.actorOf(Props.create(Greeter.class), “greeter");
greeter.tell(new Greeting(“Charlie Parker”));
80. 2. SEND
17
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) => log.info(s”Hello ${who}")
}
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
greeter ! Greeting("Charlie Parker")
81. 2. SEND
17
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) => log.info(s”Hello ${who}")
}
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
greeter ! Greeting("Charlie Parker")
Send the message asynchronously
82. Bring it together
18
case class Greeting(who: String)
!
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) => log.info(s”Hello ${who}")
}
}
!
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
greeter ! Greeting("Charlie Parker")
84. 3. BECOME
X
public class Greeter extends AbstractActor {
public Greeter {
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchEquals(“stop" -> {
!!!! }).build();
}
}
85. 3. BECOME
X
public class Greeter extends AbstractActor {
public Greeter {
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchEquals(“stop" -> {
!!!! }).build();
}
}
context().become(ReceiveBuilder.
86. 3. BECOME
X
public class Greeter extends AbstractActor {
public Greeter {
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchEquals(“stop" -> {
!!!! }).build();
}
}
Change the behavior
context().become(ReceiveBuilder.
87. 3. BECOME
X
public class Greeter extends AbstractActor {
public Greeter {
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchEquals(“stop" -> {
!!!! }).build();
}
}
Change the behavior
context().become(ReceiveBuilder.
match(Greeting.class, m -> {
88. 3. BECOME
X
public class Greeter extends AbstractActor {
public Greeter {
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchEquals(“stop" -> {
!!!! }).build();
}
}
Change the behavior
context().become(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Go Away!”);
89. 3. BECOME
X
public class Greeter extends AbstractActor {
public Greeter {
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchEquals(“stop" -> {
!!!! }).build();
}
}
Change the behavior
context().become(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Go Away!”);
}).build());
90. 3. BECOME
X
public class Greeter extends AbstractActor {
public Greeter {
receive(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Hello " + m.who);
}).
matchEquals(“stop" -> {
!!!! }).build();
}
}
Change the behavior
context().become(ReceiveBuilder.
match(Greeting.class, m -> {
println(“Go Away!”);
}).build());
91. 3. BECOME
19
class GreetingActor extends Actor with ActorLogging {
def receive = happy
!
val happy: Receive = {
case Greeting(who) => log.info(s”Hello ${who}")
case Angry => context become angry
}
!
val angry: Receive = {
case Greeting(_) => log.info("Go away!")
case Happy => context become happy
}
}
92. 3. BECOME
19
class GreetingActor extends Actor with ActorLogging {
def receive = happy
!
val happy: Receive = {
case Greeting(who) => log.info(s”Hello ${who}")
case Angry => context become angry
}
!
val angry: Receive = {
case Greeting(_) => log.info("Go away!")
case Happy => context become happy
}
}
Redefine the behavior
94. Resilient
• Failure is embraced as a natural state in the app lifecycle
• Resilience is a first-class construct
• Failure is detected, isolated, and managed
• Applications self heal
21
“The Typesafe Reactive Platform helps us maintain a very
aggressive development and deployment cycle, all in a fail-forward manner.
It’s now the default choice for developing all new services.”
Peter Hausel, VP Engineering, Gawker Media
122. 4. SUPERVISE
X
Every single actor has a default supervisor strategy.
Which is usually sufficient.
But it can be overridden.
class Supervisor extends UntypedActor {
private SupervisorStrategy strategy = new OneForOneStrategy(
10, Duration.create(1, TimeUnit.MINUTES),
DeciderBuilder.
match(ArithmeticException.class, e -> resume()).
match(NullPointerException.class, e -> restart()).
matchAny( e -> escalate()).
build());
!
@Override public SupervisorStrategy supervisorStrategy() {
return strategy;
}
123. 4. SUPERVISE
X
class Supervisor extends UntypedActor {
private SupervisorStrategy strategy = new OneForOneStrategy(
10, Duration.create(1, TimeUnit.MINUTES),
DeciderBuilder.
match(ArithmeticException.class, e -> resume()).
match(NullPointerException.class, e -> restart()).
matchAny( e -> escalate()).
build());
!
@Override public SupervisorStrategy supervisorStrategy() {
return strategy;
}
ActorRef worker = context.actorOf(
Props.create(Worker.class), "worker");
public void onReceive(Object i) throws Exception {
…
}
}
124. Monitor through Death Watch
X
public class WatchActor extends AbstractActor {
final ActorRef child = context().actorOf(Props.empty(), "child");
!
public WatchActor() {
context().watch(child);
receive(ReceiveBuilder.
match(Terminated.class,
t -> t.actor().equals(child),
t -> {
… // handle termination
}).build()
);
}
}
125. Monitor through Death Watch
X
public class WatchActor extends AbstractActor {
final ActorRef child = context().actorOf(Props.empty(), "child");
!
public WatchActor() {
context().watch(child);
receive(ReceiveBuilder.
match(Terminated.class,
t -> t.actor().equals(child),
t -> {
… // handle termination
}).build()
);
}
}
Create a child actor
126. Monitor through Death Watch
X
public class WatchActor extends AbstractActor {
final ActorRef child = context().actorOf(Props.empty(), "child");
!
public WatchActor() {
context().watch(child);
receive(ReceiveBuilder.
match(Terminated.class,
t -> t.actor().equals(child),
t -> {
… // handle termination
}).build()
);
}
}
Create a child actor
Watch it
127. Monitor through Death Watch
X
public class WatchActor extends AbstractActor {
final ActorRef child = context().actorOf(Props.empty(), "child");
!
public WatchActor() {
context().watch(child);
receive(ReceiveBuilder.
match(Terminated.class,
t -> t.actor().equals(child),
t -> {
… // handle termination
}).build()
);
}
}
Create a child actor
Watch it
Handle termination message
128. 4. SUPERVISE
29
Every single actor has a default
supervisor strategy.
Which is usually sufficient.
But it can be overridden.
129. 4. SUPERVISE
29
Every single actor has a default
supervisor strategy.
Which is usually sufficient.
But it can be overridden.
class Supervisor extends Actor {
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: ArithmeticException => Resume
case _: NullPointerException => Restart
case _: Exception => Escalate
}
!
val worker = context.actorOf(Props[Worker], name = "worker")
!
def receive = {
130. 4. SUPERVISE
29
class Supervisor extends Actor {
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: ArithmeticException => Resume
case _: NullPointerException => Restart
case _: Exception => Escalate
}
!
val worker = context.actorOf(Props[Worker], name = "worker")
!
def receive = {
case n: Int => worker forward n
}
}
!
131. Cleanup & (Re)initialization
30
class Worker extends Actor {
...
override def preRestart(
reason: Throwable, message: Option[Any]) {
... // clean up before restart
}
override def postRestart(reason: Throwable) {
... // init after restart
}
}
132. Monitor through Death Watch
31
class Watcher extends Actor {
val child = context.actorOf(Props.empty, "child")
context.watch(child)
!
def receive = {
case Terminated(`child`) => … // handle child termination
}
}
133. Monitor through Death Watch
31
Create a child actor
class Watcher extends Actor {
val child = context.actorOf(Props.empty, "child")
context.watch(child)
!
def receive = {
case Terminated(`child`) => … // handle child termination
}
}
134. Monitor through Death Watch
31
Create a child actor
Watch it
class Watcher extends Actor {
val child = context.actorOf(Props.empty, "child")
context.watch(child)
!
def receive = {
case Terminated(`child`) => … // handle child termination
}
}
135. Monitor through Death Watch
31
Create a child actor
Watch it
class Watcher extends Actor {
val child = context.actorOf(Props.empty, "child")
context.watch(child)
Handle termination message
!
def receive = {
case Terminated(`child`) => … // handle child termination
}
}
137. Elastic
• Elasticity and Scalability to embrace the Cloud
• Adaptive Scale on Demand
• Clustered servers support joining and leaving of nodes
• More cost-efficient utilization of hardware
33
“Our traffic can increase by as much as 100x for 15 minutes each day.
Until a couple of years ago, noon was a stressful time.
Nowadays, it’s usually a non-event.”
Eric Bowman, VP Architecture, Gilt Groupe
153. • Supports two different models:
• Command Sourcing
• Event Sourcing
• Great for implementing
• durable actors
• replication
• CQRS etc.
• Messages persisted to Journal and replayed on restart
43
Use Akka Persistence
156. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
157. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery
as normal operation
158. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery
as normal operation
only state-changing behavior
during recovery
159. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery
as normal operation
only state-changing behavior
during recovery
persisted before validation
160. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery
as normal operation
only state-changing behavior
during recovery
persisted before validation events cannot fail
161. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery
as normal operation
only state-changing behavior
during recovery
persisted before validation events cannot fail
allows retroactive changes to
the business logic
162. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery
as normal operation
only state-changing behavior
during recovery
persisted before validation events cannot fail
allows retroactive changes to
the business logic
fixing the business logic will not
affect persisted events
163. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery
as normal operation
only state-changing behavior
during recovery
persisted before validation events cannot fail
allows retroactive changes to
the business logic
fixing the business logic will not
affect persisted events
naming: represent intent,
imperative
164. X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery
as normal operation
only state-changing behavior
during recovery
persisted before validation events cannot fail
allows retroactive changes to
the business logic
fixing the business logic will not
affect persisted events
naming: represent intent,
imperative
naming: things that have
completed, verbs in past tense
165. Domain Events
• Things that have completed, facts
• Immutable
• Verbs in past tense
Akka
Persistence
Webinar
• CustomerRelocated
• CargoShipped
• InvoiceSent
“State transitions are an important part of our problem
space and should be modeled within our domain.”
Greg Young, 2008
166. Life beyond Distributed Transactions:
an Apostate’s Opinion
Position Paper by Pat Helland
http://www-‐db.cs.wisc.edu/cidr/cidr2007/papers/cidr07p15.pdf
“In general, application developers simply do not implement
large scalable applications assuming distributed transactions.”
Pat Helland
Akka
Persistence
Webinar
167. Consistency boundary
• An Actor is can define an Aggregate Root
• Each containing one or more Entities
• Aggregate Root is the Transactional Boundary
• Strong consistency within an Aggregate
• Eventual consistency between Aggregates
• No limit to scalability
Akka
Persistence
Webinar
172. • Purely asynchronous and non-blocking
web frameworks
• No container required, no inherent
bottlenecks in session management
48
Typesafe Reactive Platform
• Actors are asynchronous and
communicate via message passing
• Supervision and clustering in support of
fault tolerance
• Asynchronous and immutable
programming constructs
• Composable abstractions enabling
simpler concurrency and parallelism