Create a sample application set which conforms the following requirements (the following paragraph in the Russian language):
Wallet - кошельки(балансы), Game - написать аналог игры: ставки, таймер, работа с балансом(успешные ставки/победа, так же эмулировать ошибки и отработать их чтобы деньги пользователя не потерялись) example
This project build on Turborepo.
This turborepo uses NPM as a package manager. It includes the following apps/packages:
- gw-web: a NextJs Bff gateway facade application for Web clients
- game: a sample NestJs app which performs a gameplay
- user: a NestJs app which manages user accounts
- wallet: a NestJs app which manages wallet accounts
@lib/db
: a Prisma based NextJs component that intended to operate over the database@lib/grpc
: a gRPC common component containing all the protobuf files and dynamic types and event code, generated from those protobuf files@lib/queue
: a queue management component@lib/utils
: set of common utilseslint-config-custom
:eslint
andprettier
configurationstsconfig
:tsconfig.json
s used throughout the monorepo
📝 Each app/package is 100% TypeScript.
This turborepo has some additional tools already setup for you:
- TypeScript for static type checking
- ESLint for code linting
- Prettier for code formatting
This code is provided as a PoC and it could be improved. For instance:
- add auth service, that would be used asymmetric keys as a secret; cover endpoints by authorisation
- add tests for real business cases
- better exception handling, including creation of business level exceptions and map them on appropriated layers
- use some accounting library for financial operations
- replace startAt along with finishAt to Postgres' range, add index to it, utilitize @lib/utils/data-time helper to build ranges within the codebase
- separate final game action instant from finishAt marker, do it slightly earlier
- turn each of gRpc methods which change state to idempotent methods
- add some correlation property (e.g. transactionId) and append it to each of message involve in a financial flow (or even to each at all)
- get rid of DRY principle violation in several places on codebase
- return null for optional properties within some DTOs
- use Temporal for a process orchestration
This chapter contains minor notice regarding to approaches.
The microservices interact with each other using a direct gRpc call as a primary transport layer. However, there is an additional interaction method based on the BullMQ queues. As you can notice, there is a lot of code related to gRpc. But don't be afraid, most of that code is auto-generated, since the code in the repository is based on the code-first approach. How to build types from protobuf files?
An gRpc service method is idempotent if an identical request can be made once or several times in a row with the same effect while leaving the server in the same state. In other words, an idempotent method should not have any side-effects (except for keeping statistics).
Useful links:
The Open API document is available by relative path <HOST:WEB_GATEWAY_SERVICE_HTTP_PORT>/docs
(e.g. localhost:3000/docs
).
Please read this section carefully.
NODE_ENV
: (optional)DATABASE_URL
: Services use it for connecting to a databaseREDIS_HOST
: The game service use Redis to manage task queue; host to connectREDIS_PORT
: The game service use Redis to manage task queue; port to connectGAME_SERVICE_GRPC_URL
: Url for connecting to game service through the gRpc transportUSER_SERVICE_GRPC_URL
: Url for connecting to user service through the gRpc transportWALLET_SERVICE_GRPC_URL
: Url for connecting to wallet service through the gRpc transportWEB_GATEWAY_SERVICE_HTTP_PORT
: Port that used by gw-web service to handle client conntecions
📝 Please take a look at provided env.example file located at the project root level.
To make sure everithing works fine, copy .env.example file into .env.local
and adjust settings inside according section above. This step doesn't required for run applications locally, since in that case the dev.env
file would be used.
To first-time initialize the repository, run the following commands:
npm i
npm run build
It installs dependencies and build all components.
📝 Make sure no local Hardhat node is running.
npm run test
📝 Make sure nodejs and npm are installed.
It needs that Postgres and Redis instances accept connections. This repository has a docker-compose file for easily run Postgres and Redis locally.
cd docker
docker-compose up --detach
It also needs to migrate database state. For a development purpose the database seeding also supported.
npm -w @lib/db run db:migrate
The following step is optional (it will seed the database):
npm -w @lib/db run db:seed
To run all applications and services at-once, type:
npm run dev
or, to precisely run only the client application, run the following command:
npm -w gw-web run dev
📝 This repository includes Postman documents that could be imported and used for calling to the API.
To add or update a dependency, add -w parameter with a corresponding name-space. Do not use the NPM in the former manner (without the name-space specifying):
npm -w <namespace> ...<rest_args>
In case of weird compilation errors, it could be helpful to clean and rebuild apps/packages:
npm run clean
Turborepo can use a technique known as Remote Caching to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.
By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can create one, then enter the following commands:
npx turbo login
This will authenticate the Turborepo CLI with your Vercel account.
Next, you can link this repo to your Remote Cache by running the following command from the root of your turborepo:
npx turbo link
When you use turbo with tools that inline environment variables at build time (e.g. Next.js or Create React App), it is important to tell turbo about it. Otherwise, you could ship a cached build with the wrong environment variables! Details
The cache strategy depends on APP_ENV
and it is configured in turbo.json
Learn more about the power of Turborepo: