% Life Beyond Relational Database % Capital Match Team % 2016-03-10
- Introduction
- Event-Sourcing Model
- Implementation & Usage
- Future works
- Capital Match is the leading plaform in Singapore for peer-to-peer lending to SMEs
- Backend system developed in Haskell, frontend in Clojurescript/Om since 2014
- Core Development team of 3 + 1: Amar, Arnaud, Guo Liang, Zhou Yu
- Really great for querying
$\longrightarrow$ SQL Rocks! - Conceptually simple to understand: Everything is a Table
- Ubiquitous
- Writes/updates are complex
- Impedance Mismatch: Lot of data is more tree-ish or graph-ish
- One single Database for everything
$\longrightarrow$ SPOF - Mutable State
- RDBMS stores the state of the model at some point in time...
- ... But we are also interested in the transitions ...
- ... And state1 can always be reconstructed from a sequence of transitions.
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.
- Audit current state and what lead to it
- Implement generic undo/redo mechanism2
- Run simulations with different hypothesis over live data
- Cope with data format migrations
- Handle potentially conflicting changes3
- Events are what makes a model dynamic: What affects it, how it reacts to outside world...
- Provide foundation for Domain Driven Design techniques
$\longrightarrow$ Better business models, Ubiquitous language - Lead to Event Storming technique for "requirements" elicitation and business domain modelling4
- Each model delimits a Bounded Context: It is responsible for a single cohesive part of the domain
- Models are pure immutable data structures
- Distinguish Commands from Events
-
Commands compute Event from State
act :: Command -> Model -> Event
-
Events modify model
apply :: Event -> Model -> Model
Services are used to orchestrate interaction between one or more business models and the outside world
- Services are functions operating across several contexts
- They can be synchronous or asynchronous (we use mostly synchronous)5
- There are no distributed transactions: Service has to cope with failures from each context
- We have a monad to express effects and sequencing on each context:
WebStateM
newtype WebStateM g l m a = WebStateM { runWebM :: TVar g -> l -> m a }
-
g
is a "global" Model which can be accessed concurrently$\longrightarrow$ protected in STM -
l
is local data, contextual to a single service execution -
m
is underlying monad, usuallyIO
data StoredEvent s = StoredEvent { eventVersion :: EventVersion
, eventType :: EventType s
, eventDate :: Date
, eventUser :: UserId
, eventRequest :: Encoded Hex
, eventSHA1 :: Encoded Hex
, event :: ByteString
}
- We use a simple Append-only file store, writing serialized events (mostly JSON) packed with metadata
- Each event has a (monotonically increasing) version which is used for proper deserialization
- Events carry useful information for troubleshooting and auditing: User who initiated the request, request id itself, SHA1 representing version of appplication
- Events Store serializes concurrent writes
- Anatomy of a complete business model
- Web layer w/ servant
- Service layer (w/ Free monads...)
- Business model
- Migration code
- Standalone service
- Using Haskell scripts for operational queries and updates
- Separate Read Model from Write Model
- Write Model: Append-only linear data store per context, very fast, minimize locking/write time
- Read model: Optimized for specific querying, may be relational if needed in order to make it more user-friendly
- Resilience of models
$\longrightarrow$ Replication - Use Raft to maintain strong consistency of models: several implementations in Haskell
- Started implementation of practical cluster based on Raft, called raptr
- Turn event stream into a source of truth
$\longrightarrow$ Blockchain6 and beyond... - Juno: Smart contracts over Raft cluster
- Uses cryptographically signed events to ensure history cannot be tampered with
- Turns journal into a "legally binding ledger"?
Footnotes
-
Assuming state is deterministic of course ↩
-
May require invertible events ↩
-
That's the way RDBMS handle transactional isolation: Record a log of all operations on data then reconcile when transactions are committed ↩
-
I never know how many
l
s modelling takes... ↩ -
Synchronicity is a property of the business domain, e.g. depends on what client expects from the service and whether or not he wants to "pay" for synchronous confirmation ↩
-
Blockchain is all rage in the FinTech ecosystem those days, although early implementation like Bitcoins or Dogecoins failed to deliver all their promises. ↩