eletrotupi / tcc/ monografia/chapters/tech.texc377fb94289224023a0ab5d229329b5a4e06af51
16.6 KB | 326 lines Raw
1
\chapter{Tecnologias}
2

3
Este capítulo apresenta as principais tecnologias empregadas no desenvolvimento
4
do aplicativo, organizadas de acordo com a camada da arquitetura em que atuam:
5
\textit{front-end}, \textit{back-end} e infraestrutura/persistência de dados.
6

7
\section{Front-end}
8

9
O termo \textit{frontend} refere-se à camada de um sistema responsável pela
10
interação direta com o usuário: a interface visual, a lógica de apresentação e
11
o processamento que ocorre no dispositivo do próprio usuário, seja um navegador
12
ou, como no caso do Nexo, um aplicativo móvel, conforme
13
~\citet{sommerville:2010}. É o frontend que renderiza telas, captura entradas
14
do usuário (como toques e formulários) e comunica-se com o \textit{backend}
15
através de requisições de rede, geralmente utilizando APIs. No caso do Nexo,
16
essa camada é implementada com \textit{React Native} e Expo, permitindo que a
17
mesma base de código seja compilada tanto para Android utilizando bibliotecas
18
em \textit{TypeScript}.
19

20
\subsection{React Native}
21

22
React Native é um \textit{framework} de código aberto, mantido pela Meta, que
23
permite o desenvolvimento de aplicativos móveis multiplataforma a partir de
24
uma única base de código escrita em JavaScript/TypeScript, utilizando
25
componentes que são traduzidos para elementos nativos tanto no iOS quanto no
26
Android \citep{reactnative:2026}.
27

28
A escolha do React Native se justifica pelo escopo reduzido da equipe de
29
desenvolvimento (um único desenvolvedor) e facilidade de conhecimento e
30
manutenção, sem trazer, para este projeto, benefícios que justifiquem esse
31
custo adicional. Além disso, o ecossistema maduro do React Native oferece
32
ampla disponibilidade de bibliotecas para funcionalidades já previstas no
33
aplicativo, como notificações locais e gráficos.
34

35
\subsection{Zustand}
36

37
Zustand é uma biblioteca leve de gerenciamento de estado para aplicações
38
React e React Native, que dispensa a estrutura verbosa de \textit{providers}
39
e \textit{reducers} tipicamente associada a bibliotecas como Redux
40
\citep{zustand:2026}.
41

42
Ela foi escolhida para gerenciar o estado efêmero da aplicação (por exemplo,
43
dados de formulários em preenchimento ou estados de navegação temporários)
44
por sua API minimalista e baixo \textit{overhead} de configuração, adequada a um
45
projeto de escopo individual em que a complexidade adicional de soluções
46
mais robustas de gerenciamento de estado não se justifica.
47

48
\section{Back-end}
49

50
Já o termo \textit{backend} refere-se à camada de um sistema que processa a
51
lógica de negócio, gerencia o acesso e a persistência de dados, e expõe essa
52
funcionalidade a clientes (como o frontend) através de uma interface bem
53
definida, tipicamente uma API HTTP \cite{Fowler_2013}. O \textit{backend} é
54
responsável por validar dados recebidos, aplicar regras de negócio, autenticar
55
usuários, e coordenar o acesso ao banco de dados e a outros serviços
56
auxiliares. No caso do Nexo, essa camada é implementada em Node.js com o
57
framework \textit{Express}, seguindo uma arquitetura em camadas (\textit{Route}
58
$\rightarrow$ \textit{Controller} $\rightarrow$ \textit{Service} $\rightarrow$
59
Prisma) que separa as responsabilidades de validação, lógica de negócio e
60
acesso a dados.
61

62
\subsection{Express.js}
63

64
Express.js é um \textit{framework} minimalista para aplicações web em
65
Node.js, amplamente utilizado para a construção de APIs \textit{REST},
66
oferecendo uma camada fina de abstração sobre o roteamento de requisições HTTP e
67
\textit{middlewares} \citep{express:2026}.
68

69
Sua adoção se deve à simplicidade e à flexibilidade não opinativa do
70
\textit{framework}, que permite estruturar a API de acordo com as
71
necessidades específicas do projeto, sem impor convenções rígidas de
72
arquitetura, além de sua ampla documentação e maturidade no ecossistema
73
Node.js.
74

75
\subsection{Zod}
76

77
Zod é uma biblioteca de validação e definição de esquemas de dados para
78
TypeScript, que permite declarar formatos esperados de entrada (como corpos
79
de requisição) e inferir automaticamente os tipos estáticos correspondentes
80
\citep{zod:2026}.
81

