Build REST API with CQRS (Command Query Responsibility Segregation) architecture to store key-value in local memory cache. This is a sample project using CQRS design.
Requirements:
- Access data as normal cache (set and get methods)
- Throttle request per ip-address or username
- Persistence data
Normally we're easy to catch CQS (Command Query Seperation) than CQRS. CQS puts commands and queries in different methods within a type otherwhile CQRS puts commands and queries on different objects. In this project, I separate data into 2 objects:
- Raw Data: using to update cache
- Aggregate Data: using to get the latest cache state (Ex: number of requests by ip-address)
- POST /cache/add
- POST /cache/get
- POST /cache/remove
- GET /cache/peek
- POST /cache/take
Get how many request send to cache in interval time by ip-address. Interval time is configured by rate-schedule
in application.conf
- GET /cache/rate?ipAddress=
- GET /cache/rate-report
Body format:
{
"key" : string,
"value" : string
}
For example:
{
"key":"01234567-9abc-def0-1124-56789abc1004",
"value":"1234"
}
- Play framework 2.6
- Akka 2.5.6 (Persistent Actor)
Architecture
- Using CQRS
add/remove/take/peek ┌────────────────┐ ┌──────────────────┐
┌───▶ │ CommandService │ ─────▶│ RawInMemoryActor │
│ └────────────────┘ └──────────────────┘
│ |
┌─────────────────┐ │ | forward
──▶ │ CacheController │──▶│ └--------------------------┐
└─────────────────┘ │ ▼
│ ┌──────────────┐ ┌────────────────────────┐
└───▶ │ QueryService │ ──────▶ │ AggregateInMemoryActor │
rate/rate-report └──────────────┘ └────────────────────────┘
Data structure
- The data structure in the project based on MRU (Most Recently Used) cache and used the LinkedMap to implement the key-value memory cache. LinkedMap keeps track of the order in which each element is added, so the complexity of Add/Remove/Peek/Take is O(c) (constant time).
- Using PersistentActor to store persistent data in Database.
- Start docker
docker-compose up -d
- Start application
sbt run