What if the pitfalls identified in Peter Deutsch’s “eight fallacies of distributed computing” were not inconveniences but opportunities? We present our view of the emerging patterns within distributed systems architecture and argue for a modern semantics of distributed systems based on sympathy with the network. In our approach, event sourcing and stream processing provide the processing model, while microservices and bounded contexts provide the domain model.
We discuss the implementation of Concursus, an open source framework for bringing event sourcing patterns to distributed applications, which represents the evolution of our thinking about event sourcing, based on our practical experience of implementing distributed systems in production.
6. Event Sourcing
“Event Sourcing ensures that all changes to application state are stored as a sequence of
events. Not just can we query these events, we can also use the event log to reconstruct
past states, and as a foundation to automatically adjust the state to cope with retroactive
changes.”
http://martinfowler.com/eaaDev/EventSourcing.html
7. What is Concursus?
Problems Concursus addresses:
ü Processing events in a scalable and reliable way
ü Processing guarantees and ordering: exactly once, out of order, repeated or missed
delivery, etc..
ü Building meaningful domain models to reason about and build business logic around
ü Flexibility: building additional views as needed
8. Tendencies:
• From internet of users to internet of things
• From “presence” to “presents”
• From monoliths to microservices
Why Concursus?
14. Handling Events
ü Delivery constraints
out of order, repeated, delayed or missed delivery
ü Processing guarantees
at least once or exactly once processing, idempotency
ü Ordering
partial ordering across aggregates (with reasonable assumptions)
15. Data Processing Layers
ü Durable
sufficiently durable buffer for async processing (what’s happening)
ü Persistent
a permanent record of everything that has happened (what happened)
ü Transient
fast and consistent, but also disposable state (what happens)
16. Building Blocks
• Java 8 and Kotlin: APIs
• Cassandra: Persistent state (Event store)
• Kafka: Durable state (Message broker)
• Hazelcast: Transient state (cache, idempotency filters)
• Also, RabbitMQ and Redis
17. Sources of Inspiration
Stream processing frameworks such as Apache Storm and Spark
Google papers: Cloud dataflow, MillWheel
Apache Spark papers
The Axon CQRS framework
Domain Driven Design
Functional programming
24. Domain Model: Summary
Every Event occurs to an Aggregate, identified by its type and id.
Every Event has an eventTimestamp, generated by the source of
the event.
An Event History is a log of Events, ordered by eventTimestamp,
with an additional processingTimestamp which records when the
Event was captured.
31. Processing Model: Summary
Events arrive partitioned, interleaved and out-of-order.
Events are sorted into event histories by aggregate type and id.
Events are sorted within event histories by event timestamp, not
processing timestamp.
Event consumers need to take into account the possibility that an
event history may be incomplete at the time it is read – consider
using a watermark to give incoming events time to “settle”.
32. Programming Model: Core Metaphor
Received
at
Depot
Loaded
onto
Truck
Delivery
Failed
Received
at
Depot
Loaded
onto
Truck
Delivered
41. Kotlin Mapping
fun describeEvent(event: ParcelEvent): Unit = when (event) {
is ReceivedAtDepot -‐> println("Received at depot:
${event.depotId}")
is LoadedOntoTruck -‐> println("Loaded onto truck:
${event.truckId}")
is Delivered -‐> println("Delivered to:
${event.destinationId}")
is DeliveryFailed -‐> println("Delivery failed")
}
42. Event-handling middleware is a chain of Consumer<Event>s that transforms, routes, persists
and dispatches events. A single event submitted to this chain may be:
■ Checked against an idempotency filter (e.g. a Hazelcast distributed cache)
■ Serialised to JSON
■ Written to a message queue topic
■ Retrieved from the topic and deserialised
■ Persisted to an event store (e.g. Cassandra)
■ Published to an event handler which maintains a query-optimised view of part of the system
■ Published to an event handler which maintains an index of aggregates by event property
values (e.g. lightbulbs by wattage)
Event-Handling Middleware
43. • Kafka Streams
• Narrative threads across event histories
• Generic Attribute indexing
• State management and caching
• Improved cloud tooling
Future Directions