Like many other software vendors Quicksign has chosen to migrate its SAAS platform from an on-premise monolith to a microservices platform in the Cloud.
Using a modern tech stack including Kafka, Kubernetes, the Camunda BPMN engine and some in-house development we have built a Kappa CQRS-ES multi-tenant platform able to execute BPMN workflows that can discover and consume any microservice registered in a Kubernetes backed extended registry that allows content type and protocol negotiation.
Our platform leverages Kafka Streams to dynamically generate from a reactive stream of workflow events a bespoke REST-API (HATEOAS) representation so as to ease our customers’ integration effort and allow fast iterations.
We propose to briefly introduce the audience to BPMN and then expose the rationale that led us to build this architecture, its advantages in terms of agility and scalability.
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Do microservices dream about CQRS-ES, Kafka Stream and BPMN ? - Voxxed microservices paris 2018
1. 2018-10-30
Cédric Vidal, CTO
Do Microservices dream about
CQRS, Kafka Stream and BPMN ?
@cedricvidal
#VoxxedMicroservices #QuickSign #Kafka #Camunda
2. First, why BPMN ?
Bad press
• Dusty
• Scary
• Complicated
• Is it like UML ??
• “I’d rather code it using my favorite language”
3. Digital onboarding
• European leader in digital onboarding for financial services
• B2B2C SAAS solution in white label
4. All our customers are different
• Specific to each customer/product/country
• Business requirements
• Integration requirements
• Different maturity levels, business wise and technical wise
5. Progressive onboarding of our customers
• Start small
• Optimize progressively
• Gain maturity, business wise and technical wise
6. Hence BPMN!
• A process for each
customer/product
• State and transitions
• XML file stored in git
• Multiple versions in prod
7. Kafka is the backbone of
microservices architecture
9. ~500 million tweets per day
~1,3 million trace entries per day
We are not twitter ...
10. But we grow!
+70% more
transactions
each year
Millions of
digital
subscriptions
per year
Critical
channel
11. Traceability
● Regulated business
● Qualified signature ETSI 319 411
● Be able to prove conformance
● Non-repudiation
=> we need to keep logs of everything
12. Query
View
apply events
on data
Query
Query Store
Event
Handler
Query
Handler
CQRS-ES using Kafka Streams
user
Command Query Responsibility Segregation (Event Sourcing)
Aggregation Root
Store
direct modification
commands & queries
Controller
reads
Pre-computed views
action
evaluates
Command
Event
EventEvent
Event Store
stores
produces
Command
Handler
reads
Event
EventEvent
Aggregation
Root
Event
Handler
Streams
Streams
Streams
Streams
15. BPMN in our architecture
REST APIWrite Interface Read Interface
Commands
Activities are
µ-services
OCR
Fraud
Signature
History stream
History SPI
append only immutable log
Queries
Extremely fast read access
StreamsKV StorePre-computed JSON
representations
Case µS
stream
processors
Async replies
Avro
16. Why Kafka ?
General observation
• Works for extremely massive big data use
cases
• Should handle easily our CQRS ES based
workloads …
Important for CQRS-ES
• At least once guaranteed delivery
• Sequential processing per topic partition
• RPC like low latency
• Durability
• Replayability
but isn’t quite enough!
17. Important for CQRS-ES
• Automatic offset tracking (inherited from
Consumer API)
• Embedded ephemeral disk based KV Store to
persist intermediate stream states and query
stores (Facebook’s RocksDB)
• Rich streaming DSL with operators
(aggregates, joins, …)
• Interactive Queries
Kafka Streams - The key
18. Can be tricky to grasp at first
• The trick is to make sure all messages related to a
given aggregation root end up on the same partition
How do you route your events?
ConsumergroupAConsumergroupB
C1
C2
C3
C4
C5
Kafka Cluster
Server 1
Server 2
Topic A
P0
P1
P2
P3
Aggregation
D1
D2 D3
D4 D5
root
partition = nextValue(topic) % numPartitions
Messages
M3
key=null
M2
key=null
M1
key=null
Streams
19. Event routing - Aggregation root id as key
– All messages belonging to a given aggregation root are given the
aggregation root id
partition = hash(key) % numPartitions
ConsumergroupAConsumergroupB
C1
C2
C3
C4
C5
Kafka Cluster
Server 1
Server 2
Topic A
P0
P1
P2
P3
Aggregation
D1
D2 D3
D4 D5
rootMessages
M2M3 M1
key=M1 key=M1 key=M1
Simple but … ⚠️ Warning : Loose consistency if compacting
Streams
20. – Use another composite id from which you can extract the aggregation
root id with a custom strategy
Event routing - Composite key
Custom Partitioner strategy :
rootkey = extractRootFrom(key)
partition = hash(rootkey) % numPartitions
ConsumergroupAConsumergroupB
C1
C2
C3
C4
C5
Kafka Cluster
Server 1
Server 2
Topic A
P0
P1
P2
P3
Aggregation
D1
D2 D3
D4 D5
rootMessages
M2M3 M1
key=M1M3 key=M1M2 key=M1
Streams
21. How do you query your data ?
Take 1: Application state as an external datastore
Read interface
Full Text Search Index forward
3
ask Kafka Stream
(using “Interactive Queries”)
2
Take 2: Application state in embedded RocksDB KV Store
1
round-robin
Read interface
C1
P1
P2
C2
P3
C3
P4
22. How do I return state after a command?
• Status 2xx/5xx anyone?
• Materialize the outcome to a kafka topic
• Wait for a specific correlation id
• Using Kafka Streams Interactive Queries and KV non blocking get
23. Crazy ?
● Quite a ride
● One API per customer
● API “generated” from BPMN
● Sync or Async
● Push or polling