https://github.com/luissimas/desafio-liven
Desafio do processo seletivo da Liven.
https://github.com/luissimas/desafio-liven
Last synced: 4 months ago
JSON representation
Desafio do processo seletivo da Liven.
- Host: GitHub
- URL: https://github.com/luissimas/desafio-liven
- Owner: luissimas
- Created: 2022-02-03T14:55:36.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2022-02-09T13:14:17.000Z (over 3 years ago)
- Last Synced: 2025-01-02T07:43:11.895Z (6 months ago)
- Language: TypeScript
- Size: 601 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.org
Awesome Lists containing this project
README
#+title:Treinamento Liven
#+author: Luís Augusto Simas* Enunciado
O sistema lida com duas entidades: *usuários* e *endereços*, que mantém um relacionamento do tipo =1:N=, ou seja, um usuário pode ter vários endereços, mas os endereços estão relacionados à apenas um usuário.As operações requeridas são *CRUDs* para ambas as entidades:
- Criação de usuários
- Listagem de usuários e seus endereços
- Alteração de usuários
- Remoção de usuários
- Criação de endereços
- Listagem endereços do usuário
- Alteração de endereços
- Remoção de endereçosComo especificado, as operações devem ser fornecidas através de um servidor HTTP, portanto optou-se por expor endpoints seguindo os *princípios REST* (embora a API não seja Restful!). Isso envolve o uso semântico dos verbos HTTP e a definição de *endpoints orientados a recursos*.
Além disso, as rotas de listagem devem permitir a especificação de *parâmetros de filtragem* através de querystrings nas rotas.
* Processo de desenvolvimento
** Ambiente
Foram utilizadas as ferramentas descritas no enunciado, com destaque para as seguintes:- Typescript
- Node.js
- Express
- Mysql
- Knex.js ♥️ Objection.jsAlém dessas, foram utilizadas algumas outras ferramentas para auxiliar o desenvolvimento ou adicionar funcionalidades extras do sistema.
** Arquitetura
Buscou-se por implementar o sistema seguindo o padrão da *clean architecture* (porém não aderindo 100% a ele). Isso tem algumas implicações:- Definição de camadas do sistema e suas responsabilidades.
- Desacoplamento entre as camadas.
- Desacoplamento entre regras de negócio e implementações externas.#+attr_org: :width 800
[[file:presentation/clean.png]]** Testes
Ao longo do desenvolvimento foram realizados testes unitários usando a biblioteca /Jest/. Os testes foram aplicados principalmente na camada de *domínio* e *casos de uso*, auxiliando tanto na *especificação* quanto na validação das funcionalidades mais importantes da aplicação.Para *cada entidade e caso de uso* há ao menos um *conjunto de testes* que validam suas funcionalidades.
** Extras
*** Validação de requisições
Para validar os dados enviados nas requisições à API, foram utilizadas duas bibliotecas:- *JOI* para a definição e validação de schemas.
- *Celebrate* para a criação de middlewares de validação.*** Error handling
O tratamento de erros é feito de forma *centralizada* na aplicação, fazendo uso da função =next()= do express em conjunto com um /error handler/ customizado implementado como um *middleware*.**** Definição de erros
Os erros são definidos na camada de *domínio*, portanto não contém nenhuma informação externa às regras de negócio da aplicação.#+begin_src typescript
export abstract class BaseError extends Error {
public readonly details?: stringconstructor(message: string, details?: string) {
super(message)
this.details = details
}
}
#+end_srcDessa classe base são derivadas as classes de erro específicas:
#+begin_src typescript
export class EntityNotFound extends BaseError {
constructor(public readonly entity: string, public readonly details?: string) {
super(`${entity} not found.`, details)
}
}
#+end_src**** Error handler
O papel do /error handler/ é *identificar os errors* gerados em outras camadas da aplicação, *atribuir* a eles um *status HTTP* e então *enviar uma resposta* com o máximo de informação possível sobre o erro.#+begin_src typescript
type StatusAssoc = {
[key: string]: number
}const httpStatus: StatusAssoc = {
UserAlreadyExists: 400,
EntityNotFound: 404,
InvalidFieldError: 400,
}export async function handler(error: BaseError, _req: Request, res: Response, _next: NextFunction): Promise {
const { message, details } = error
const name = error.constructor.name
const status = httpStatus[name] || 500if (message) {
return res.status(status).json({ error: message, details: details })
}return res.status(status).json({ 'Internal error': error.message })
}
#+end_src*** Envio de e-mails
Foi implementado também o envio de e-mails no momento do *cadastro do usuário*. A implementação atual faz uso da biblioteca /nodemailer/ para o envio dos e-mails.*** Documentação
A documentação da API foi feita seguindo os padrões da /OpenAPI Specification/, utilizando a ferramenta /Swagger/ como interface. A documentação é extraída das rotas através de /JSDocs/ pela biblioteca /swagger-jsdoc/ e servida em um endpoint =/docs= através da biblioteca /swagger-ui-express/.* Fim
Muito obrigado!