Crypto dot com backend test. I spent about 6 hrs finishing the project and reviewing the code. I provide a docker-compose to build a server easily and swagger document for testing.
go mod vendor
step 1 : build test mysql instance
make run.test.mysql
step 2 : run test
go test -p=1 ./internal/test/...
note: if test occur driver: bad connection
error, please run again.
run with docker-compose docker-compose install
make compose.up
run local
go mod vendor
make run.local.mysql
make migrate.local.up
make run.server env=local
note: After running server with docker-compose, you can use swagger to test api.
This project layout is adopted a idea from golang standard project layout and clean architecture
internal/domain
: Domain layer define domain-logic interfaces. It can be used to comply Inversion dependency principle.internal/app
: This package is used to manage dependency and initialize application.internal/model
: Define data structure and its behavior.internal/service
: Servicer layer implement core business logic.internal/delivery
: Handler layer which act as the presenter. It could be RESTful, graphQL or gRPC. Delivery layer will be RESTful API in this project.internal/repository
: Repository provide db operation and encapsulate database-specific logic (e.g SQL).internal/test
: Test code and factory object. The test include concurrency testing.pkg/config
: This package used to initialize configuration.build/migrations
: db schema
|users| -has-many-> |wallets| -has-many-> |transactions|
Transferring money between wallets should consider race condition when multiple requests are processed simultaneously.
The most safe way is used to FOR UPDATE
SQL to put a write lock on record. However, we can use optimistic lock to have better performance.
Therefore, I use following SQL to update wallet balance.
UPDATE wallets SET amount=amount-? WHERE amount >= ? AND serial_number = ?;
If this update statement show no rows affected, you can determine amount of this wallet is insufficient.
Considering that users might need to review their transaction, we need to add proper index on transactions
table.
The most common search scenario will be search transactions during a period, so I create two composite indexes idx_to_wallet_id_created_ati
and idx_from_wallet_id_created_ati
for this search scenario.
These indexes does not include kind
column because kind column can be multiple values in search condition.
swagger URI : http://localhost:8080/swagger/index.html
Authentication :
After creating an account, you can use /api/v1/auth
api to get your token, then put your token in Authentication
header with Bearer
schema.
POST /api/v1/signup
: create an account and wallet
POST /api/v1/auth
: get token
Wallet :
POST /api/v1/wallets
: create wallet
GET /api/v1/wallets/:serial
: get wallet and its transactions in 7dys.
GET /api/v1/wallets
: get all wallets.
Transaction :
POST /api/v1/transfer
: transfer money
POST /api/v1/deposit
: deposit money
POST /api/v1/withdraw
: withdraw money
- List wallets and transactions meanwhile solve n+1 query problem.
- Allow users to search their transactions.
- Provide delete api by implementing soft delete operation.
- Add cicd flow (e.g travis-ci, ansible).
- Add kubernetes deployment and Helm chart.
- Add redis rate limiter (e.g leaky bucket).
- Improve security with CORS, HTTPs and CSRF secret.
- Add performance monitoring mechanism (e.g Elastic APM and OpenTelemetry).
- Provide gRPC endpoints.