From ac7580c08ed13d7d4aad798062fc89497aa88793 Mon Sep 17 00:00:00 2001 From: Luis Date: Sat, 9 Aug 2025 19:04:59 -0300 Subject: [PATCH 1/6] =?UTF-8?q?Adicionando=20documenta=C3=A7=C3=A3o=20de?= =?UTF-8?q?=20arquitetura=20adotada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentacao/arquitetura.md | 17 +++++++++++++++++ documentacao/image.png | Bin 0 -> 59882 bytes 2 files changed, 17 insertions(+) create mode 100644 documentacao/arquitetura.md create mode 100644 documentacao/image.png diff --git a/documentacao/arquitetura.md b/documentacao/arquitetura.md new file mode 100644 index 00000000..6caf221f --- /dev/null +++ b/documentacao/arquitetura.md @@ -0,0 +1,17 @@ +# 1. Arquitetura adotada + +O sistema adota uma arquitetura **Cliente-Servidor** com as seguintes características: + +## 1.1 Backend Monolítico com Estrutura em Camadas + +- O backend é construído com **Node.js** e **Express**. Utiliza o ORM **Prisma** para acesso ao banco de dados **PostgreSQL**. A autenticação é feita via **JWT**, e o upload de imagens utiliza o **Multer**. + +- O backend é organizado em camadas para separar responsabilidades, como a camada de Roteamento, Middlewares (para autenticação) e Acesso a Dados (abstraído pelo Prisma). + +## 1.2 Frontend Desacoplado + +- O frontend está em `frontend` e é desenvolvido com **Next.js** (React). Ele consome as APIs do backend via chamadas HTTP usando **Axios**. Para validação de formulários, utiliza **Zod**. O frontend é desacoplado do backend, rodando em seu próprio container Docker e se comunicando apenas por API REST. As páginas e componentes estão organizados em `frontend/src/app`. +Essa separação de contêineres entre o frontend e o backend é orquestrada pelo `docker-compose.yml`. + +# 2. +![diagrama](image.png) \ No newline at end of file diff --git a/documentacao/image.png b/documentacao/image.png new file mode 100644 index 0000000000000000000000000000000000000000..52d61aba5b70913ac899795030a11498f06e57ba GIT binary patch literal 59882 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 ADgXcg literal 0 HcmV?d00001 From 9d76e27a5f97e0c7f6f98b2d65b2567ab6860923 Mon Sep 17 00:00:00 2001 From: luisfilipemendesnogueira Date: Sat, 9 Aug 2025 19:22:37 -0300 Subject: [PATCH 2/6] =?UTF-8?q?Adicionando=20documenta=C3=A7=C3=A3o=20de?= =?UTF-8?q?=20arquitetura=20adotada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentacao/arquitetura.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentacao/arquitetura.md b/documentacao/arquitetura.md index 6caf221f..7f3ddc7d 100644 --- a/documentacao/arquitetura.md +++ b/documentacao/arquitetura.md @@ -4,14 +4,14 @@ O sistema adota uma arquitetura **Cliente-Servidor** com as seguintes caracterí ## 1.1 Backend Monolítico com Estrutura em Camadas -- O backend é construído com **Node.js** e **Express**. Utiliza o ORM **Prisma** para acesso ao banco de dados **PostgreSQL**. A autenticação é feita via **JWT**, e o upload de imagens utiliza o **Multer**. +- O backend é construído com **Node.js** e **Express**. Utiliza o ORM **Prisma** para acesso ao banco de dados **PostgreSQL**. A autenticação é feita via **JWT**, e o upload de imagens utiliza o **Multer** -- O backend é organizado em camadas para separar responsabilidades, como a camada de Roteamento, Middlewares (para autenticação) e Acesso a Dados (abstraído pelo Prisma). +- O backend é organizado em camadas para separar responsabilidades, como a camada de Roteamento, Middlewares (para autenticação) e Acesso a Dados (abstraído pelo Prisma) ## 1.2 Frontend Desacoplado - O frontend está em `frontend` e é desenvolvido com **Next.js** (React). Ele consome as APIs do backend via chamadas HTTP usando **Axios**. Para validação de formulários, utiliza **Zod**. O frontend é desacoplado do backend, rodando em seu próprio container Docker e se comunicando apenas por API REST. As páginas e componentes estão organizados em `frontend/src/app`. -Essa separação de contêineres entre o frontend e o backend é orquestrada pelo `docker-compose.yml`. +Essa separação de contêineres entre o frontend e o backend é orquestrada pelo `docker-compose.yml` # 2. -![diagrama](image.png) \ No newline at end of file +![diagrama](image.png) From 910c5ab396d24958e1e9ca206fe70523fb09988a Mon Sep 17 00:00:00 2001 From: luisfilipemendesnogueira Date: Sat, 9 Aug 2025 19:58:27 -0300 Subject: [PATCH 3/6] =?UTF-8?q?Adicionando=20Princ=C3=ADpios=20e=20padr?= =?UTF-8?q?=C3=B5es=20de=20projeto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentacao/padroes.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 documentacao/padroes.md diff --git a/documentacao/padroes.md b/documentacao/padroes.md new file mode 100644 index 00000000..fd9e0d0a --- /dev/null +++ b/documentacao/padroes.md @@ -0,0 +1,9 @@ +# 1. Princípios +- Single Responsibility Principle (Princípio da Responsabilidade Única - SRP): A classe Authenticator tem a responsabilidade de gerenciar a criação e validação de tokens JWT. Ela não se preocupa com a lógica de negócio do usuário ou com o armazenamento de dados, delegando essas tarefas a outras partes do sistema. A separação de responsabilidades também é visível na estrutura de pastas (controllers, services, repositories). + +- Dependency Inversion Principle (Princípio da Inversão de Dependência - DIP): Os serviços (módulos de alto nível, em services/) dependem de abstrações, como as classes de repositório (em repositories/), em vez de implementações concretas de acesso a dados. Da mesma forma, os controladores (em controllers/) dependem de serviços. Isso desacopla os componentes, tornando o sistema mais flexível e fácil de testar, pois as dependências concretas podem ser substituídas (por exemplo, por mocks em testes) + +# 2. Padrões de Projeto (Design Patterns) +- Singleton: A classe Authenticator no arquivo authenticator.ts é exportada como uma única instância (authenticatorInstance). Isso garante que apenas um objeto de autenticação seja utilizado em toda a aplicação, centralizando o controle e o estado (como o segredo do JWT) em um único ponto. + +- Repository: A estrutura do projeto inclui uma pasta repositories (ex: repositories/user.ts, repositories/animal.ts). Este padrão abstrai a camada de acesso a dados, separando a lógica de negócio das interações diretas com o banco de dados. As classes de serviço (em services/) dependem desses repositórios para persistir e recuperar dados, sem conhecer os detalhes da implementação da persistência. \ No newline at end of file From b11ee9a629ce246631aa6af37eb5bb2d7b5571b7 Mon Sep 17 00:00:00 2001 From: luisfilipemendesnogueira Date: Sat, 9 Aug 2025 22:13:13 -0300 Subject: [PATCH 4/6] Adicionando code smells --- backend/src/repositories/animal.dto.d.ts | 51 +- documentacao/smells-refatoracoes.md | 687 ++++++++++++++++++ .../AnimalRegisterForm/AnimalRegisterForm.tsx | 129 ++-- 3 files changed, 821 insertions(+), 46 deletions(-) create mode 100644 documentacao/smells-refatoracoes.md diff --git a/backend/src/repositories/animal.dto.d.ts b/backend/src/repositories/animal.dto.d.ts index 890f2789..77a7f79a 100644 --- a/backend/src/repositories/animal.dto.d.ts +++ b/backend/src/repositories/animal.dto.d.ts @@ -1,4 +1,51 @@ -import { Animal } from '@prisma/client' +import { Animal as PrismaAnimal } from '@prisma/client' + +export type AnimalStatus = 'available' | 'adopted' | 'pending' + +export class Animal { + constructor( + public id: string, + public name: string, + public type: string, + public gender: 'Macho' | 'Fêmea', + public userId: string, + public status: AnimalStatus, + public race?: string, + public description?: string, + public createdAt?: Date, + public updatedAt?: Date, + ) {} + + isAvailable(): boolean { + return this.status === 'available' + } + + isAdopted(): boolean { + return this.status === 'adopted' + } + + shortDescription(maxLength = 50): string { + if (!this.description) return '' + return this.description.length > maxLength + ? this.description.slice(0, maxLength) + '...' + : this.description + } + + static fromPrisma(animal: PrismaAnimal): Animal { + return new Animal( + animal.id, + animal.name, + animal.type, + animal.gender as 'Macho' | 'Fêmea', + animal.userId, + animal.status as AnimalStatus, + animal.race, + animal.description, + animal.createdAt, + animal.updatedAt, + ) + } +} export namespace CreateAnimalRepositoryDTO { export type Params = { @@ -21,4 +68,4 @@ export namespace UpdateAnimalStatusRepositoryDTO { } export type Result = Animal | null -} +} \ No newline at end of file diff --git a/documentacao/smells-refatoracoes.md b/documentacao/smells-refatoracoes.md new file mode 100644 index 00000000..da112ab0 --- /dev/null +++ b/documentacao/smells-refatoracoes.md @@ -0,0 +1,687 @@ +## 1 - Smell Identificado: Long Method +### Código Original +#### AnimalRegisterForm.tsx +``` +import { animalTypesForSelect } from '@/constants/animal-types-for-select' +import { DefaultSelect } from '../DefaultSelect' +import * as S from './AnimalRegisterForm.styles' +import { animalGenderForSelect } from '@/constants/animal-gender-for-select' +import { Plus, Trash } from '@phosphor-icons/react' +import { useState } from 'react' +import { z } from 'zod' + +import * as Dialog from '@radix-ui/react-dialog' +import { DefaultDialog } from '../DefaultDialog' +import { Button } from '../Button' +import { useForm } from 'react-hook-form' +import { zodResolver } from '@hookform/resolvers/zod' +import { getCookie } from 'cookies-next' +import { animalRegister } from '@/api/register-animal' +import { AnimalTypeEnum } from '@/enums/animal-type' +import { AnimalGenderEnum } from '@/enums/animal-gender' + +const animalRegisterFormSchema = z.object({ + name: z.string().min(1, { message: 'O nome é obrigatório' }), + type: z.nativeEnum(AnimalTypeEnum, { + required_error: 'O tipo é obrigatório', + }), + gender: z.nativeEnum(AnimalGenderEnum, { + required_error: 'O gênero é obrigatório', + }), + race: z + .string() + .optional() + .transform((value) => (value === '' ? undefined : value)), + description: z + .string() + .optional() + .transform((value) => (value === '' ? undefined : value)), + pictures: z + .array(z.instanceof(File)) + .min(1, 'Adicione ao menos uma foto do animal') + .max(5, { message: 'Você pode adicionar no máximo 5 fotos' }), +}) + +export type AnimalRegisterFormData = z.infer + +export function AnimalRegisterForm() { + const [animalPictures, setAnimalPictures] = useState([]) + const [maxPicsWarningModalOpen, setMaxPicsWarningModalOpen] = useState(false) + + const { + register, + handleSubmit, + formState: { errors }, + setValue, + watch, + } = useForm({ + resolver: zodResolver(animalRegisterFormSchema), + defaultValues: { pictures: [] }, + }) + + const descriptionValue = watch('description', '') + + const handleAnimalImageUpload = (e: React.ChangeEvent) => { + if (e.target.files) { + const uploadedFiles = Array.from(e.target.files) + + if ( + uploadedFiles.length > 5 || + animalPictures.length + uploadedFiles.length > 5 + ) { + setMaxPicsWarningModalOpen(true) + return + } + + const newPictures = [...animalPictures, ...uploadedFiles] + setAnimalPictures(newPictures) + setValue('pictures', newPictures) // Atualiza no react-hook-form + } + } + + const handleRemoveAnimalPicture = (picIndex: number) => { + const newAnimalPictures = animalPictures.filter( + (pic, index) => picIndex !== index, + ) + + setAnimalPictures(newAnimalPictures) + setValue('pictures', newAnimalPictures) // Atualiza no react-hook-form + } + + const onSubmit = async (data: AnimalRegisterFormData) => { + try { + const token = getCookie('token') + + const response = await animalRegister(data, token) + + if (response.status === 201) { + alert('Animal cadastrado com sucesso!') + window.location.href = '/area_logada/meus_animais' + } else { + alert( + response.data.message || + 'Ocorreu um erro ao tentar registrar o animal.', + ) + } + } catch (err) { + const error = err as Error + console.error('Erro no registro do animal:', error) + alert(error.message || 'Ocorreu um erro ao tentar registrar o animal.') + } + } + + return ( + <> + + + Atenção + + + + Você pode adicionar no máximo 5 fotos! + + + + + + + + + + + + + + + + + + + Tipo + {errors.type && {errors.type.message}} + + + + setValue('type', value as AnimalTypeEnum) + } + /> + + + + + + + Gênero + {errors.gender && {errors.gender.message}} + + + + setValue('gender', value as AnimalGenderEnum) + } + /> + + + + + + + + + +