Skip to content

MateusBertazzo/test-tecnico-java-spring-boot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

65 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Desafio Técnico versão Java

 Documentação
Breve explicação sobre minhas decisões:

Arquitetura:

 Por se tratar de uma pequena API e um projeto pequeno, resolvi usar arquitetura de camadas
 pela velocidade de desenvolvimento e organização de fácil entendimento.

Cardialidade:

 - Um CarEntity está relacionado a um único ModelCarEntity.
 - Um ModelCarEntity pode estar relacionado a vários CarEntity.
 
 Ou seja um MODELO pode ter vários Carros.

 - Um ModelCarEntity está relacionado a uma única BrandEntity.
 - Uma BrandEntity pode estar relacionada a vários ModelCarEntity.

 Ou seja uma MARCA pode ter vários MODELOS.

 Logo, para evitar bugs e manter a integridade do banco, decidi que
 no momento em que registro um carro, automaticamente preciso também
 registrar um Modelo que por sua vez precisa de uma Marca. Não existe
 um Carro sem um modelo, como também não existe um Modelo sem uma Marca,
 não faz sentido cadastrar-los separadamente.

Método de resposta para o cliente:

 Optei por implementar a classe ApiResponseService, que basicamente serve para padronizar a resposta ao cliente,
 facilitando a manipulação dos dados retornados pela API, padronizando todos os EndPoints.
Banco de dados

Modelo de tabelas

Rota de Carros

http://localhost:8080/api/car

POST /register:

🔍 Formato/exemplo de request e resposta

Essa rota precisa de um @RequestBody (CarAndModelAndBrandDto) /register exemplo:

Note que: Ao cadastrar um carro, automaticamente um Modelo e uma Marca também são cadastrados para manter integridade do Banco
{
  "nameBrand": "Bugati",
  "modelName": "fusca",
  "year": 2024,
  "fuel": "Gasolina",
  "numDoors": 2,
  "color": "White",
  "priceFip": 50.000
}

Exemplo de resposta em caso de sucesso - 201 - CREATED:

{
    "success": true,
    "message": "Carro registrado com sucesso",
    "response": null
}

Exemplo de resposta em caso de input de usuário inválido - 400 - BAD REQUEST:

{
    "timestamp": "2024-04-07T13:32:02.004+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/api/car/register"
}

Exemplo de resposta em caso de erro interno - 500 - SERVER INTERNAL ERROR:

{
    "success": false,
    "message": "Erro ao registrar o carro: Mensagem de erro",
    "response": null
}

UPDATE /update/{id}:

🔍 Formato/exemplo de request e resposta

Está rota recebe um @PathVariable(id) e @RequestBody(CarAndModelAndBrandDto) /update/{id}:

{
  "year": 2023,
  "fuel": "Alcool",
  "numDoors": 2,
  "color": "Preto"
}

Exemplo de resposta em caso de sucesso - 200 - OK:

{
    "success": true,
    "message": "Carro atualizado com sucesso",
    "response": null
}

Exemplo de resposta em caso de input de usuário inválido - 400 - BAD REQUEST:

{
    "timestamp": "2024-04-07T14:01:06.216+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/api/car/update/1"
}

Exemplo de resposta em caso de ServerInternelError - 500 - SERVER INTERNAL ERROR:

{
    "success": false,
    "message": "Erro ao atualizar o carro: Mensagem de erro",
    "response": null
}

GET /get-all:

🔍 Formato/exemplo de request e resposta

Exemplo de resposta:

{
    "success": true,
    "message": "Carros retornados com sucesso!",
    "response": [
        {
            "id": 1,
            "year": 2024,
            "fuel": "Alcool",
            "color": "Preto",
            "numDoors": 2,
            "priceFip": 50.000,
            "modelName": "Gol",
            "modelId": 1,
            "timestampRegister": 1712424660
        },
        {
            "id": 2,
            "year": 2024,
            "fuel": "Gasolina",
            "color": "Preto",
            "numDoors": 2,
            "priceFip": 50.000,
            "modelName": "Gol",
            "modelId": 1,
            "timestampRegister": 1712424708
        },
    ]
}

Exemplo de resposta em caso de ServerInternelError - 500 - SERVER INTERNAL ERROR:

{
    "success": false,
    "message": "Erro ao buscar os carros: Mensagem de erro",
    "response": null
}

DELETE /delete/{id}:

🔍 Formato/exemplo de requisição e resposta

Está rota recebe um @PathVariable(id) /delete/{id}:

Exemplo de resposta em caso de sucesso - 200 - Ok:

{
    "success": true,
    "message": "Carro deletado com sucesso",
    "response": null
}

Exemplo de resposta em caso de erro - 500 - ServerInternalError:

{
    "success": false,
    "message": "Erro ao deletar o carro: Ocorreu um erro ao processar a operação com o carro.",
    "response": null
}

Rota de Modelos

