Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
405b36b
feat: add initial setup files
Arthuz2 Feb 20, 2026
fd3ecdd
chore: add paginator and validator bundle
Arthuz2 Feb 21, 2026
0ca00f7
chore: configure api route prefix
Arthuz2 Feb 21, 2026
9f7c933
feat: add HealthCheck endpoint
Arthuz2 Feb 21, 2026
402222c
feat: create investment and owner domains (entity, repository and mig…
Arthuz2 Feb 22, 2026
6c06dad
chore: add serializer bundle
Arthuz2 Feb 24, 2026
8dc26d4
refactor: change ID type from int to Uuid in Investment and Owner ent…
Arthuz2 Feb 24, 2026
1ee25bc
feat: add validator service for validating data in DTOs
Arthuz2 Feb 24, 2026
343d9c6
feat: add custom exception classes for investment validation
Arthuz2 Feb 24, 2026
fded925
feat(investment): implement investment creation flow
Arthuz2 Feb 24, 2026
7bae33f
feat(investment): add custom domain exceptions
Arthuz2 Feb 25, 2026
94f4218
feat(investment): add investment calculation service
Arthuz2 Feb 25, 2026
a818482
feat(investment): add get investment by id
Arthuz2 Feb 25, 2026
c69c99b
feat(investment): add custom domain exceptions
Arthuz2 Feb 25, 2026
482ad60
feat(investment): add investment tax calculation
Arthuz2 Feb 25, 2026
4510980
feat(investment): add withdraw dto with date validation
Arthuz2 Feb 25, 2026
83558fa
feat(investment): add withdraw investment flow
Arthuz2 Feb 25, 2026
a86eb65
feat(investment): add custom domain exceptions
Arthuz2 Feb 26, 2026
0770b7e
feat(owner): add method get by email
Arthuz2 Feb 26, 2026
84723be
feat(investment): add get investments by email flow
Arthuz2 Feb 26, 2026
67a79f6
chore(test): add phpunit bundle
Arthuz2 Feb 26, 2026
a117b27
feat(owner): add custom domain exceptions
Arthuz2 Feb 26, 2026
0859cf9
feat(investment): add required email verification
Arthuz2 Feb 26, 2026
d2814c1
test(investment): add tests for investment calculator service
Arthuz2 Feb 26, 2026
8df98a5
docs: add API documentation to README
Arthuz2 Feb 26, 2026
cfdf5af
docs: update README installation instructions
Arthuz2 Feb 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# editorconfig.org

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[{compose.yaml,compose.*.yaml}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
4 changes: 4 additions & 0 deletions .env.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

###> symfony/framework-bundle ###
APP_SECRET=change-me
###< symfony/framework-bundle ###
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
APP_ENV=dev
APP_SECRET=change-me
APP_SHARE_DIR=var/share
DEFAULT_URI=http://localhost

DATABASE_URL="mysql://app:app@127.0.0.1:3306/investments?serverVersion=8.0.32&charset=utf8mb4"
3 changes: 3 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# define your env variables for the test env here
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

###> symfony/framework-bundle ###
/.env
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###

###> phpunit/phpunit ###
/phpunit.xml
/.phpunit.cache/
###< phpunit/phpunit ###
220 changes: 154 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,176 @@
# Back End Test Project <img src="https://raw.githubusercontent.com/Coderockr/backend-test/refs/heads/main/coderockr.banner.svg" align="right" height="50px" />
# Investments API

You should see this challenge as an opportunity to create an application following modern development best practices (given the stack of your choice), but also feel free to use your own architecture preferences (coding standards, code organization, third-party libraries, etc). It’s perfectly fine to use vanilla code or any framework or libraries.
API REST desenvolvida em Symfony para gerenciamento de investimentos.

## Scope
O sistema permite:

In this challenge you should build an **API** for an application that stores and manages investments, it should have the following features:
- Criar um investimento
- Consultar investimento por ID
- Realizar saque de investimento
- Listar investimentos por proprietário

1. __Creation__ of an investment with an owner, a creation date and an amount.
1. The creation date of an investment can be today or a date in the past.
2. An investment should not be or become negative.
2. __View__ of an investment with its initial amount and expected balance.
1. Expected balance should be the sum of the invested amount and the [gains][].
2. If an investment was already withdrawn then the balance must reflect the gains of that investment
3. __Withdrawal__ of a investment.
1. The withdraw will always be the sum of the initial amount and its gains,
partial withdrawn is not supported.
2. The withdrawal date must be informmed by the user, and it can be a date in the past or today, but can't happen before the investment creation or the future.
3. [Taxes][taxes] need to be applied to the withdrawals before showing the final value.
4. __List__ of a person's investments
1. This list should have pagination.
---

__NOTE:__ the implementation of an interface will not be evaluated, and if implemented the endpoints still need to be usable/readable for a machine.
## Tecnologias Utilizadas

### Gain Calculation
- PHP 8+
- Symfony
- Doctrine ORM
- MySQL
- Docker
- PHPUnit
- KNP Paginator

The investment will pay 0.52% every month in the same day of the investment creation.
---

Given that the gain is paid every month, it should be treated as [compound gain][], which means that every new period (month) the amount gained will become part of the investment balance for the next payment.
## Instalação

### Taxation
Clone o repositório:

When money is withdrawn, tax is triggered. Taxes apply only to the gain portion of the money withdrawn. For example, if the initial investment was 1000.00, the current balance is 1200.00, then the taxes will be applied to the 200.00.
```sh
git clone <repo-url>
cd backend-test
```

The tax percentage changes according to the age of the investment:
* If it is less than one year old, the percentage will be 22.5% (tax = 45.00).
* If it is between one and two years old, the percentage will be 18.5% (tax = 37.00).
* If older than two years, the percentage will be 15% (tax = 30.00).
Mude para a branch development:

## Requirements
1. Create project using any technology of your preference. It’s perfectly OK to use vanilla code or any framework or libraries;
2. Although you can use as many dependencies as you want, you should manage them wisely;
3. It is not necessary to send the notification emails, however, the code required for that would be welcome;
4. The API must be documented in some way.
```sh
git checkout development
```

## Deliverables
The project source code and dependencies should be made available in GitHub. Here are the steps you should follow:
1. Fork this repository to your GitHub account (create an account if you don't have one, you will need it working with us).
2. Create a "development" branch and commit the code to it. Do not push the code to the main branch.
3. Include a README file that describes:
- Special build instructions, if any
- List of third-party libraries used and short description of why/how they were used
- A link to the API documentation.
4. Once the work is complete, create a pull request from "development" into "main" and send us the link.
5. Avoid using huge commits hiding your progress. Feel free to work on a branch and use `git rebase` to adjust your commits before submitting the final version.
Crie o arquivo `.env`:

## Coding Standards
When working on the project be as clean and consistent as possible.
```sh
cp .env.example .env
```

## Project Deadline
Ideally you'd finish the test project in 5 days. It shouldn't take you longer than a entire week.
Configure o arquivo `.env` com suas credenciais de banco de dados.

## Quality Assurance
Use the following checklist to ensure high quality of the project.
Instale as dependências:

### General
- First of all, the application should run without errors.
- Are all requirements set above met?
- Is coding style consistent?
- The API is well documented?
- The API has unit tests?
```sh
composer install
```

## Submission
1. A link to the Github repository.
2. Briefly describe how you decided on the tools that you used.
Suba o container:
```sh
docker compose up -d
```

## Have Fun Coding 🤘
- This challenge description is intentionally vague in some aspects, but if you need assistance feel free to ask for help.
- If any of the seems out of your current level, you may skip it, but remember to tell us about it in the pull request.
A depender da configuração da sua máquina o container docker pode demorar 10-20 segundos pra ficar pronto.

## Credits
Execute as migrations:

This coding challenge was inspired on [kinvoapp/kinvo-back-end-test](https://github.com/kinvoapp/kinvo-back-end-test/blob/2f17d713de739e309d17a1a74a82c3fd0e66d128/README.md)
```sh
php bin/console doctrine:migrations:migrate
```

[gains]: #gain-calculation
[taxes]: #taxation
[interest]: #interest-calculation
[compound gain]: https://www.investopedia.com/terms/g/gain.asp
Inicie o servidor:

```sh
symfony server:start
```

ou

```sh
php -S localhost:8000 -t public
```

A API estará disponível em:

http://localhost:8000

---

## Endpoints

### Criar investimento

POST /investments

Body:

```json
{
"ownerEmail": "user@email.com",
"investedValue": 1000,
"creationDate": "2024-01-01"
}
```

---

### Buscar investimento por ID

GET /investments/{id}

---

### Realizar saque

POST /investments/{id}/withdraw

Body:

```json
{
"withdrawDate": "2024-12-01"
}
```

---

### Listar investimentos por proprietário

GET /investments?ownerEmail=user@email.com&page=1&limit=10

Query Params:

- ownerEmail (obrigatório)
- page (opcional, padrão 1)
- limit (opcional, padrão 10, máximo 50)

---

## Postman Collection

O arquivo `postman_collection.json` está disponível na raiz do projeto.

Para utilizar:

1. Abra o Postman
2. Clique em "Import"
3. Selecione o arquivo `postman_collection.json`

A collection contém todos os endpoints com exemplos prontos.

---

## Testes

Testes unitários foram implementados para a camada de regra de negócio:

- InvestmentCalculatorService::calculateMonths
- InvestmentCalculatorService::calculateBalance
- InvestmentCalculatorService::calculateTax

Para executar os testes:

```sh
php bin/phpunit
```

---

## Decisões Técnicas

- Uso de DTO para entrada de dados
- Validação centralizada via DTOValidatorService
- UUID como identificador de investimento
- Paginação implementada com KNP Paginator
- Regra de cálculo isolada em InvestmentCalculatorService para facilitar testes

---

### Desenvolvido por Arthur Porcino
21 changes: 21 additions & 0 deletions bin/console
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php

use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;

if (!is_dir(dirname(__DIR__).'/vendor')) {
throw new LogicException('Dependencies are missing. Try running "composer install".');
}

if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
}

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

return function (array $context) {
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);

return new Application($kernel);
};
4 changes: 4 additions & 0 deletions bin/phpunit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env php
<?php

require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit';
7 changes: 7 additions & 0 deletions compose.override.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

services:
###> doctrine/doctrine-bundle ###
database:
ports:
- "5432"
###< doctrine/doctrine-bundle ###
18 changes: 18 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
services:
###> doctrine/doctrine-bundle ###
database:
image: mysql:8.0
environment:
MYSQL_DATABASE: investments
MYSQL_USER: app
MYSQL_PASSWORD: app
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
volumes:
- database_data:/var/lib/mysql

volumes:
###> doctrine/doctrine-bundle ###
database_data:
###< doctrine/doctrine-bundle ###
Loading