Upload
jimmy-lu
View
1.694
Download
1
Embed Size (px)
Citation preview
Agenda• Event Sourcing• Reactor• Spring Statemahcine• Demo of Event Sourcing with Reactor
and Spring Statemachine• Summary
Event Sourcing• All changes to application state are
stored as a sequence of events• Not only manage current state• Replays sequence of events to
reconstruct application state• Natively support audit trail• Eventually consistent• Commonly works with CQRS pattern
https://msdn.microsoft.com/en-us/library/dn589792.aspx
Event Store• Append-only store (events are
immutable)• Series immutable events• compensating event• Acts as the source of truth of
materialised view• Simple schema for storing event• Behaves like a database with message
broker characteristics
Event Store• Better work with version data format• Consider creating snapshots at
specific intervals if event stream is too large
• Consumers of the events must be idempotent
• Event ID typically maps to individual entities
• The order of the event is important
Event store
https://geteventstore.com/
Benefits of Event Sourcing• A solution to ensure atomicity when
mutating entity states while publishing events simultaneously
• 100% accurate audit logging which is not an afterthought
• Easy temporal queries• Single event centric model• Conflict management• Simplified/Better testing
A Taste of Event Sourcing• // Akka Persistence• public void onReceiveRecover(Object msg) { • if (msg instanceof Evt) { • state.update((Evt) msg); • } else if (msg instanceof SnapshotOffer) { • state = (ExampleState)((SnapshotOffer)msg).snapshot(); • } else { • unhandled(msg); • } • }
http://doc.akka.io/docs/akka/snapshot/java/persistence.html#Event_sourcing
• // Akka Persistence• public void onReceiveCommand(Object msg) {• if (msg instanceof Cmd) { • final String data = ((Cmd)msg).getData(); • final Evt evt1 = new Evt(data + "-" + getNumEvents()); • final Evt evt2 = new Evt(data + "-" + (getNumEvents() + 1)); • persistAll(asList(evt1, evt2), new Procedure<Evt>() { • public void apply(Evt evt) throws Exception { • state.update(evt); • if (evt.equals(evt2)) { • getContext().system().eventStream().publish(evt); • } • } • }); • } else if (msg.equals("snap")) { • saveSnapshot(state.copy()); • } • ......• }
http://doc.akka.io/docs/akka/snapshot/java/persistence.html#Event_sourcing
• public class Account { • ...... • • public List<Event> process(OpenAccountCommand cmd) { • return EventUtil.events(new AccountOpenedEvent(cmd.getInitialBalance())); • }• • public List<Event> process(CreditAccountCommand cmd) { • return EventUtil.events(new AccountCreditedEvent(cmd.getAmount(), cmd.getTransacti
onId())); • }• • public void apply(AccountOpenedEvent event) { • balance = event.getInitialBalance(); • }• • public void apply(AccountDebitedEvent event) { • balance = balance.subtract(event.getAmount()); • }• • public void apply(AccountCreditedEvent event) { • balance = balance.add(event.getAmount()); • } • } https://github.com/cer/event-sourcing-examples
Reactor• A foundational library for building reactive
fast-data applications on the JVM• An implementation of the Reactive Streams
Specification• Building on top of the Disruptor RingBuffer• Functional and reactive to allow for easy
composition of operations• 10’s of millions of operations per second
(even up to 100’s if you have enough hardware horsepower)
Snippet of Reactor• static { • // Only done once, statically, and shared across this classloader • Environment.initialize(); • } • // Create a Stream subclass we can sink values into • Broadcaster<String> b = Broadcaster.create(); • b • // dispatch onto a Thread other than 'main' • .dispatchOn(Environment.cachedDispatcher()) • // transform input to UC • .map(String::toUpperCase) • // only let certain values pass through • .filter(s -> s.startsWith("HELLO")) • // produce demand • .consume(s -> System.out.println(Thread.currentThread() + ": " + s)); • // Sink values into this Broadcaster • b.onNext("Hello World!"); • // This won't print • b.onNext("Goodbye World!"); • // Must wait for tasks in other threads to complete • Thread.sleep(500); http://projectreactor.io/
Reactor Modules• reactor-core• reactor-stream• reactor-bus• reactor-net
http://projectreactor.io/docs/reference/
reactor-core
http://projectreactor.io/docs/reference/
reactor-stream
http://projectreactor.io/docs/reference/
reactor-bus
http://projectreactor.io/docs/reference/
reactor-net
http://projectreactor.io/docs/reference/
What/Why State Machine• Application is and may exist in a finite
number of states and then something happens which takes your application from one state to the next.
• What will drive a state machine are triggers which are either based on events or timers.
• Behavior is always guaranteed to be consistent
• Easily debugged due to ways how operational rules are written in stone
Spring Statemachine (SSM)• Easy to use flat one level state machine for
simple use cases.• Hierarchical state machine structure to ease
complex state configuration.• State machine regions to provide even more
complex state configurations.• Usage of triggers, transitions, guards and
actions.• Distributed state machine based on a Zookeeper• State machine event listeners.
Abstractions Provided by SSM• States• Hierarchical
States• Regions• Transitions• Guards• Actions
• Pseudo States– Initial State– Terminate State– History State– Choice State– Fork State– Join State
• Distributed States
Turnstile
http://docs.spring.io/spring-statemachine/docs/current/reference/htmlsingle/
Turnstile Code Sample• public enum States { • LOCKED, UNLOCKED • }• • public enum Events { • COIN, PUSH • }
• public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception {
• states.withStates() • .initial(States.LOCKED) • .states(EnumSet.allOf(States.class)); • }
http://docs.spring.io/spring-statemachine/docs/current/reference/htmlsingle/
Turnstile Code Sample• public void configure(StateMachineTransitionConfigurer<States, Ev
ents> transitions) throws Exception { • transitions • .withExternal() • .source(States.LOCKED) • .target(States.UNLOCKED) • .event(Events.COIN) • .and() • .withExternal() • .source(States.UNLOCKED) • .target(States.LOCKED) • .event(Events.PUSH); • }
http://docs.spring.io/spring-statemachine/docs/current/reference/htmlsingle/
Washer
http://docs.spring.io/spring-statemachine/docs/current/reference/htmlsingle/
Washer Code Sample• public void configure(StateMachineStateConfigurer<States, Events> st
ates) throws Exception { • states• .withStates() • .initial(States.RUNNING) • .state(States.POWEROFF) • .end(States.END) • .and() • .withStates() • .parent(States.RUNNING) • .initial(States.WASHING) • .state(States.RINSING) • .state(States.DRYING) • .history(States.HISTORY, History.SHALLOW); • } http://docs.spring.io/spring-statemachine/docs/current/reference/htmlsingle/
Building Event Sourced Applications
• The states (finite, normally represented by enumeration) of a domain object are maintained by spring state machine
• Pub/Sub events by reactor-bus within the application
• Event stream could be mapped to reactor-stream with reactive streams support
• Distributed state could be used to enforce strong consistency of domain objects
Some Thoughts About The Idea
• Why not existing frameworks?• Lighter-weight state machine?• Lookup API for a state machine?
– State nextState = statemahcine.lookup(currentState, event);
• Distributed state or not?• https://github.com/spring-projects/sp
ring-statemachine/issues/7• Choice of event store? Kafka?
Cassandra?
Reference• Event Sourcing by Martin Fowler• Event Sourcing Basics• Developing event-driven microservices wi
th event sourcing and CQRS
• https://github.com/cer/event-sourcing-examples/wiki
• Akka Persistence Documentation• Spring Statemachine Documentation• Reactor Documentation