82
Zod foi escolhido para validar os dados recebidos pela API antes de seu
83
processamento, garantindo que registros de humor, sono e demais entradas do
84
usuário estejam em conformidade com o formato esperado. A inferência
85
automática de tipos a partir dos esquemas evita a duplicação de definições de
86
tipo entre a camada de validação e o restante do código TypeScript do
87
back-end.
88

89
\begin{lstlisting}[language=typescript, caption={Validação e Inferência usando Zod}, label={lst:zod-inference}]
90
import {
91
  BaseMoodOption,
92
  MoodComponentOption,
93
  IntensityLevel
94
} from '@prisma/client';
95

96
export const MoodComponentSchema = z.object({
97
  component: z.nativeEnum(MoodComponentOption),
98
  intensity: z.nativeEnum(IntensityLevel)
99
});
100

101
export const CreateMoodSchema = z.object({
102
  annotation: z.string().optional(),
103
  moment: z.coerce.date(),
104
  selectedMood: z.nativeEnum(BaseMoodOption),
105
  anxietyLevel: z.number().int().min(0).max(10),
106
  stressLevel: z.number().int().min(0).max(10),
107
  energyLevel: z.number().int().min(0).max(10),
108
  moodComponents: z.array(MoodComponentSchema).min(1)
109
});
110

111
export type CreateMoodInput = z.infer<typeof CreateMoodSchema>;
112
\end{lstlisting}
113

114
\sourcecode{Elaborado pelo autor (2026)}
115

116
Aqui, é possível observar alguns elementos, como a extração direta dos
117
\textit{enums} mapeados pelo Prisma, citado anteriormente nas linhas 1--5. Em
118
seguida, são criados os \textit{schemas} de validação: um valida os componentes
119
de humor de forma aninhada dentro do objeto recebido, e o outro valida os dados
120
do próprio registro --- os níveis aceitos de ansiedade, estresse e energia; a
121
conversão do campo \texttt{moment} para um objeto de data a partir da entrada
122
recebida; a exigência de ao menos um componente de humor selecionado; entre
123
outros. Por fim, esses dados são exportados como um \textit{type} do
124
TypeScript, usado pelo compilador para a verificação estática de tipos no
125
restante do back-end.
126

127
\subsection{Valkey e BullMQ}
128

129
Valkey é um banco de dados em memória, do tipo chave-valor, criado como
130
\textit{fork} de código aberto do Redis, mantido sob a governança da Linux
131
Foundation \citep{valkey:2026}. BullMQ é uma biblioteca para gerenciamento de
132
filas de tarefas em Node.js, construída sobre bancos de dados compatíveis com
133
o protocolo Redis \citep{bullmq:2026}.
134

135
A combinação das duas ferramentas foi adotada para o processamento paralelo
136
e assíncrono de tarefas que não precisam bloquear a resposta imediata da API,
137
como o disparo de notificações programadas e o processamento de indicadores
138
a partir do histórico de registros do usuário. A escolha do Valkey em
139
específico, e não do Redis diretamente, se deve à mudança de licenciamento do
140
Redis em 2024, que passou a restringir certos usos comerciais; o Valkey
141
mantém compatibilidade total de protocolo, permanecendo sob licença
142
permissiva de código aberto.
143

144
\section{Infraestrutura e Persistência de Dados}
145

146
A camada de \textit{infraestrutura e persistência} de um sistema abrange os
147
componentes responsáveis por armazenar e gerenciar os dados de forma durável,
148
bem como os serviços auxiliares que sustentam o funcionamento da aplicação em
149
produção \cite{Fowler_2013}. O elemento central dessa camada costuma ser o
150
sistema de gerenciamento de banco de dados (SGBD), responsável por garantir a
151
integridade, consistência e durabilidade dos dados armazenados. No caso do
152
Nexo, essa função é cumprida pelo PostgreSQL, um SGBD relacional, acessado
153
através do Prisma, um \textit{Object-Relational Mapper} (ORM) que traduz
154
operações do banco de dados em chamadas de código TypeScript, eliminando a
155
necessidade de escrever consultas SQL manualmente na maior parte dos casos.
156

157
Complementarmente, o Nexo utiliza o Valkey como armazenamento em memória de
158
alta velocidade, empregado tanto para cache quanto como \textit{broker} de
159
mensagens para o sistema de filas, cujo suas filas são gerenciadas com a
160
biblioteca BullMQ para lidar com os \textit{background jobs} enviados. Os
161
mesmos são responsáveis por processar os indicadores semanais e diários
162
descritos nos capítulos anteriores.
163

164
\subsection{PostgreSQL}
165