http://localhost:8080/api/model-car

UPDATE /update/{id}:

🔍 Formato/exemplo de request e resposta

Está rota recebe um @PathVariable(id) e @RequestBody(ModelDto) /update/{id}:

{
    "modelName": "Corsa",
    "priceFip": 15.000
}

Exemplo de resposta em caso de sucesso - 200 - OK:

{
    "success": true,
    "message": "Modelo atualizado com sucesso",
    "response": null
}

Exemplo de resposta em caso de input de usuário inválido - 400 - BAD REQUEST:

{
    "timestamp": "2024-04-07T14:01:06.216+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/api/car/update/1"
}

Exemplo de resposta em caso de erro interno - 500 - SERVER INTERNAL ERROR:

{
    "success": false,
    "message": "Erro ao atualizar o modelo: Mensagem de erro",
    "response": null
}

GET /get-all:

🔍 Formato/exemplo de request e resposta

Exemplo de resposta:

{
    "success": true,
    "message": "Carros retornados com sucesso!",
    "response": [
        {
            "id": 1,
            "year": 2024,
            "fuel": "Alcool",
            "color": "Preto",
            "numDoors": 2,
            "priceFip": 50.000,
            "modelName": "Gol",
            "modelId": 1,
            "timestampRegister": 1712424660
        },
        {
            "id": 2,
            "year": 2024,
            "fuel": "Gasolina",
            "color": "Preto",
            "numDoors": 2,
            "priceFip": 50.000,
            "modelName": "Gol",
            "modelId": 1,
            "timestampRegister": 1712424708
        },
    ]
}

Exemplo de resposta em caso de ServerInternelError - 500 - SERVER INTERNAL ERROR:

{
    "success": false,
    "message": "Erro ao buscar os carros: Mensagem de erro",
    "response": null
}

DELETE /delete/{id}:

🔍 Formato/exemplo de requisição e resposta

Está rota recebe um @PathVariable(id) /delete/{id}:

Exemplo de resposta em caso de sucesso - 200 - Ok:

{
    "success": true,
    "message": "Carro deletado com sucesso",
    "response": null
}

Exemplo de resposta em caso de erro - 500 - ServerInternalError:

{
    "success": false,
    "message": "Erro ao deletar o carro: Mensagem de erro.",
    "response": null
}

Rota de Marcas

http://localhost:8080/api/brand

UPDATE /update/{id}:

🔍 Formato/exemplo de request e resposta

Está rota recebe um @PathVariable(id) e @RequestBody(BrandRequestDto) /update/{id}:

{
    "nameBrand": "Ferrari"
}

Exemplo de resposta em caso de sucesso - 200 - OK:

{
    "success": true,
    "message": "Marca atualizada com sucesso",
    "response": null
}

Exemplo de resposta em caso de input de usuário inválido - 400 - BAD REQUEST:

{
    "timestamp": "2024-04-07T14:01:06.216+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/api/car/update/1"
}

Exemplo de resposta em caso de ServerInternelError - 500 - SERVER INTERNAL ERROR:

{
    "success": false,
    "message": "Erro ao atualizar marca: Mensagem de erro",
    "response": null
}

GET /get-all:

🔍 Formato/exemplo de request e resposta

Exemplo de resposta:

{
    "success": true,
    "message": "Marcas encontradas com sucesso",
    "response": [
        {
            "id": 1,
            "deleted": false,
            "nameBrand": "Ferrari"
        },
        {
            "id": 2,
            "deleted": false,
            "nameBrand": "Tesla"
        },
        {
            "id": 3,
            "deleted": false,
            "nameBrand": "Mustang"
        }
    ]
}

Exemplo de resposta em caso de ServerInternelError - 500 - SERVER INTERNAL ERROR:

{
    "success": false,
    "message": "Erro ao buscar marcas: Mensagem de erro",
    "response": null
}

DELETE /delete/{id}:

🔍 Formato/exemplo de requisição e resposta

Está rota recebe um @PathVariable(id) /delete/{id}:

Exemplo de resposta em caso de sucesso - 200 - Ok:

{
    "success": true,
    "message": "Marca deletada com sucesso",
    "response": null
}

Exemplo de resposta em caso de erro - 500 - ServerInternalError:

{
    "success": false,
    "message": "Erro ao deletar marca: Mensagem de erro.",
    "response": null
}

Link Swagger configurado pra 8080

http://localhost:8080/swagger-ui/index.html#/

Docker e Docker compose

Para subir a aplicação com todas as suas dependências e o banco mysql, basta rodar o seguinte comando:

docker-compose up --build

Docker Run

Versão em Kotlin

Branch:

feat-versão-api-em-kotlin

image

Tecnologias

  • Java 17;
  • Kotlin
  • Spring Boot;
  • Spring Validation;
  • JPA
  • Hibernate
  • MySQL;
  • Swagger;
  • Docker
  • Docker compose