Este foi um teste realizado a partir de um processo seletivo. O teste consiste na construção de um pequeno backend (servindo como API) para cadastro de notas fiscais com disparo de email após sua criação (através das queues).
Desenvolver uma api Rest para controle das notas fiscais dos usuários:
- Criar endpoints para cadastro e login dos usuários;
- Criar CRUD para o gerenciamento das notas fiscais:
- As api’s das notas fiscais só podem ser acessadas por usuários autenticados;
- Cada nota só pode ser acessada pelo usuário que a criou;
- A cada nota fiscal criada, disparar um email para o usuário que a criou.
- Fazer devidos retornos de response e HTTP status code;
- Criar um projeto público no github com a solução do desafio.
- PHP 8+;
- Banco de Dados: MySQL ou Mongodb;
- Framework: Laravel 10+;
- Pontos extras:
- Utilizar docker;
- Rodar pipeline no Github Actions para execução dos testes.
(Se clicar no link acima, irá diretamente para a página das pipelines que foram rodadas)
Campo (* obrig.) | Tipo | Descrição |
---|---|---|
numero* | char(9) | Código único da NF (com exatamente 9 caracteres) |
data_emissao* | date | Data de emissão da NF |
valor* | decimal(10,2) | Valor do frete (sempre maior que zero) |
cnpj_remetente* | string(14) | CNPJ da empresa remetente - Poderá enviá-lo com ou sem a máscara |
nome_remetente* | string(100) | Nome da empresa remetente |
cnpj_transportador* | string(14) | CNPJ do transportador - Poderá enviá-lo com ou sem a máscara |
nome_transportador* | string(100) | Nome da empresa de transporte |
No banco de dados resolvi utilizar o MySQL e para o processamento das filas, Redis. Ainda a respeito das filas, como envolve disparo de emails, aproveitei do Mailtip já configurado no LaravelSail. Usei também o NGINX como servidor HTTP e utilizei o PHP em sua versão 8.2. Como esperado, usei o Docker a partir do LaravelSail, que já trás um ambiente robusto e completamente configurado. Rodei isso no WSL2 com Ubuntu e o docker instalado nele, nativamente.
Após clonar o projeto em seu aparelho, basta rodar o comando do composer para instalar as dependências:
composer install
OBS: caso não tenha o composer em sua máquina, recomendo utilizar seu container docker para instalar as dependências do projeto.
Após a instalação das dependências, para rodar os comandos dentro do container, utilizaremos o comando do sail:
./vendor/bin/sail <command>
Considere criar um alias
para rodar apenas sail
na linha de comando. Pesquise a melhor forma de fazer de acordo com
seu
sistema operacional.
Em seguida, faça uma cópia do arquivo .env.example
para .env
e configure seu ambiente de desenvolvimento como
banco de dados e etc.
E lembre-se também de gerar uma chave para aplicação:
sail artisan key:generate
Finalmente, para rodar a aplicação:
sail up -d
Daqui pra frente irei considerar como domínio principal o http://localhost
.
Este é o link da documentação do Postman
Antes de iniciarmos o banco de dados, sugiro deixar a fila configurada e rodando para acompanhar seus disparos.
Deixe também o servidor local de email (Mailpit já configurado nos containers docker) aberto em alguma aba de seu navegador. Siga as instruções abaixo:
- Accesse seu servidor de email;
- Caso não tenha nenhuma configuração disponível, recomendo o uso do
Mailtip
que já vem setado por padrão no LaravelSail. Basta subir os containers (como mencionado anteriormente) e acessar a urlhttp://localhost:8025
(ou mude a porta de acesso caso tenha outra configuração).
- Caso não tenha nenhuma configuração disponível, recomendo o uso do
- Com o terminal aberto e dentro do diretório do projeto, basta usar o comando:
sail artisan queue:work
Se o projeto estiver configurado corretamente e não tiver conflito com portas, a fila estará pronta para ser executada.
Se quiser uma interface visual para acompanhar a execução das filas, poderá instalar o horizon. Na verdade ele já está instalado.
-
Caso tenha executado as queues anteriormente, finalize o processo com
Ctrl+C
; -
Instale o Horizon:
sail artisan horizon:install
- Publique seus arquivos de configuração:
sail artisan horizon:publish
- Acesse a rota
http://localhost/horizon
; - Execute as queues a partir do horizon:
sail artisan horizon
Verá na tela a execução das filas e um conjunto de relatórios e métricas a respeito das mesmas.
No banco de dados, além das tabelas padrão que o laravel já traz (incluindo usuário), criei apenas 1 tabela: invoices. Nela, inseri os dados solicitados no teste e fiz o relacionamento com a tabela de usuários.
Após criar sua base de dados local e configura-la em seu .env
, execute o comando:
sail artisan migrate --seed
Caso tenha populado o banco de dados antes de rodar as queues e não tenha visto a enxurrada de emails, basta executar:
sail artisan migrate:fresh --seed
Dessa forma você verá os emails de notificação serem enviados em grande quantidade.
- Tratamento dos dados com FormRequest (
InvoiceRequest
):- Para validar os CNPJ, criei uma
Rule
chamadaDocumentRule
com uma validação espefícica. Independente disso, o usuário poderá envia os CNPJ com ou sem máscara. O importante é que este seja válido; - Mantive os campos em inglês na base de dados, mas dei liberdade para o usuário mandar seus dados em português, tratando a request ante de sua validação.
- Para validar os CNPJ, criei uma
- Exibição dos dados em Json através das Resources:
- Como mencionado anteriormente, estou exibindo os dados em português por ter sido solicitado no enunciado,
mas internamente os campos estão com o nome em inglês. Utilizei também
Attributes
eMutations
para validar os dados na saída (ambos CNPJ e exibição do valor monetário da NF).
- Como mencionado anteriormente, estou exibindo os dados em português por ter sido solicitado no enunciado,
mas internamente os campos estão com o nome em inglês. Utilizei também
- Criação de
Observer
para a criação de uma nova nota fiscal (InvoiceObserver
);InvoiceObserver::creating
: De todos os dados necessários para a Nota Fiscal ser criada, observei que 2 deles não deveriam estar no controle do usuário:numero
edata_emissao
. Logo, após todo o processo de validação e cadastro, eu crio de forma automática o número/código da NF e atribuo a data atual como a data de emissão do documento;InvoiceObserver::created
: Após o cadastro de uma nova nota fiscal, este método executará um job para enviar uma notificação com os dados para seu dono/criador. O job é oSendInvoiceJob.php
- Para não permitir que um usuário veja, edite ou exclua uma nota fiscal que não seja dele, criei o
InvoicePolicy
para barrar qualquer uma das ações citadas anteriormente; - CRUDs construídos usando a metodologia do TDD;
- Para rodar os testes:
# Para rodar todos de uma vez
sail pest
# Para rodá-los separadamente
sail pest tests/Feature/Auth/RegisterTest.php
sail pest tests/Feature/Auth/AuthenticationTest.php
sail pest tests/Feature/InvoiceTest.php
- Endpoints/rotas da API:
POST api/auth/login ...................................................... Auth\AuthenticationController@login
GET|HEAD api/auth/logout .................................................... Auth\AuthenticationController@logout
POST api/auth/register ............................................................... Auth\RegisterController
GET|HEAD api/invoices ................................................... invoices.index › InvoiceController@index
POST api/invoices ................................................... invoices.store › InvoiceController@store
GET|HEAD api/invoices/{invoice} ........................................... invoices.show › InvoiceController@show
DELETE api/invoices/{invoice} ..................................... invoices.destroy › InvoiceController@destroy
- Não realizei o
update
das Notas Fiscais justamente por serem Notas fiscais.
Este foi um teste desafiador e também motivador. Espero ter conseguido passar um pouco da ideia que tive pra solucionar o problema e como me organizei, como estruturei o código para deixá-lo melhor manutenível, com fácil compreensão e rápida leitura.
Recomendo fortemente que dê ua olhadas nas minhas pipelines, issues e no kanban que montei pra esse projeto vinculado as Pull Requests. E, novamente, segue a documentação da API no Postman.