166
PostgreSQL é um sistema gerenciador de banco de dados relacional, de código
167
aberto, reconhecido por sua conformidade com o padrão SQL, robustez e suporte
168
a tipos de dados avançados \citep{postgresql:2026}.
169

170
Sua escolha se justifica pela natureza estruturada e relacional dos dados do
171
domínio da aplicação (registros de humor associados a usuários, horários,
172
gatilhos e categorias), que se beneficia das garantias de integridade
173
referencial e das transações ACID oferecidas por um banco relacional
174
maduro, em detrimento de soluções não relacionais.
175

176
\subsection{Prisma}
177

178
Prisma é um \textit{Object-Relational Mapper} (ORM) para o ecossistema
179
Node.js/TypeScript, que gera automaticamente um cliente de acesso ao banco de
180
dados tipado a partir de um esquema declarativo, além de oferecer um sistema
181
de migrações versionadas \citep{prisma:2026}.
182

183
O Prisma foi escolhido por permitir a manutenção do esquema do banco de dados
184
de forma versionada e sincronizada com o código da aplicação, reduzindo a
185
chance de divergência entre a estrutura do banco e as entidades manipuladas
186
pelo back-end, além de prover checagem de tipos em tempo de compilação para
187
as consultas realizadas.
188

189
\section{Implantação e Desenvolvimento Contínuo} \label{sec:deploy-and-ci}
190

191
Já os termos \textit{implantação} (\textit{deployment}) e
192
\textit{automatização} referem-se ao conjunto de práticas e ferramentas
193
utilizadas para transformar o código-fonte de uma aplicação em um sistema
194
efetivamente disponível e em execução, de forma repetível e com o mínimo de
195
intervenção manual \cite{Humble_Farley_2011}. No caso do Nexo, essa camada é
196
sustentada por três ferramentas complementares. O Docker é utilizado para
197
empacotar a aplicação e suas dependências em contêineres isolados e portáveis,
198
garantindo que o ambiente de execução seja idêntico entre desenvolvimento e
199
produção; o Docker Compose orquestra a execução conjunta de múltiplos
200
contêineres (aplicação, banco de dados, Valkey), definindo suas configurações e
201
interdependências em um único arquivo declarativo. O GitHub Actions é
202
responsável pela integração e entrega contínuas (CI/CD), automatizando
203
etapas como execução de testes e construção de imagens a cada alteração no
204
código. Por fim, o Ansible é utilizado para o provisionamento inicial da
205
infraestrutura, automatizando a configuração do servidor onde a aplicação é
206
hospedada.
207

208
\subsection{Docker e Docker Compose}
209

210
Docker é uma plataforma de conteinerização que permite empacotar uma
211
aplicação e suas dependências em uma unidade isolada e portátil, capaz de ser
212
executada de forma consistente em diferentes ambientes \citep{docker:2026}. O
213
Docker Compose é uma ferramenta complementar que permite definir e orquestrar
214
múltiplos contêineres --- e as relações entre eles --- por meio de um único
215
arquivo de configuração declarativo \citep{dockercompose:2026}.
216

217
No projeto, o Docker Compose é utilizado tanto no ambiente de desenvolvimento
218
quanto no ambiente de produção para orquestrar os serviços de banco de dados
219
(PostgreSQL), armazenamento em memória (Valkey) e da própria API, garantindo
220
que as versões e configurações desses serviços sejam idênticas entre as
221
máquinas de desenvolvimento e o servidor de produção.
222

223
O uso de \textit{healthchecks} em conjunto com a diretiva
224
\texttt{depends\_on} e a condição \texttt{service\_healthy} assegura
225
que a API só seja iniciada após o banco de dados e o Valkey estarem de
226
fato prontos para aceitar conexões, evitando falhas de inicialização
227
por condição de corrida (\textit{race condition}), como ilustrado no
228
Código~\ref{lst:healthcheck-compose}.
229

230
\begin{lstlisting}[caption={Configuração do \textit{healthcheck} do Valkey e de dependência condicional da API no \texttt{docker-compose.yml}}, label={lst:healthcheck-compose}]
231
valkey:
232
  image: valkey/valkey:9.0-alpine
233
  healthcheck:
234
    test: ["CMD", "valkey-cli", "ping"]
235
    interval: 10s
236
    timeout: 5s
237
    retries: 5
238
    start_period: 5s
239

240
api:
241
  build: ./api
242
  depends_on:
243
    valkey:
244
      condition: service_healthy
245
    db:
246
      condition: service_healthy
247
\end{lstlisting}
248

249
\sourcecode{Elaborado pelo autor (2026)}
250

