Pro Yearly is on sale from $80 to $50! »

Commander: Better Distributed Applications through CQRS, Event Sourcing, and Immutable Logs

Commander: Better Distributed Applications through CQRS, Event Sourcing, and Immutable Logs

Talk video: https://www.youtube.com/watch?v=B1-gS0oEtYc
Reference implementation on GitHub: https://github.com/capitalone/cqrs-manager-for-distributed-reactive-services

Have you ever hit a wall with REST? Does modeling your problem domain into CRUD-able entities feel like fitting a square peg into a round hole? Perhaps instead of modeling our services like little databases, we should instead model them like reactors over immutable event streams.

REST APIs are great, but their typical implementation tightly couples various concerns that would be better separated:

* Reads (perception) with writes (action)
* Current state with historical narrative
* Business logic with HTTP request processing, and with operational concerns like metrics and monitoring

Commander is:

1. A pattern for writing distributed applications that de-couples these concerns using ideas from CQRS and Event Sourcing, thereby alleviating common frustrations with CRUD-flavored REST
2. An implementation of the core component in this pattern using Clojure in the HTTP layer, using Java with the new Kafka Streams library in the event stream processing layer, and using Datomic for persistence

The Commander pattern imposes a clear separation of action from perception, and uses immutable values conveyed by Kafka and the Kafka Streams library to separate business logic from HTTP request processing, all while preserving the historical narrative of the entire event stream.

In this talk, I'll discuss the benefits and tradeoffs of applying this pattern in an enterprise context, and both demonstrate and open-source my implementation.

C4adca5489db5657ba31ca253525147f?s=128

Bobby Calderwood

September 17, 2016
Tweet

