CQRS and Event Sourcing for Java Developers

5,177 views

Published on

As presented at CJUG. Recording will be up here: http://www.meetup.com/ChicagoJUG/events/231837105/

As soon as an application becomes even moderately complex, CQRS and an Event Sourced architecture start making a lot of sense. The talk is focused on: - the challenges and tactics of separating the write model from the query model in a complex domain - how commands naturally lead to events and to an event based system, and - how events get projected into useful, eventually consistent views. Event Sourcing is one of those things that you really need to push through at the beginning (much like TDD) and that - once understood and internalized, will change the way you architect a system. This talk introduces you to the basic concepts and problem spaces to solve.

Published in: Technology

CQRS and Event Sourcing for Java Developers

  1. 1. CQRS and Event Sourcing for Java Developers @myfear blog.eisele.net https://keybase.io/myfear
  2. 2. • Classical architectures and modernization • CRUD vs. CQRS • A little example • Wrapping it up Agenda
  3. 3. Classical Architectures
  4. 4. Application Server EAR - Enterprise Archive RESTMobile Web UI .JAR.JAR .JAR .JAR.JAR .JAR .JAR .JAR .JAR .JAR Browser RDBMS
  5. 5. Application Server Application Server Application Server EAR - Enterprise Archive RESTMobile Web UI .JAR.JAR .JAR .JAR.JAR .JAR .JAR .JAR .JAR .JAR Browser RDBMS
  6. 6. Modernization
  7. 7. Module Module Module WebUI .JAR.JAR .JAR .JAR.JAR .JAR .JAR .JAR .JAR .JARBrowser RDBMS RDBMS RDBMS
  8. 8. Routing Module Tracking Module Order Module Tracker UIBrowser HistoryDB Order DB RoutesDB
  9. 9. CRUD is OK!
  10. 10. • For simple domain models! • Complex models start to show weaknesses • DTO vs. VO • Read vs. Write performance • Optimistic Locking • Distributed Caches but only …
  11. 11. But what else?
  12. 12. Complexity of Domain Model Effort to change / enhance CRUD CQRS
  13. 13. • Command Query Responsibility Segregation CQRS
  14. 14. • The Command – for Writes • e.g. CreateCargo • The Query – for Reads • e.g. ListCargo Commands and Queries
  15. 15. Just separating reads from writes?
  16. 16. • The Command • handle(CreateCargo command) {…} • The Event • on(CargoCreated event) {…} Commands evolve into Events
  17. 17. • Occurred in the past • Changed the system state (Write operation) • CargoCreated, LegAdded, CargoRouted, … Events
  18. 18. WAIT! WHAT DID YOU DO TO MY ENTITIES?
  19. 19. • Book-keeping of changes • Contains a full history implicitly • Storing events in sequence with a strict global order (time-stamp based) • No updates or deletes. Ever! • No single “current state”. The collection of events make up the system state in any point of time. Persistent Events
  20. 20. • Capturing Changes instead of updating Objects JPA Entities vs. Immutable Facts
  21. 21. Current State is a second class citizen
  22. 22. • Capturing Changes instead of updating Objects The read-side
  23. 23. • Where was my vessel last week? • Who created the shipping request? • How much cargo did we ship last year? • Which vessels have been re-routed more than twice in under an hour? Answer all kind of new questions
  24. 24. The read-side cargoId location Location Cargo1 1.2,3 Cargo2 1.3,2 Cargo3 1.4,1 Cargo4 2.2,5 CargoRouted(1.2,3) CargoRouted(1.3,2) CargoRouted(1.4,1)
  25. 25. Neo4J Cassandra Cassandra
  26. 26. Implementation example. A little one.
  27. 27. Cargo Tracker https://github.com/lagom/activator-lagom-cargotracker
  28. 28. Registration Shipping Frontend Cassandra
  29. 29. restCall(Method.POST, "/api/registration", register() ), restCall(Method.GET, "/api/registration/all", getAllRegistrations() ), Write-Side Read-Side
  30. 30. The PersistentEntity public class CargoEntity extends PersistentEntity<RegistrationCommand, RegistrationEvent, CargoState> { //... } CargoEntity.java
  31. 31. The write-side PersistentEntityRef<RegistrationCommand> ref = persistentEntityRegistry.refFor( CargoEntity.class, request.getId()); RegistrationServiceImpl.java
  32. 32. The read-side (preparation) prepareCreateTables(session) .thenCompose(a -> prepareWriteCargo(session) .thenCompose(b -> prepareWriteOffset(session) .thenCompose(c -> selectOffset(session)))); CargoEventProcessor.java
  33. 33. The read-side (1) private CompletionStage<Done> prepareCreateTables(CassandraSession session) { return session.executeCreateTable( "CREATE TABLE IF NOT EXISTS cargo (" + "cargoId text, name text, description text," + "PRIMARY KEY (cargoId, destination))") ); }
  34. 34. The read-side (2) private CompletionStage<Done> prepareWriteCargo(CassandraSession session) { return session.prepare( "INSERT INTO cargo (cargoId, name, description, " + " owner,destination) VALUES (?, ?,?,?,?)") .thenApply(ps -> { setWriteCargo(ps); return Done.getInstance(); });
  35. 35. The read-side (offsets?) private CompletionStage<Optional<UUID>> selectOffset(CassandraSession session) { return session.selectOne("SELECT offset FROM blogevent_offset") .thenApply( optionalRow -> optionalRow.map(r -> r.getUUID("offset"))); }
  36. 36. The read-side (event trigger) @Override public EventHandlers defineEventHandlers(EventHandlersBuilder builder) { builder.setEventHandler(CargoRegistered.class, this::processCargoRegistered); return builder.build(); } RegistrationEvent!
  37. 37. The read-side (actual persistence) private CompletionStage<List<BoundStatement>> processCargoRegistered(CargoRegistered event, UUID offset) { BoundStatement bindWriteCargo = writeCargo.bind(); bindWriteCargo.setString("cargoId", event.getCargo().getId()); bindWriteCargo.setString("name", event.getCargo().getName()); bindWriteCargo.setString("description"); //... }
  38. 38. WHY IS THIS SO.. complicated?
  39. 39. BECAUSE.. it’s lightning FAST for users!!
  40. 40. [info] s.c.r.i.RegistrationServiceImpl - Cargo ID: 322667. [info] s.c.r.i.CargoEventProcessor - Persisted 322667
  41. 41. • All data kept in memory! • All state changes stored as events • Replay events of an PersistentEntity to recreate it • Strong consistency for PersistentEntity’s and Journal-Entries • Eventual Consistency for Read Side More precisely, because:
  42. 42. BECAUSE.. you can do a lot more a lot easier!!
  43. 43. • Recreate bugs • Migrate systems • Introduce new read-sides • Process higher volumes • Extended caching scenarios For example:
  44. 44. BECAUSE.. the examples are based on LAGOM and it DOES A LOT MORE for you!! ..oO(you can do this with Spring / Hibernate / Java EE – your choice)
  45. 45. • Lagom is asynchronous by default. • Developer productivity • Build for microservices • Takes you to production • …. You’ve heard this before, but:
  46. 46. ..oO(you can do this with Spring / Hibernate / Java EE – your choice)
  47. 47. Links and further reading
  48. 48. Project Site: http://www.lightbend.com/lagom GitHub Repo: https://github.com/lagom Documentation: http://www.lagomframework.com/documentation/1.0.x/java/Home.html Cargo Tracker Example: https://github.com/lagom/activator-lagom-cargotracker
  49. 49. •Keep all data in memory! • Store all state changes as events • Replay all events of an actor to recreate it • Strong consistencyfor Actor (aggregate) and Journal • Eventual Consistencyfor Read Side https://msdn.microsoft.com/en-us/library/jj554200.aspx
  50. 50. https://www.infoq.com/minibooks/domain-driven-design-quickly
  51. 51. Written for architects and developersthat must quickly gain a fundamental understandingof microservice-basedarchitectures, this freeO’Reilly reportexploresthe journey fromSOAto microservices,discussesapproachesto dismantling your monolith,and reviews the key tenets ofa Reactive microservice: • Isolate all the Things • Act Autonomously • Do OneThing, and Do It Well • Own Your State, Exclusively • Embrace AsynchronousMessage-Passing • Stay Mobile,but Addressable • Collaborate as Systems to Solve Problems http://bit.ly/ReactiveMicroservice
  52. 52. The detailed example inthis reportis based on Lagom, a new frameworkthat helps you follow the requirementsfor buildingdistributed,reactive systems. • Get an overview of the Reactive Programming model and basic requirementsfor developing reactive microservices • Learnhow to create base services, expose endpoints,and then connect them with a simple, web-based user interface • Understand how to deal with persistence,state, and clients • Use integration technologiesto start a successfulmigration away fromlegacy systems http://bit.ly/DevelopReactiveMicroservice
  53. 53. http://bit.ly/SustainableEnterprise • Understand thechallenges ofstartinga greenfield development vs tearingapart an existing brownfield application into services • Examine your business domain to see if microservices would bea good fit • Explorebest practices for automation,high availability, data separation,and performance • Align your development teams around business capabilities and responsibilities • Inspect design patterns such as aggregator, proxy, pipeline, or shared resources to model service interactions

×