251
A adoção do Docker se justifica, no contexto de uma aplicação de saúde
252
mental de código aberto, pelo objetivo de garantir a reprodutibilidade do
253
ambiente de execução: qualquer pessoa que clone o repositório --- seja para
254
contribuir com o projeto, auditar seu funcionamento ou realizar sua própria
255
implantação --- consegue reproduzir o ambiente completo sem depender de
256
configurações manuais específicas do sistema operacional hospedeiro, o que
257
reforça o caráter \textit{self-service} do projeto.
258

259
\subsection{GitHub Actions}
260

261
GitHub Actions é uma plataforma de integração e entrega contínuas (CI/CD)
262
nativa do GitHub, que permite a definição de fluxos de trabalho automatizados
263
--- disparados por eventos como \textit{push} ou \textit{pull request} ---
264
descritos de forma declarativa em arquivos YAML \citep{githubactions:2026}.
265

266
No projeto, o fluxo de trabalho de CI/CD é dividido em três etapas
267
sequenciais e dependentes entre si. A primeira etapa (\texttt{test}) sobe um
268
serviço efêmero de PostgreSQL e executa a suíte de testes automatizados da
269
API a cada \textit{push} ou \textit{pull request} que modifique arquivos do
270
diretório da API.
271

272
A segunda etapa (\texttt{build-and-push}), condicionada ao
273
sucesso da primeira e restrita a eventos de \textit{push}, constrói a imagem
274
Docker de produção da API e a publica no GitHub Container Registry (GHCR),
275
com marcação automática pelo SHA do \textit{commit} e, condicionalmente, pela
276
tag \texttt{latest} quando o \textit{push} ocorre na \textit{branch}
277
\texttt{master}.
278

279
Por último, a terceira etapa (\texttt{deploy-production}), restrita à
280
\textit{branch} \texttt{master}, conecta-se ao servidor de produção via SSH e
281
realiza a atualização do serviço da API por meio da atualização da tag da
282
imagem no arquivo \texttt{.env} e da recriação do contêiner correspondente
283
via Docker Compose.
284

285
Esse processo elimina a necessidade de intervenção manual para a maior
286
parte do ciclo de implantação, restringindo a ação humana direta sobre o
287
servidor de produção a situações excepcionais, o que reduz a superfície de
288
erro humano e aumenta a confiabilidade do processo de entrega de novas
289
versões da aplicação.
290

291
\subsection{Ansible}
292

293
Ansible é uma ferramenta de automação de infraestrutura que permite
294
descrever, de forma declarativa e idempotente, o estado desejado de um ou
295
mais servidores, dispensando a necessidade de um agente instalado na máquina
296
gerenciada, uma vez que se comunica com ela via SSH \citep{ansible:2026}.
297

298
No escopo deste projeto, o papel do Ansible é ortogonal ao do Docker Compose
299
e ao do GitHub Actions: enquanto estes automatizam, respectivamente, a
300
orquestração dos serviços da aplicação e o ciclo de integração e entrega
301
contínuas, o Ansible é responsável exclusivamente pelo provisionamento
302
inicial do servidor de produção, executado uma única vez --- ou sempre que um
303
novo servidor precisar ser provisionado. Esse provisionamento consiste em:
304
(1) criar o usuário dedicado à aplicação; (2) adicionar a chave pública SSH
305
utilizada pelo GitHub Actions para se autenticar no servidor durante a etapa
306
de implantação; (3) criar os diretórios da aplicação com as permissões
307
adequadas; e (4) instalar o Docker no servidor. A cópia do arquivo de
308
variáveis de ambiente de produção (\texttt{.env.production}), por conter
309
segredos e credenciais sensíveis, é deliberadamente mantida como uma etapa
310
manual, não automatizada, e não é versionada no repositório.
311

312
A separação entre o provisionamento inicial (\textit{Ansible}), a orquestração de
313
execução (\textit{Docker Compose}) e a automação do ciclo de
314
entrega (\textit{GitHub Actions}) reflete uma decisão arquitetural:
315
cada ferramenta atua em uma camada distinta e bem delimitada do processo
316
de implantação, o que favorece a reprodutibilidade de todo o processo,
317
do zero, por qualquer pessoa que deseje hospedar sua própria instância da aplicação.
318

319
Essa preocupação é particularmente relevante no contexto de um
320
aplicativo de saúde mental de código aberto: ao reduzir a dependência
321
de serviços proprietários de implantação (\textit{walled gardens}) e
322
ao documentar e automatizar o processo de provisionamento, o projeto
323
favorece um modelo mais alinhado aos princípios de privacidade e de
324
autonomia sobre os dados dos usuários, permitindo que instituições ou
325
indivíduos preocupados com a confidencialidade de dados sensíveis de
326
saúde mental hospedem e controlem sua própria instância da aplicação.