This document discusses Clojure and the architecture used at Nuday Games for their game Rock Science. Key points include:
- Nuday Games uses Clojure for its backend due to benefits like concurrency, small team productivity, and attracting good engineers
- The architecture includes a frontend iOS app, load balancers, HTTP servers running a REST API on Tomcat, and databases
- The REST API is built with Liberator and routes/resources are defined with namespaces, schemas are used for validation
- Events are stored in a DynamoDB table and can be queried to retrieve events by player ID and timestamp
- Mutation commands could be replayed by reading from the event log in
3. Why Clojure?
● See “Hackers and Painters”
● Small dev team can do a lot quickly
● Easier to reason about concurrency
● Engineers attracted to Clojure tend to be
good
● Fits in well with all the battle-tested Java
machinery for service at scale
12. Types, what are they good for?
● Dynamic typing is wonderful, but…
● Prismatic Schema gives you validation and
documentation without fighting the compiler
14. Event sourcing
http://martinfowler.com/eaaDev/EventSourcing.html
The fundamental idea of Event Sourcing is that of ensuring
every change to the state of an application is captured in an
event object, and that these event objects are themselves
stored in the sequence they were applied for the same
lifetime as the application state itself.
21. Persisting events
● Events created on every
mutation, so write fast
● Not really relational
● Good fit for NoSQL!
Specifically, DynamoDB
● Dynamo is scaled for
reads/sec and writes/sec
per table
27. Ideal mutation flow?
HTTP POST, PUT, or DELETE
Create mutation command
Log mutation command to DynamoDB, return ID
Publish mutation ID to SQS
Receive mutation ID from SQS
Apply mutation command
Update DynamoDB with mutation outcome
HTTP GET /mutation/:id
Update client state
App
REST service
Worker
App
28. Challenges with this approach
● Our app assumes synchronous requests
● Extra SQS puts and gets introduce more
latency
● Implementing long polling after the fact is a
PITA
● Web sockets to the rescue?
29. Compromise mutation flow
HTTP POST, PUT, or DELETE
Apply mutation command
Log mutation command to DynamoDB
Return result
Update client state
App
REST service
App
30. Replaying commands
Read command from DynamoDB
Publish command to SQS
Receive mutation command from SQS
Apply mutation command
If result is different than it was the first time, fail?
Loader
Worker
31. No CQRS
HTTP POST, PUT, or DELETE
Log mutation model to DynamoDB
Perform mutation
Return result
Update client state
App
REST service
App
32. Replaying without CQRS
Read mutation model from DynamoDB
HTTP POST, PUT, or DELETE
Loader
Perform mutation
Return result
REST service
If result is different, fail? Loader
33. When event sourcing, do:
● Ensure that your mutations are idempotent
● Log enough to replay