Skip to content

gavrylenkoIvan/balance-service

Repository files navigation

codecov

Test task avitoTech

Content

  1. Task description
  2. Implementation
  3. Endpoints
  4. Starting
  5. Testing
  6. Examples

Task description

Develop a microservice for working with users' balance (balance, crediting / debiting / transferring funds). The service must provide an HTTP API and accept/return requests/responses in JSON format. Additionally, implement methods for converting the balance and obtaining a list of transactions. Full description in TASK.

Implementation

  • Following the REST API design.
  • Clean architecture and dependency injection
  • Working with framework labstack/echo.
  • Working with Postgres using sqlx and writing SQL queries.
  • App configuration with viper library.
  • Launching with Docker.
  • Unit/Integration testing using mocks testify, sqlmock, gomock.

Project structure:

.
├── internal  // business logic
│   ├── handler     
│   ├── service     
│   └── repository  
├── cmd    
├── pkg       // Importable code (logging and utils) 
│   ├── utils     
│   └── logging           
├── schema    // SQL migrations files
├── configs   // App configs
├── models    // Custom types
├── scripts   // Shell scripts
├── docs      // Swagger documentation

Endpoints

  • GET /balance/{user_id} - get user`s balance
    • Path variables:
      • user_id - unique user`s id.
    • Query params:
      • currency - convert user`s balance to currency (EUR by default).
  • GET /transactions/{user_id} - get user`s transactions
    • Path variables:
      • user_id - unique user`s id.
    • Query params:
      • page
      • limit - number of transactions per page
      • sort
  • POST /top-up/{user_id} - replenishment of the user's balance
    • Path variables:
      • user_id - unique user`s id,
    • Request body:
      • amount - replenishment amount in EUR.
  • POST /debit/{user_id} - write-off from the user's balance
    • Path variables:
      • user_id - unique user`s id,
    • Request body:
      • amount - replenishment amount in EUR.
  • POST /transfer/ - transferring funds to the balance of another user
    • Path variables:
      • user_id - unique user`s id,
    • Request body:
      • to_id - id of the user whose balance the funds are credited to,
      • amount - transfer amount in EUR.

Starting

Build docker-compose:

make compose-build

Start container:

make compose-up

Testing

To run tests, use:

make test

Examples

1. GET /balance for user_id=1

Request:

$ curl --location --request GET 'localhost:8080/balance/1' \
--header 'Content-Type: application/json'

Response body:

{
    "user_id": 1,
    "balance": 4.13
}

2. GET /balance for user_id=1 and currency=USD

Request:

$ curl --location --request GET 'localhost:8080/balance/1?currency=UAH' \
--header 'Content-Type: application/json'

Response body:

{
    "user_id": 1,
    "balance": 165.43
}

3. GET /transactions for user_id=1, page=1, limit=1, sort=date

Request:

$ curl --location --request GET 'localhost:8080/transactions/1?page=1&limit=1&sort=date' \
--header 'Content-Type: application/json'

Response body:

[
   {
        "id": 1,
        "user_id": 1,
        "amount": 30,
        "operation": "",
        "date": "2023-06-14 02:19:40"
   }
]

4. GET /transactions for user_id=2, page=1, limit=1, sort=date

$ curl --location --request GET 'localhost:8080/transactions/2?page=1&limit=2&sort=date' \
--header 'Content-Type: application/json'

Response body:

[
    {
        "id": 2,
        "user_id": 2,
        "amount": 101,
        "operation": "",
        "date": "2023-06-14 02:19:40"
    },
    {
        "id": 3,
        "user_id": 2,
        "amount": 32,
        "operation": "",
        "date": "2023-06-14 02:19:40"
    }
]

5. POST /top-up for user_id=1, amount=1000

Request:

$ curl --location --request POST 'localhost:8080/top-up' \
--header 'Content-Type: application/json' \
--data-raw '{
    "user_id":1,
    "amount":1000
}'

Response body:

{
    "user_id": 1,
    "balance": 1004.13
}

6. POST /debit for user_id=1, amount=1000

Request:

$ curl --location --request POST 'localhost:8080/debit' \
--header 'Content-Type: application/json' \
--data-raw '{
    "user_id":1,
    "amount":1000
}'

Response body:

{
    "user_id": 1,
    "balance": 4.13
}

But if you try to do it again, there will no enough money to perform debit:

{
    "message": "not enough money to perform purchase"
}

7. POST /transfer for user_id=1, to_id=2, amount=1000

Request:

$ curl --location --request POST 'localhost:8080/transfer' \
--header 'Content-Type: application/json' \
--data-raw '{
    "user_id":1,
    "to_id":2,
    "amount":1
}'

Response body:

{
    "user_id": 2,
    "balance": 33
}