Transcript

  1. Commander Better Distributed Applications through CQRS, Event Sourcing, and Immutable

    Logs
  2. Hi, I’m Bobby I’m on the Technology Fellow’s team at

    I dislike accidental complexity bobby.calderwood@capitalone.com @bobbycalderwood https://github.com/bobby
  3. Problem Space • Provide valuable informational and transactional services via

    Mobile and Web software • To lots of customers, with excellent user experience • Securely and in compliance with regulations • With ability to easily enhance, experiment, monitor, maintain, and operate • By many participants within a large organization
  4. Big Ideas • Immutability is central to information systems •

    Data language of system >> Programming language of components • Action and perception are not the same, and immutability facilitates their separation • Businesses services are not databases, they’re event stream reactors • Cross-cutting concerns must be satisfied in the presence of Conway’s Law
  5. Problematic Architecture /[resource](/:id) Database Logic Analytics Web Services Business Logic

    Databases and Services Operations Metrics/Monitoring Security/Compliance Audit Afterthoughts Other side-effects
  6. Immutability is central to information systems

  7. Event Sourcing Analogy Image by Alan Light CC BY-SA 3.0

  8. Data Loss by Design /[resource](/:id) Database Logic Analytics Web Services

    Business Logic Databases and Services Operations Metrics/Monitoring Security/Compliance Audit Afterthoughts Other side-effects
  9. The (data) language of the System >> The (runtime) language

    of each component
  10. What is system language? /[resource](/:id) Database Logic Analytics Web Services

    Business Logic Databases and Services Operations Metrics/Monitoring Security/Compliance Audit Afterthoughts Other side-effects
  11. Action != Perception

  12. Writes != Reads

  13. Writes Tied to Reads /[resource](/:id) Database Logic Analytics Web Services

    Business Logic Databases and Services Operations Metrics/Monitoring Security/Compliance Audit Afterthoughts Other side-effects
  14. Business Services are not Databases

  15. –John M. Culkin “We shape our tools and thereafter our

    tools shape us.”
  16. Database Leaking /[resource](/:id) Database Logic Analytics Web Services Business Logic

    Databases and Services Operations Metrics/Monitoring Security/Compliance Audit Afterthoughts Other side-effects
  17. Conway’s Law Yup, totally a thing

  18. –Melvin Conway “organizations which design systems ... are constrained to

    produce designs which are copies of the communication structures of these organizations”
  19. Cross-cutting Concerns? /[resource](/:id) Database Logic Analytics Web Services Business Logic

    Databases and Services Operations Metrics/Monitoring Security/Compliance Audit Afterthoughts Other side-effects
  20. We can do better!

  21. Commander • A better architecture for APIs and services using

    REST + Immutable Event Log + Reactive Event Stream Processing • The write-handling component of that architecture, my implementation is in Clojure
  22. Commander Architecture • Several categories of microservices with structured interactions

    among them • REST + CQRS + Event Sourcing + Reactive Event Stream Processing
  23. Commander Architecture /commands(/:id) /[query-apis] commands /updates Read- optimized View Command

    Processing Analytics [other topics] Arbitrary command action Arbitrary command action Microconsume rs Web Services Load balancing Auth(n|z) Input validation events Business Logic Event Log Datastores and Materialized Views sync “Ledger” of events Operations Metrics/Monitoring Security/Compliance Audit User-facing APIs Third-party Partners WS/SSE audit
  24. Commander Component /commands(/:id) /[query-apis] commands /updates Read- optimized View Command

    Processing Analytics [other topics] Arbitrary command action Arbitrary command action Microconsume rs Web Services Load balancing Auth(n|z) Input validation events Business Logic Event Log Datastores and Materialized Views sync “Ledger” of events Operations Metrics/Monitoring Security/Compliance Audit User-facing APIs Third-party Partners WS/SSE audit
  25. Embrace Immutability

  26. Immutable Data Log /commands(/:id) /[query-apis] commands /updates Read- optimized View

    Command Processing Analytics [other topics] Arbitrary command action Arbitrary command action Microconsume rs Web Services Load balancing Auth(n|z) Input validation events Business Logic Event Log Datastores and Materialized Views sync “Ledger” of events Operations Metrics/Monitoring Security/Compliance Audit User-facing APIs Third-party Partners WS/SSE audit
  27. Express actions in domain language (not in database language)

  28. A Command {"id": "33bb75db-6e13-48ee-8a54-b3976d3d065b", "action": "transfer-money", "data": {"from_account": "12345", "to_account":

    "54321", "amount": 10000} "timestamp": "2016-05-20T14:33:28.902-00:00"}
  29. An Event {"id": "d435ed18-4ff7-4cae-a21b-3adb7b06fe58", "parent": "33bb75db-6e13-48ee-8a54-b3976d3d065b", "action": "money-transferred", "data": {"id":

    "a6b903f6-0b9c-4c5b-95fa-afd4cc3bf938", "from_account": "12345", "to_account": "54321", "amount": 10000}, "timestamp": "2016-05-20T14:33:28.904-00:00"}
  30. Separate Action from Perception

  31. 1 Log => n Data Views Read- optimized View Command

    Processing Analytics [other topics] Arbitrary command action Arbitrary command action Microconsume rs events Business Logic Event Log Datastores and Materialized Views “Ledger” of events Operations Metrics/Monitoring Security/Compliance Audit HDFS Elastic Search Domain- specific
  32. Commander Architecture /commands(/:id) /[query-apis] commands /updates Read- optimized View Command

    Processing Analytics [other topics] Arbitrary command action Arbitrary command action Microconsume rs Web Services Load balancing Auth(n|z) Input validation events Business Logic Event Log Datastores and Materialized Views sync “Ledger” of events Operations Metrics/Monitoring Security/Compliance Audit User-facing APIs Third-party Partners WS/SSE audit
  33. Exploit Conway’s Law

  34. Primary Team Provides /commands(/:id) /[query-apis] commands /updates Read- optimized View

    Command Processing Analytics [other topics] Arbitrary command action Arbitrary command action Microconsume rs Web Services Load balancing Auth(n|z) Input validation events Business Logic Event Log Datastores and Materialized Views sync “Ledger” of events Operations Metrics/Monitoring Security/Compliance Audit User-facing APIs Third-party Partners WS/SSE audit
  35. Enterprise Provides /commands(/:id) /[query-apis] commands /updates Read- optimized View Command

    Processing Analytics [other topics] Arbitrary command action Arbitrary command action Microconsume rs Web Services Load balancing Auth(n|z) Input validation events Business Logic Event Log Datastores and Materialized Views sync “Ledger” of events Operations Metrics/Monitoring Security/Compliance Audit User-facing APIs Third-party Partners WS/SSE audit
  36. Why Commander Component? • Single writer to commands topic •

    Ensuring schema conformance • Indexing all Commands and Events for reads and server-push • Provides optional illusion of synchrony to clients
  37. How to implement Reactive Services?

  38. Kafka Streams!

  39. What is Kafka? • Apache Kafka is publish-subscribe messaging rethought

    as a distributed commit log • But it’s not really about messaging, that’s just the interface • Logs > Messages for my domain • It provides distributed, immutable logs!
  40. What is Kafka Streams? • A Java library for building

    streaming applications on top of Kafka, lives in your application • Low-level API for building topologies of processors, streams, and tables • High-level DSL for common patterns like filter, map, aggregations, joins, stateful and stateless processing • Nice operational characteristics (low latency, elastic, fault-tolerant)
  41. How to use Kafka Streams within Commander • Implement Command

    Processor • Implement Event consumers and producers • Provide local state management as backend for APIs
  42. Commander Architecture /commands(/:id) /[query-apis] commands /updates Read- optimized View Command

    Processing Analytics [other topics] Arbitrary command action Arbitrary command action Microconsume rs Web Services Load balancing Auth(n|z) Input validation events Business Logic Event Log Datastores and Materialized Views sync “Ledger” of events Operations Metrics/Monitoring Security/Compliance Audit User-facing APIs Third-party Partners WS/SSE audit
  43. KStreamBuilder builder = new KStreamBuilder(); KStream<UUID, Map> commands = builder.stream(commandsTopic);

    KStream<UUID, Map> customerEvents = commands .filter((id, command) -> command.get("action") .equals("create-customer")) .map((id, command) -> { Map userEvent = new HashMap(command); userEvent.put("action", "customer-created"); userEvent.put("parent", id); Map userValue = (Map) userEvent.get("data"); userValue.put("id", UUID.randomUUID()); return new KeyValue<>(UUID.randomUUID(), userEvent); }).through(eventsTopic); KStream<UUID, Map> customers = customerEvents .map((id, event) -> { Map customer = (Map) event.get("data"); UUID customerId = (UUID) customer.get("id"); return new KeyValue<UUID, Map>(customerId, customer); }); customers.through(customersTopic); StateStoreSupplier store = Stores.create("Customers") .persistent() .build(); builder.addStateStore(store); customers.process(customerStore, "Customers"); this.kafkaStreams = new KafkaStreams(builder, kafkaStreamsConfig); this.kafkaStreams.start();
  44. public class CustomerStore implements Processor<UUID, Map> { private KeyValueStore<UUID, Map>

    store; public List<Customer> getCustomers() { List<Customer> customers = new ArrayList<>(); KeyValueIterator<UUID, Map> iterator = store.all(); while (iterator.hasNext()) { KeyValue<UUID, Map> entry = iterator.next(); customers.add(new Customer(entry.value)); } iterator.close(); return customers; } public Customer getCustomer(UUID id) { return new Customer(store.get(id)); } @Override public void init(ProcessorContext processorContext) { this.store = (KeyValueStore<UUID, Map>) context.getStateStore("Customers"); } @Override public void process(UUID uuid, Map map) { store.put(uuid, map); } @Override public void punctuate(long l) {} @Override public void close() {} }
  45. Demo

  46. Summary • Capture customer intent and business events as immutable

    data in domain language • From these action streams, services implement their own functionality in this common lingua franca • building many independent data views • reactively • without temporal or organizational coordination
  47. Giant Shoulders • Immutability: Rich Hickey, Stu Halloway • CQRS:

    Udi Dahan, Martin Fowler, Chris Richardson • Kafka Event Stream Reactors: Neha Narkhede, Jay Kreps, Martin Kleppmann • Organization and Management: Mel Conway, Eliyahu Goldratt, Gene Kim, Michael Nygard
  48. References • https://tinyurl.com/capital-one-cmdr • http://www.datomic.com/ • http://blog.cognitect.com/?tag=NewNormal+Series • http://www.confluent.io/blog/event-sourcing-cqrs-stream- processing-apache-kafka-whats-connection/

    • https://engineering.linkedin.com/distributed-systems/log-what- every-software-engineer-should-know-about-real-time-datas- unifying • https://www.infoq.com/presentations/Value-Values