- Turbo to setup monorepo
- Nest.JS framework
- Node v20.9.0
- Postgres DB + TypeORM
- Redis for caching and locking
- RabbitMQ as messaging broker
- Github Actions CI
# Use node v20.9.0
nvm use
# Install all packages
npm install
# Start project
# (first start may take few minutes, because of docker build)
make up
Product app handles all comunication with database and with client.
On created/updated/deleted review send message to review-processing
queue.
At first I wanted all communication through RabbitMQ to go through exchange and only if there is exchange-queue binding, the message would get to consumer. But that took much time because amqp microservice setup is not perfect in NestJS. So I chose to use queues directly
There is also consumer on product
queue where the app listens for ranking average.updated
event
Received average updates are stored in redis and ony updated in DB once every minute (could be setup by env, currently hardcoded). Db update is done in transaction
API docs are available on http://localhost:3002/api with started project app. Documentation is done with Swagger. All endpoints can be tried on that link for testing.
For local development it's setup in two instances.
Handles all calculations related to rating average. The main idea is that for each product we will store review count and rating sum in redis. Then on different events we will modify these values:
- created -> count +=1, sum += rating
- updated -> sum += (newrating - oldrating)
- deleted -> count -=1, sum -= rating
That way we can always calculate current rating average and we don't need to cache all reviews. All we need are the sums and counts. Also, in case its the first review(or after long this) we always fetch current count from product
There is implemented redis lock by productId. So we always process only one event for that product and don't run into any race conditions
It should be possible to use Lua script directly in redis, instead of two separate redis call. If used, we would not need redis locks, as the update would happen atomically
- Tests - only E2E tests for
/products
endpoints. Rest wasn't done because of time constraints - Security - endpoints are public. Should accept some access_token or api key. Not done, because of of scope
- Shared package for shared types
- Pagination for reviews list. Currently only done for
GET /products
- Caching of GET endpoints in Redis. Currently cache is used only for rating average calculations. But should be easy to add by redis and invalidate on update/delete
- Use Lua script in review-processing