0% acharam este documento útil (0 voto)
16 visualizações21 páginas

JS e EXPESS - JS

Enviado por

spyonthief
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
16 visualizações21 páginas

JS e EXPESS - JS

Enviado por

spyonthief
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
Você está na página 1/ 21

Capítulo 1: A Organização do Caos – Por Que Precisamos de Arquitetura?

Imagine por um momento que você está construindo uma casa. Se você simplesmente começasse a
empilhar tijolos e passar fiação sem nenhum plano ou projeto, em pouco tempo a obra se tornaria um
labirinto confuso, difícil de manter e quase impossível de expandir. No desenvolvimento de software
acontece a mesma coisa. Em JavaScript, uma linguagem extremamente flexível, é tentador sair
codificando sem uma estrutura definida – mas essa flexibilidade pode rapidamente levar a um
emaranhado caótico de código, o famoso “spaghetti code”. A arquitetura de software entra em cena
como o nosso projeto arquitetônico: um mapa que guia a construção de uma aplicação sólida e
funcional. Em outras palavras, arquitetura é a solução para evitar o caos em sistemas complexos.

Dentro do vasto universo de arquiteturas de software, um padrão se destaca pela longevidade, pela
elegância e pela capacidade de organizar até os projetos mais complexos: o padrão Modelo–Visão–
Controlador, ou simplesmente MVC. Embora não seja novidade (foi concebido no final da década de
1970 – creditado a Trygve Reenskaug, que o documentou em 1978 no contexto da linguagem
Smalltalk-80), o MVC se mantém notavelmente relevante. Décadas se passaram, a tecnologia evoluiu,
mas os princípios fundamentais desse padrão continuam válidos, inclusive no dinâmico ecossistema do
JavaScript.

Mas por que o MVC é tão valorizado? O objetivo principal desse padrão é separar as preocupações da
aplicação em três componentes claros, cada qual com sua responsabilidade bem definida: dados,
interface com o usuário e lógica de controle. Ao dividir uma aplicação nessas três camadas – Modelo,
Visão e Controlador – conseguimos organizar o código de forma que o desenvolvimento se torna mais
simples de entender e acompanhar. Essa divisão também torna a manutenção muito mais fácil: é
possível alterar uma parte (por exemplo, a forma como os dados são exibidos na tela) sem precisar
mexer em outras (como a forma que os dados são armazenados ou as regras de negócio). Em suma, o
MVC fornece uma solução testada e comprovada para um problema comum na engenharia de
software: como estruturar um projeto de modo limpo, escalável e de fácil manutenção.

Capítulo 2: Decifrando a Tríade – Model, View e Controller


Para entender como Modelo, Visão e Controlador trabalham juntos, vamos usar uma analogia
simples. Pense em um restaurante. O cliente que entra com fome representaria o usuário da sua
aplicação. O salão de jantar – com o cardápio, as mesas e toda a apresentação – é a Visão: é o que o
usuário vê e com o que ele interage diretamente. A cozinha, onde os alimentos são armazenados e
preparados, é o Modelo: ali estão os dados e a lógica de negócio, tudo o que acontece “nos bastidores”
para que o prato (a funcionalidade) fique pronto. E quem faz a ponte entre o salão e a cozinha? O
garçom – que no nosso caso representa o Controlador. É ele quem leva o pedido do cliente até a
cozinha e, depois, traz a refeição pronta de volta à mesa. Essa tríade (Modelo, Visão e Controlador)
opera em harmonia para que o cliente seja bem servido. Cada componente tem seu papel específico, e
é justamente essa coordenação organizada que garante o bom funcionamento do “restaurante” – ou da
aplicação, no nosso caso.

O Modelo: O Cérebro da Aplicação (Dados e Lógica de Negócio)


O Modelo é o coração pulsante e o cérebro da aplicação. Nele residem os dados e as regras de negócio
– tudo aquilo que a aplicação faz de fato. Podemos pensar no Modelo como a cozinha do restaurante: é
onde as matérias-primas (dados) são armazenadas e preparadas de acordo com receitas (regras de
negócio) para gerar pratos prontos (informações processadas). A principal responsabilidade do Modelo
é gerenciar o estado da aplicação. Isso significa lidar com armazenamento e recuperação de
informações (muitas vezes comunicando-se com um banco de dados), executar cálculos ou operações

1
complexas, aplicar validações e garantir que todas as regras de negócio sejam cumpridas. Importante
destacar que o Modelo funciona de forma independente da interface do usuário; ele não sabe nada
sobre como os dados serão exibidos ou solicitados, ele apenas cuida dos dados em si.

Em aplicações JavaScript, especialmente no back-end com Node.js, a implementação do Modelo


geralmente envolve múltiplos arquivos ou camadas. Por exemplo, se usamos um banco de dados, é
comum definir modelos de dados usando bibliotecas como Sequelize (para bancos SQL) ou Mongoose
(para bancos NoSQL como MongoDB). Esses modelos definem a estrutura dos dados – equivalendo, por
exemplo, às tabelas e colunas de um banco de dados ou aos campos de um documento. Além disso, é
comum extrair a lógica de negócio para serviços especializados. Pense assim: o arquivo do modelo
(digamos, um arquivo Pessoa.js que define um esquema de Pessoa no banco de dados) define o
que é uma Pessoa (quais campos ela tem, tipos de dados, restrições etc.), enquanto um serviço (por
exemplo, PessoaService.js ) define como lidamos com essas pessoas (funções para criar uma nova
pessoa, listar pessoas existentes, aplicar regras específicas antes de salvar, etc.). Essa separação
adicional – modelo de dados de um lado e lógica de negócios do outro – não é obrigatória no MVC
clássico, mas muitas vezes surge como uma boa prática dentro do ecossistema Node.js para manter o
código modular e testável. Em sistemas pequenos, às vezes o Modelo pode ser bem simples, talvez até
só uma estrutura de dados na memória. Mas conforme a aplicação cresce, o Modelo tende a englobar
essas partes múltiplas (definições de dados e operações sobre eles), sempre mantendo a ideia central:
ele gerencia os dados e as regras, mas não se preocupa com apresentação ou fluxo de interação.

A Visão: A Face Amigável da Aplicação (Interface do Usuário)


Se o Modelo é a cozinha, a Visão é o salão do restaurante – é tudo o que o usuário vê e com o que
interage. A Visão é responsável por apresentar os dados de maneira compreensível e amigável. Pode
ser uma página HTML num contexto web, pode ser uma interface gráfica de um aplicativo desktop, ou
até mesmo uma resposta em formato JSON numa API (embora, nesse último caso, geralmente seja
consumida por outra aplicação, não diretamente por um usuário humano). No caso de aplicações
JavaScript web tradicionais, podemos imaginar a Visão como sendo uma página HTML ou um template
que exibe informações vindas do Modelo. O papel fundamental da Visão é mostrar os dados ao
usuário e capturar as ações do usuário (cliques, preenchimento de formulários, navegação etc.),
encaminhando essas interações para o Controlador. Uma característica importante da Visão no MVC é
que ela deve ser “burra” no sentido de não conter lógica de negócio. Ela não toma decisões por conta
própria sobre como processar dados – ela apenas exibe o que lhe é dado e avisa o Controlador quando
o usuário faz algo (como clicar em um botão ou selecionar um menu).

Em desenvolvimento web com JavaScript, a Visão pode se manifestar de formas diferentes dependendo
da tecnologia escolhida. Se você estiver usando uma abordagem mais tradicional no back-end, a Visão
pode ser um arquivo HTML ou um template gerado por um mecanismo de template (template engine)
como Handlebars, EJS ou Pug. Nesse caso, o servidor insere os dados dentro do HTML antes de enviar a
página pronta ao navegador do usuário. Por exemplo, um template pode ter uma marcação especial
{{nome}} onde, ao renderizar a página, o nome do usuário será inserido. Por outro lado, em
frameworks front-end modernos como React, Vue ou Angular, a Visão é frequentemente construída
como componentes ou templates declarativos que o próprio navegador (com ajuda do framework)
renderiza dinamicamente. Em Angular, por exemplo, há arquivos .html de templates que
correspondem a componentes – esses arquivos definem a estrutura da interface (botões, campos,
textos), mas não incluem a lógica de como os dados vêm parar ali. Independentemente da ferramenta,
o princípio permanece: a Visão cuida do como exibir. Ela não sabe fazer cálculos complicados ou
acessar banco de dados – se precisa de algum dado ou resultado de alguma ação, ela vai pedir isso ao
Controlador. Também, uma Visão bem feita não sabe nada sobre onde os dados vieram ou o que o
usuário fará com eles; ela apenas mostra e reage às interações. Assim, caso a lógica por trás dos dados
mude (por exemplo, mudamos o banco de dados ou a forma de calcular um valor), a Visão não precisa

2
ser alterada. Essa independência entre Visão e Modelo, mediada pelo Controlador, é essencial para
manter o sistema flexível.

O Controlador: O Maestro da Orquestra (Fluxo e Tomada de Decisão)


Se voltarmos à nossa analogia do restaurante, o Controlador é o garçom experiente que coordena tudo.
Ele recebe o pedido do cliente no salão (a ação do usuário na interface) e leva esse pedido para a
cozinha (aciona o Modelo para manipular dados), depois retorna com o prato pronto para servir ao
cliente (pega os dados do Modelo e passa para a Visão exibir). Em termos técnicos, o Controlador é o
ponto central que gerencia o fluxo da aplicação e a comunicação entre Modelo e Visão. É ele que
entende o que o usuário quer e decide como a aplicação deve responder.

A jornada do Controlador geralmente começa quando uma interação acontece na Visão – por exemplo,
o usuário clica em um botão “Salvar”. A Visão, sozinha, não sabe salvar nada, então ela notifica o
Controlador dessa intenção do usuário. O Controlador então recebe essa requisição (no contexto web,
pense em uma requisição HTTP chegando ao servidor, ou em um evento sendo disparado no front-end)
e vai decidir o próximo passo. Se for uma operação que envolve dados, o Controlador chamará o
Modelo apropriado. Por exemplo, “Salvar” um item novo pode significar mandar o Modelo inserir um
novo registro no banco de dados. O Controlador passa ao Modelo os dados necessários (talvez os
campos preenchidos pelo usuário no formulário) e pede que o Modelo execute a lógica de negócio
correspondente. O Modelo, por sua vez, realiza essa tarefa – por exemplo, valida os dados e os salva no
banco – e devolve um resultado (talvez confirmando que deu tudo certo, ou retornando o objeto recém-
criado, ou até um erro se algo falhou). Com o resultado em mãos, cabe ao Controlador decidir como
prosseguir. Se o item foi salvo com sucesso, o Controlador pode então solicitar à Visão que exiba uma
página atualizada, talvez a lista de itens agora incluindo o novo. Para isso, ele pode pedir ao Modelo
uma lista atualizada de dados e escolher a Visão (uma página ou componente) adequada para mostrar
esses dados. Em seguida, ele passa os dados para essa Visão renderizar. Por outro lado, se houvesse
um erro (digamos que o Modelo reportou uma validação falhou), o Controlador poderia decidir
reencaminhar o usuário para uma página de erro ou de volta ao formulário com mensagens de aviso.
Em todos os casos, é o Controlador quem toma essas decisões de fluxo: qual ação executar, qual
resposta dar e qual Visão apresentar a seguir.

Muitas vezes se debate: o usuário está interagindo com a Visão ou com o Controlador? Do ponto de
vista da experiência, o usuário clica em botões e preenche campos na interface – ou seja, ele interage
com a Visão. Porém, esses cliques e ações na verdade estão desencadeando chamadas ao Controlador.
Então podemos dizer que, indiretamente, o usuário está solicitando coisas ao Controlador através da
Visão. O Controlador é realmente o “maestro da orquestra”: ele não toca o violino (não renderiza a
interface) nem toca a tuba (não lida diretamente com o banco de dados), mas diz ao violino quando
entrar e à tuba quando parar, garantindo que a música saia harmoniosa. Em resumo, o Controlador
orquestra a interação entre o que o usuário faz e o que a aplicação deve fazer em resposta, mantendo
as outras camadas sincronizadas e focadas em suas especialidades.

Capítulo 3: A Jornada de uma Requisição HTTP – Um Passeio pelo MVC


Para tornar tudo isso mais concreto, vamos acompanhar uma história: a jornada de uma requisição em
uma aplicação web MVC escrita em JavaScript. Imagine que nossa aplicação é um gerenciador de lista
de tarefas, e você (usuário) decide adicionar uma nova tarefa. Você está na página que mostra suas
tarefas (Visão), e clica no botão “Adicionar tarefa”. O que acontece a seguir nos bastidores?

Tudo começa na Visão do navegador. Ao clicar em “Adicionar tarefa”, talvez um formulário seja enviado
ou uma chamada AJAX seja disparada – de qualquer forma, uma requisição HTTP é enviada do seu
navegador para o servidor indicando a intenção de criar uma nova tarefa. Essa é a início da jornada: o
pedido do usuário saiu do mundo da interface e está a caminho do mundo do servidor.

3
Ao chegar no servidor, a requisição encontra o roteador (sistema de rotas) da aplicação. Pense no
roteador como uma recepcionista diligente: sua função é examinar cada requisição que chega (ela olha
a URL e o método HTTP, como GET ou POST) e decidir para onde encaminhá-la. No nosso exemplo, a
requisição pode ser algo como um POST para “/tarefas/adicionar”. O roteador reconhece esse caminho
e sabe exatamente qual parte do código deve lidar com isso – ele aponta para o Controlador
responsável pelas tarefas. Assim, o roteamento garante que a requisição vá para o lugar certo, isto é,
ele encaminha o pedido para o método apropriado do Controlador de tarefas.

Agora entramos no território do Controlador. O controlador específico de tarefas (vamos chamá-lo de


TarefaController aqui, só para ilustrar) tem um método para adicionar uma nova tarefa. Esse
método é invocado pelo roteador, recebendo os dados da requisição (por exemplo, o título e descrição
da tarefa que você digitou). O Controlador entende o que você quer fazer – adicionar uma tarefa – mas
ele próprio não vai salvar a tarefa diretamente; essa não é a sua responsabilidade direta. Em vez disso,
ele funciona como um coordenador. O TarefaController chama o Modelo, ou alguma função de
serviço associada ao Modelo, para realizar a operação de criação da nova tarefa. É como dizer: “Ei
Modelo, aqui estão os detalhes desta tarefa nova. Por favor, salve isso e me diga o resultado.”

Ao receber o chamado, o Modelo entra em ação. Esse é o momento em que a lógica de negócio
acontece de fato. O Modelo “sabe” como criar uma nova tarefa – talvez precise validar se o título não
está vazio, talvez precise registrar a data de criação, e certamente precisará comunicar-se com o banco
de dados para inserir um novo registro de tarefa. Suponha que todas as regras de negócio sejam
cumpridas e o banco de dados retorne confirmando que a nova tarefa foi salva com sucesso,
retornando também um ID para essa tarefa ou o objeto completo recém-criado. O Modelo então
devolve esse resultado ao Controlador. (Caso houvesse algum problema – digamos, o título era inválido
ou o banco de dados estivesse indisponível – o Modelo informaria o Controlador sobre a falha em vez
do sucesso. Mas vamos seguir pelo caminho feliz primeiro.)

De volta ao Controlador, agora com o resultado em mãos, é hora de preparar a resposta ao usuário. A
nova tarefa foi salva corretamente? Ótimo, então precisamos atualizar o que o usuário vê para refletir
isso. O Controlador pode então decidir que a aplicação deve exibir novamente a lista de tarefas, agora
incluindo a tarefa recém-adicionada. Para isso, ele pode solicitar ao Modelo ou serviço uma lista
atualizada de todas as tarefas. O Modelo responde com a lista contendo a nova tarefa. Munido desses
dados atualizados, o Controlador escolhe a Visão adequada – talvez a mesma página de lista de tarefas
que o usuário já estava visualizando – e pede para renderizar essa Visão novamente, só que agora com
a lista atualizada. Em termos práticos, o Controlador pode chamar algo como uma função de
renderização passando os dados (a lista de tarefas) e indicando qual template deve ser usado para
construir o HTML.

Chegamos então à Visão novamente, fechando o ciclo. A Visão (por exemplo, um template chamado
listaTarefas.html ) recebe os dados enviados pelo Controlador e monta a página. Ela irá iterar por
cada tarefa na lista e gerar o código HTML necessário para exibi-las – incluindo a nova tarefa que você
acabou de adicionar. Essa página completa é então enviada pelo servidor de volta para o seu
navegador.

Finalmente, do ponto de vista do usuário, a página recarrega (ou talvez, em uma aplicação single-page,
só a seção de lista se atualize dinamicamente) mostrando agora todas as tarefas com a nova tarefa
incluída. A jornada da requisição foi concluída com sucesso: o seu clique desencadeou uma série de
passos coordenados entre Visão, Controlador e Modelo, e resultou em uma resposta útil e organizada.
Da próxima vez que outra interação ocorrer – como marcar a tarefa como concluída ou remover uma
tarefa – todo esse ciclo se repete, mantendo um fluxo previsível e estruturado de como os dados viajam

4
e como a interface reage. Esse passeio pelo MVC em ação ilustra na prática como cada camada colabora
dentro de uma aplicação real.

Capítulo 4: Em Prática (Parte 1) – O MVC Clássico com Node.js e Express


Agora que entendemos os conceitos, vamos ver como eles se manifestam na prática em um ambiente
JavaScript real de back-end. Um exemplo clássico é usar Node.js com o framework Express.js, que é um
dos frameworks web mais populares no ecossistema JavaScript. Embora o Express seja minimalista e
não imponha uma estrutura de diretórios rígida por conta própria, a comunidade de desenvolvedores
convergiu para um modo de organizar projetos Node que reflete nitidamente o padrão MVC.

Imagine que estamos desenvolvendo uma aplicação simples para gerenciar uma lista de pessoas (um
cadastro básico com nome e sobrenome, por exemplo). Usando MVC, é comum organizarmos o projeto
com pastas separadas para controllers (controladores), models (modelos) e views (visões), além de
algumas pastas auxiliares como routes (para definir as rotas da aplicação) e possivelmente services
(para lógica de negócio adicional). Vamos entender o papel de cada parte nessa estrutura, seguindo
nosso exemplo:

• O arquivo principal (por exemplo, index.js ): Este é o ponto de entrada da aplicação Node.
Quando executamos a aplicação, é esse arquivo que o Node roda inicialmente. Nele
configuramos o servidor Express e definimos as configurações globais. Por exemplo, aqui
indicamos em qual porta o servidor vai rodar, configuramos o mecanismo de template (se
usarmos um, como Handlebars para gerar HTML), e conectamos ao banco de dados caso haja
um. Também é no index.js que “montamos” as rotas – ou seja, importamos os módulos de
rotas e instruímos o nosso app Express a usá-los. Em resumo, o index.js inicializa a aplicação
e amarra todas as partes: ele liga o motor do carro, ajusta os espelhos e diz “vamos lá, agora
estamos prontos para receber requisições”.

• A pasta de rotas ( routes ): Dentro dela, tipicamente temos arquivos que agrupam rotas
relacionadas. Seguindo nosso exemplo, podemos ter um arquivo mainRoutes.js que define
as rotas principais do sistema. Nele, utilizamos as funcionalidades do Express para dizer coisas
como: “quando chegar uma requisição GET no caminho / , vamos chamar o método X do
controlador Y” ou “se chegar uma requisição POST em /pessoa/adicionar , vamos chamar o
método Z do controlador Y ”. Cada entrada de rota mapeia uma URL específica (e
um método HTTP específico) para uma função em um controlador. Você pode
imaginar as rotas como uma espécie de central telefônica, conectando a
chamada (requisição) ao ramal correto (função do controlador). No nosso
caso, por exemplo, a rota / (a página inicial) poderia ser mapeada para o
método getHome do mainController , enquanto a rota /add (acionada quando um
formulário de adicionar pessoa é enviado) poderia apontar para o
método postPessoa` do mesmo controlador. Essa separação num arquivo de rotas deixa claro
todos os caminhos que nossa aplicação web responde e quem lida com cada um deles.

• A pasta de controladores ( controllers ): Aqui é onde ficam os Controladores da nossa


aplicação, cada um geralmente correspondendo a um conjunto de funcionalidades ou uma área
do sistema. No nosso exemplo, podemos ter mainController.js dentro de controllers. Esse
mainController conteria funções como getHome e postPessoa que mencionamos.
Vamos detalhar essas funções para entender bem: o método getHome do controlador seria
responsável por lidar com requisições à página inicial – possivelmente buscando a lista de
pessoas cadastradas para exibir. Assim, quando o roteador direciona uma requisição GET “/” para
mainController.getHome , essa função é executada. Dentro dela, provavelmente

5
chamaremos algum código do Modelo (ou serviço) para obter os dados (neste caso, a lista de
pessoas). Uma vez obtidos, o controlador então escolhe a Visão adequada para renderizar esses
dados – talvez a página home.handlebars – e devolve a resposta. Já o método postPessoa
seria invocado quando o usuário envia o formulário de uma nova pessoa (via POST para “/add”).
Ele recebe os dados do formulário (por exemplo, nome e sobrenome da nova pessoa),
possivelmente faz alguma validação simples e então delega a criação dessa pessoa ao Modelo
ou serviço responsável. Se o modelo confirmar que salvou com sucesso no banco de dados, o
controlador pode redirecionar de volta para a página inicial ou chamar novamente o getHome
para atualizar a lista. O importante é: o controlador não está inserindo nada no banco
diretamente, ele está chamando a parte responsável por isso e depois decidindo o que a
aplicação deve fazer em seguida (neste caso, mostrar a lista atualizada ou talvez mostrar uma
mensagem de sucesso). Em um projeto organizado, cada controlador lida com um aspecto da
aplicação – poderíamos ter um usuarioController para coisas de usuário, um
produtoController para coisas de produto, e assim por diante – mantendo cada conjunto
de ações coeso e compreensível.

• A pasta de modelos ( models ): Essa é a camada Modelo do MVC, o lugar focado nos dados.
No nosso cenário de gerenciar pessoas, podemos ter um arquivo Pessoa.js dentro de
models. Esse arquivo define estruturalmente o que é uma “Pessoa” para a nossa aplicação e
como interagimos com os dados de pessoas. Se usamos um ORM como Sequelize, o
Pessoa.js provavelmente define uma entidade Pessoa com campos como nome e
sobrenome , talvez idade ou email dependendo do que queremos armazenar. Ele também
define tipos desses campos (por exemplo, nome é texto, idade é número) e possivelmente
restrições (nome é obrigatório, por exemplo). O modelo pode incluir métodos específicos
relacionados a pessoas – ou, se estamos usando recursos do ORM, o próprio ORM já fornece
métodos para criar, buscar, atualizar e remover registros de Pessoa no banco de dados. A grande
ideia é que o Pessoa.js encapsula tudo que o sistema entende sobre o dado “Pessoa” e como
persistir esse dado. Vale ressaltar que, nesse modelo em particular, não colocamos regras de
negócio elaboradas – ele está preocupado principalmente em conversar com o banco de dados
e garantir a integridade básica dos dados (como tipos corretos, campos obrigatórios, etc.).
Regras de negócio mais complexas (como “não permitir cadastrar duas pessoas com o mesmo
email”) poderiam ser implementadas aqui, mas muitas vezes, para não inflar demais o modelo,
essas regras vivem nos serviços ou controladores. Em suma, o modelo é a representação e porta
de entrada para os dados da aplicação.

• (Opcional) A pasta de serviços ( services ): Nem todos os projetos a incluem, mas ela é cada
vez mais comum e pode ser vista como um aprimoramento do MVC clássico. Os serviços são
classes ou módulos que contêm lógica de negócio mais sofisticada, utilizando os modelos para
acessar dados. No exemplo da Pessoa, poderíamos ter um PessoaService.js . Ele importaria
o modelo Pessoa e ofereceria funções como criarPessoa(dados) ou listarPessoas() .
Dentro dessas funções, ele chamaria métodos do modelo (por exemplo,
Pessoa.create(dados) para salvar no banco, ou Pessoa.findAll() para buscar todos).
A vantagem de ter o serviço é que podemos colocar ali lógica adicional, se necessário, sem
“sujar” o controlador ou depender de colocar tudo no modelo. Por exemplo, antes de criar uma
pessoa nova, o serviço poderia verificar se já existe alguém com o mesmo email e decidir lançar
um erro se for o caso – isso é uma regra de negócio que poderíamos centralizar no serviço. Ou
após criar a pessoa, talvez queremos enviar um email de boas-vindas – o serviço poderia chamar
um módulo de email. Assim, o PessoaService atua como uma camada intermediária
facultativa: ele conversa com o modelo e executa regras de negócio, de forma que o controlador
possa simplesmente chamá-lo sem se preocupar com detalhes. Em aplicações maiores, essa
separação deixa o Controlador mais enxuto (apenas coordenando chamadas e respostas) e

6
isola a “inteligência” da aplicação nos serviços, facilitando testes e reutilização dessa lógica em
diferentes controladores ou partes do sistema.

• A pasta de visões ( views ): Aqui residem os arquivos relacionados à Visão – tipicamente


templates que serão renderizados em HTML para o usuário final. Mantendo nosso exemplo,
podemos ter um arquivo home.handlebars em views. Esse seria o template da página inicial
que mostra a lista de pessoas. Dentro dele, escreveríamos HTML comum e usaríamos a sintaxe
do Handlebars (um {{ e }} para inserir valores dinâmicos) para colocar cada pessoa na lista. Por
exemplo, pode ter um bloco que vai iterar por uma lista de pessoas fornecida e gerar um <li>
ou uma linha de tabela para cada pessoa contendo seu nome e sobrenome. Quando o
controlador, lá no mainController.getHome , chama algo como res.render('home',
dados) , o Express localiza o arquivo home.handlebars e mescla com o objeto dados
passado. Esse objeto teria, digamos, uma propriedade listaDePessoas contendo todas as
pessoas. O template então consegue, através dessa propriedade, exibir as informações. O
resultado final dessa renderização é uma página HTML completa, prontinha para ser enviada ao
navegador. Notar que o template não sabe de onde vêm as pessoas (poderiam vir de um banco
de dados, de um cálculo, não importa) – ele apenas sabe que tem uma lista e como exibi-la.
Poderíamos ter outras visões, como um erro.handlebars para mostrar mensagens de erro,
ou um formPessoa.handlebars contendo um formulário para adicionar/editar pessoas.
Cada um desses arquivos representa uma tela ou parte da interface, sem lógica de negócio,
apenas com código de apresentação.

Em conjunto, essa estrutura de um projeto Node.js com Express baseada em MVC permite uma clara
divisão de responsabilidades. Se um dia precisarmos mudar o banco de dados, ajustamos os modelos
(e talvez serviços) sem tocar nos controladores ou visões. Se quisermos repaginar a interface, mudamos
as visões (templates) sem precisar reescrever as regras de negócio. E se novas regras de negócio
surgirem, adicionamos nos serviços ou modelos sem necessariamente alterar como as rotas estão
definidas. Cada parte sabe bem o seu papel, exatamente como no restaurante: a cozinha cuida dos
ingredientes, o garçom coordena pedidos e entregas, o salão apresenta a comida – e todos trabalham
de forma integrada para que o cliente tenha uma boa experiência.

Capítulo 5: Em Prática (Parte 2) – A Evolução no Front-end com Angular (Componentes em vez do MVC
Tradicional)
Vamos agora para o outro lado do desenvolvimento JavaScript: o front-end. Veremos como os princípios
de separação de responsabilidades do MVC aparecem em um framework moderno de interface, o
Angular. Curiosamente, embora o Angular seja muitas vezes descrito como um framework MVC (e a
versão original AngularJS realmente se inspirava nesse padrão), a forma como uma aplicação Angular
atual se organiza é um pouco diferente do MVC “clássico” que descrevemos até agora. Isso aconteceu
porque, no desenvolvimento de aplicações grandes no front-end, a estratégia de ter pastas globais
separadas para modelos, visões e controladores começou a apresentar desafios de escalabilidade.

Imagine uma aplicação web grande, com dezenas de telas ou componentes de interface – por exemplo,
um sistema administrativo com módulos de usuários, produtos, pedidos, relatórios, etc. Se fôssemos
seguir à risca o MVC tradicional no front-end, teríamos algo como uma pasta com todos os
componentes de interface (Visões) da aplicação, outra com todos os arquivos de lógica (Controladores)
e talvez uma com modelos de dados (se aplicável). Em pouco tempo, cada pasta seria um amontoado
enorme de arquivos, e trabalhar em uma funcionalidade específica seria complicado, porque você
precisaria lembrar de abrir e editar arquivos espalhados em três lugares diferentes (a Visão da
funcionalidade na pasta de visões, o Controlador correspondente em outra pasta, e talvez um modelo
em outra). Só para localizar os três relacionados a “usuários”, você vasculharia três diretórios distintos

7
cheios de outros arquivos de outras funcionalidades. Claramente, isso não escala bem conforme o
projeto cresce.

A abordagem adotada pelo Angular (a partir da versão 2+, ou seja, as versões modernas do framework)
foi organizar o projeto por funcionalidades ou domínios, em vez de por tipo de componente MVC.
Isso significa que a separação MVC ainda existe conceitualmente, mas é aplicada dentro de cada
funcionalidade de forma agrupada. A estrutura típica de um projeto Angular é dividida em três grandes
seções: core, shared e features.

• Core: é onde ficam serviços e componentes centrais que são carregados apenas uma vez na
aplicação e são usados globalmente. Por exemplo, um serviço de autenticação, um componente
de barra de navegação, interceptadores de requisições HTTP – elementos fundamentais que
toda a aplicação pode usar. Esse core é como a “infraestrutura” da casa.

• Shared: aqui residem peças reutilizáveis que podem ser compartilhadas entre várias
funcionalidades. Pode incluir componentes de UI genéricos (um botão customizado, um seletor
de data), pipes (funções para formatação de valores na interface), diretivas utilitárias e até
modelos de dados (interfaces ou classes) que são usadas em múltiplos lugares. Pense no shared
como uma biblioteca de componentes e serviços prontos para serem usados em qualquer parte
do aplicativo – uma espécie de caixa de ferramentas comum.

• Features: esta é a parte mais significativa em termos de organização. Cada “feature” é uma
funcionalidade ou módulo lógico da aplicação. Por exemplo, se estamos construindo um e-
commerce, podemos ter features como Usuários, Produtos, Carrinho, Pedidos, etc. Cada
feature ganha uma pasta própria dentro de features/ . Dentro da pasta de uma feature, aí
sim encontraremos separação de arquivos que lembram o MVC: componentes (visões e
controladores) e serviços (modelos de dados e lógica).

Vamos aprofundar com um exemplo concreto de feature no Angular: suponha que tenhamos a feature
de Usuários ( usuario ). Essa funcionalidade poderia lidar com listar usuários, adicionar um novo
usuário, editar dados de um usuário etc. Dentro do diretório features/usuario/ , organizamos tudo
que pertence a esse contexto de usuários:

• Haverá um componente principal dessa funcionalidade, digamos usuario.component.ts


(às vezes chamado de container ou page component). Esse arquivo .ts define uma classe
Angular Component. Essa classe funciona de forma semelhante a um Controlador do MVC: ela
gerencia o estado e as ações do que acontece na tela de usuários. Por exemplo, quando o
componente de usuários é carregado, ele pode chamar um serviço para buscar a lista de
usuários e armazená-la numa propriedade; se o usuário clicar em “criar novo usuário”, esse
componente pode invocar uma função para realizar essa ação. Ele também contém métodos
que respondem a eventos da interface, como cliques em botões ou submissão de formulários,
atuando como o coordenador dentro daquela tela de usuários.

• A classe do componente vem acompanhada de um template HTML, normalmente em


usuario.component.html . Este arquivo é a Visão para a funcionalidade de usuários. Nele
está descrito a interface: pode haver um bloco de código para exibir uma tabela de usuários,
outro com um formulário para adicionar/editar, botões, textos, etc. Este template vai utilizar a
sintaxe do Angular (como *ngFor para loops, {{ }} para interpolação de valores, e assim
por diante) para definir como os dados fornecidos pelo componente devem aparecer na tela.
Aqui vemos claramente o papel de uma Visão MVC: o template por si só não tem nenhuma

8
lógica de negócio, ele apenas pega os dados que o componente (controlador) oferece e os exibe
conforme a estrutura definida, e notifica o componente quando algo acontece, como um clique
em “Salvar usuário” (através de event bindings do Angular, e.g.,
(click)="salvarUsuario()" chamando um método no componente).

• Para completar o trio, dentro da feature de usuários teremos um serviço por exemplo
usuario.service.ts . Este arquivo define uma classe de serviço Angular (marcada com
@Injectable ) que encapsula a lógica de negócio e operações de dados referentes a usuários.
Em termos de MVC, esse serviço cumpre o papel do Modelo. Suas responsabilidades podem
incluir: buscar a lista de usuários do servidor (fazendo uma requisição HTTP a algum endpoint
de API), enviar os dados de um novo usuário para serem salvos, atualizar informações de um
usuário existente ou deletar um usuário. O serviço normalmente não sabe nada de interface; ele
apenas sabe que, para obter usuários, talvez precise fazer uma chamada via HTTP client a
GET /api/usuarios , ou que para salvar um usuário novo precisa fazer um POST com os
dados do usuário. Além disso, o serviço pode aplicar alguma lógica adicional, como ordenar os
usuários por nome após recebê-los, filtrar os que estão ativos, ou outras regras de negócio
pertinentes. O importante é que o componente (nosso “Controlador” da interface de usuário)
utiliza esse serviço para delegar qualquer tarefa relacionada a dados. Assim, se amanhã
mudarmos a API ou adicionarmos uma validação extra, alteramos o serviço – o componente não
precisa mudar, pois ele continua chamando usuarioService.criarUsuario(dados) sem se
preocupar com o que acontece por baixo dos panos.

• Muitas vezes, na feature de usuário também definimos um modelo de dados próprio, por
exemplo um arquivo usuario.model.ts ou usuario.interface.ts . Nesse arquivo,
poderíamos definir uma interface TypeScript Usuario que descreve as propriedades de um
usuário (id, nome, email, etc.). Isso não é algo que existe em tempo de execução – é mais uma
convenção de desenvolvimento para termos tipos fortes – mas serve para esclarecer que até
mesmo no front-end temos a noção de um modelo de dados. Essa interface pode ser utilizada
tanto pelo serviço quanto pelo componente para tipar variáveis e garantir consistência (por
exemplo, o serviço retorna um array de Usuario , e o componente sabe que esperar um
Usuario com campos específicos). Conceitualmente, é similar a definir um schema no banco
de dados ou um modelo no back-end, só que no front-end isso é apenas um contrato de código,
não uma entidade persistente.

Com esses três elementos – componente (controlador), template (visão) e serviço (modelo) – a feature
de usuários está estruturada de forma modular. Todos os arquivos relevantes à funcionalidade de
usuário estão confinados naquela pasta. Isso traz grandes vantagens. Primeiramente, facilita o
desenvolvimento e a manutenção: se alguém precisar mexer em algo relativo a usuários, sabe
exatamente onde procurar – não precisa caçar em pastas separadas espalhadas pelo projeto. Em
segundo lugar, essa organização é amiga do crescimento e da performance. O Angular permite que
cada feature seja carregada separadamente, através de módulos de funcionalidade e lazy loading
(carregamento sob demanda). Ou seja, o código da feature de usuários pode ser carregado apenas
quando o usuário realmente acessar a parte de usuários do sistema. Assim, se o usuário nunca visitar a
seção de relatórios, por exemplo, o código de relatórios nunca é baixado pelo navegador, mantendo a
aplicação inicial mais leve e rápida. Esse carregamento tardio só é possível graças à clara divisão de
módulos e responsabilidades – cada feature age quase como um mini-sistema MVC autônomo, que
pode ser plugado ou desplugado conforme a necessidade.

É interessante notar que frameworks front-end como React ou Vue também pregam princípios similares
de componibilidade e separação de responsabilidades, embora implementem de maneiras distintas

9
(React, por exemplo, costuma misturar markup e lógica em componentes, mas a ideia de dividir por
componentes reutilizáveis e estados gerenciados tem parentesco com MVC no sentido de isolar
preocupações). O Angular, em particular, frequentemente é referenciado como um “MVC no front-end”,
mas como vimos, na prática ele adota uma arquitetura baseada em componentes e módulos de
funcionalidade. Ainda assim, o espírito do MVC está lá: dados e lógica nos serviços (modelo),
apresentação nos templates (visão), coordenação nos componentes (controlador). Apenas a maneira de
organizar fisicamente os arquivos é que muda para atender às demandas de aplicações grandes.

Capítulo 6: O Essencial a Ser Lembrado


Ao longo desta conversa, navegamos pelos conceitos fundamentais do padrão MVC e vimos como eles
se aplicam tanto no back-end quanto no front-end do mundo JavaScript. Entendemos a
responsabilidade de cada parte – Modelo, Visão e Controlador – e acompanhamos, com um exemplo
prático, o fluxo de uma interação típica desde a interface do usuário até as entranhas do servidor e de
volta. Também exploramos como esse padrão é colocado em prática em dois contextos diferentes: em
um servidor Node.js usando Express (com sua organização em pastas de modelos, visões e
controladores, além de rotas e serviços), e em um aplicativo front-end com Angular (que agrupa
componentes, templates e serviços por funcionalidade, mantendo a separação conceitual do MVC de
forma adaptada).

A principal lição que fica é que MVC não é uma receita engessada, e sim uma filosofia de
organização de código. A essência do MVC é a separação clara de responsabilidades: dados e regras
de negócio de um lado, interface e experiência do usuário de outro, e entre eles uma camada que faz a
mediação e coordenação. Essa essência permanece valiosa independentemente da tecnologia ou do
framework específico. A forma exata de implementar essa separação pode – e deve – variar conforme a
natureza do projeto. Em um back-end monolítico, talvez a clássica divisão em pastas models, views e
controllers funcione perfeitamente. Já em um front-end sofisticado, uma abordagem por componentes
ou por módulos de funcionalidade fará mais sentido. E não há problema nenhum nisso, pois o objetivo
final é o mesmo: construir um sistema em que cada peça tenha seu propósito bem definido.

Quando você compreende padrões de arquitetura como o MVC, você ganha ferramentas mentais para
lidar com a complexidade. Projetos de software tendem a crescer e se modificar ao longo do tempo, e
um código organizado arquiteturalmente suporta muito melhor essas mudanças. Com MVC, fica mais
fácil localizar onde fazer uma alteração, fica mais seguro adicionar novas funcionalidades sem quebrar
outras, e o trabalho em equipe flui com menos conflitos, já que cada desenvolvedor pode atuar em
camadas ou módulos diferentes sem pisar no código alheio.

Em resumo, aplicar MVC (ou arquiteturas semelhantes baseadas em separar responsabilidades) resulta
em código mais limpo, mais manutenível e mais escalável. Um desenvolvedor que internaliza esses
conceitos consegue evoluir de alguém que apenas escreve código que funciona para alguém que
projeta soluções elegantes e robustas. Então, ao estudar frameworks JavaScript – seja um Express da
vida, um Angular, ou qualquer outro que siga ou adapte o MVC – preste atenção não só no “como fazer
funcionar”, mas também no “como organizar”. Essa preocupação com arquitetura pode no início
parecer trabalho extra, mas acredite: ela é o que diferencia uma aplicação trivial de um sistema
preparado para crescer. E agora que você conhece a arte da organização por trás do MVC em JavaScript,
está mais bem equipado para construir aplicações não apenas funcionais, mas estruturadas e prontas
para encarar o futuro.

10
Express.js e o Padrão MVC no Desenvolvimento
Web
Introdução e Contexto Geral
O Express.js é um framework minimalista para Node.js muito popular no desenvolvimento de
aplicações web. Por ser não opinativo, ele não impõe uma estrutura fixa de pastas ou ferramentas
específicas ao desenvolvedor 1 . Isso significa que, apesar de sua simplicidade, podemos organizá-lo
seguindo boas práticas arquiteturais para construir sistemas escaláveis e de fácil manutenção. Uma
dessas práticas é adotar o padrão de arquitetura MVC (Model-View-Controller), amplamente utilizado
por sua capacidade de separar a lógica de negócios, a apresentação dos dados e o controle do
fluxo da aplicação 2 .

Em outras palavras, o MVC nos força a dividir a aplicação em camadas distintas, cada uma com sua
responsabilidade clara. No contexto do Express, mesmo ele sendo um framework enxuto, podemos
implementar o MVC de forma eficiente para obter uma estrutura organizada e escalável 2 . A seguir,
explicaremos detalhadamente cada camada do MVC, como o Express permite estruturá-las em
diretórios e arquivos, e como integrar recursos essenciais como roteamento, bancos de dados e
motores de template. A linguagem será simples e didática – imagine uma explicação sendo narrada,
passo a passo, quase como um audiobook técnico.

Conceitos do Padrão MVC (Model-View-Controller)


Antes de entrarmos na estrutura de um projeto Express, é importante compreender o papel de cada
componente do MVC:

• Modelo (Model): responsável pela lógica de negócios e pela gestão dos dados da aplicação. O
model representa (e geralmente interage com o banco de dados), contendo as funções de
acesso, consulta e manipulação de dados. Em resumo, ele define como os dados são
armazenados e manipulados e fornece esses dados para a camada de visão quando necessário
3 .

• Visão (View): é a camada encarregada da apresentação dos dados ao usuário. Aqui ficam os
templates ou páginas que o usuário final vê (HTML gerado dinamicamente, por exemplo). A view
não acessa os dados diretamente; em vez disso, recebe as informações prontas do modelo
(por intermédio do controlador) e gera a interface gráfica ou resposta HTML a ser exibida no
navegador 4 .
• Controlador (Controller): atua como um intermediário entre a View e o Model. O controller
processa as requisições HTTP recebidas, decide qual lógica de negócio aplicar (chamando os
métodos apropriados dos models) e determina qual visão deve ser renderizada como resposta
para o cliente 5 . Em analogia simples, pense no controller como um garçom: ele recebe o
pedido do cliente (requisição), encaminha o pedido à cozinha (modelo/banco de dados) e
retorna com o prato pronto (resposta renderizada pela visão).

Esse design promove a separação de responsabilidades. Cada camada tem um propósito bem
definido, o que facilita a manutenção e evolução do sistema. Por exemplo, podemos alterar a forma de
armazenamento de dados (Model) sem modificar a interface (View), ou atualizar o layout HTML (View)

1
sem tocar na lógica de negócios (Model), contanto que a interface entre eles (via Controller) se
mantenha consistente 2 .

Fluxo de Requisições no MVC

Para deixar bem claro, vamos resumir em passos o fluxo de uma requisição dentro de uma aplicação
Express estruturada em MVC:

1. Requisição do Cliente: O usuário (cliente) faz uma requisição HTTP (por exemplo, acessando
uma URL ou enviando um formulário).
2. Roteamento/Controller: O Express recebe essa requisição e, por meio do sistema de rotas, a
direciona para uma função controladora específica que lida com aquele caminho/ação. O
roteador do Express (possivelmente configurado via express.Router ) determina qual
controller deve tratar a URL solicitada.
3. Lógica de Negócio (Model): O controller, ao ser executado, interage com os models para obter
ou manipular os dados necessários. Nesta etapa, podem ocorrer consultas ao banco de dados,
cálculos ou regras de negócio, todas implementadas na camada de modelo.
4. Seleção da Visão: Com os dados prontos (por exemplo, um conjunto de resultados de uma
consulta), o controller então escolhe qual view (template) deve ser utilizada para apresentar
aqueles dados. Ele passa os dados para essa visão.
5. Resposta ao Cliente (View Renderizada): O motor de template preenche o template da view
com os dados fornecidos e gera um conteúdo (HTML, JSON, etc.) que o Express envia de volta
como resposta ao cliente. Assim, o usuário vê no navegador a página resultante daquela
requisição, já com os dados aplicados.

Seguindo esse fluxo, percebemos que a View nunca acessa o banco de dados diretamente, e o Model
não sabe nada sobre como os dados serão apresentados ao usuário. Essa separação torna a aplicação
mais modular e fácil de testar: podemos testar as funções de Model independentemente das rotas ou
da interface, por exemplo.

Estrutura de Diretórios e Arquivos em um Projeto Express MVC


Como mencionado, o Express não impõe um formato de projeto, mas a comunidade converge para
certas convenções ao usar MVC. Uma aplicação Express bem organizada geralmente apresenta uma
estrutura de diretórios semelhante a esta:

meu-projeto/
├── controllers/
├── models/
├── views/
├── routes/
├── public/
├── app.js (ou index.js)
└── package.json

2
Vamos entender o papel de cada item nessa estrutura (lembrando que detalhes podem variar de
projeto para projeto):

• controllers/ – contém os controladores da aplicação, ou seja, módulos (arquivos .js) que


exportam funções para cada ação/requisição importante. Cada controller agrupa lógicas
relacionadas a um recurso ou funcionalidade. Por exemplo, pode haver um
usuarioController.js lidando com registro/login de usuários, contendo métodos como
register(req, res) , login(req, res) , etc. Esses métodos recebem os objetos req e
res do Express, interagem com os models e invocam a resposta apropriada (por exemplo,
renderizar uma view ou retornar um JSON).
• models/ – abriga os modelos da aplicação, representando as entidades de domínio e a lógica de
acesso aos dados. Aqui definimos estruturas de dados e funções que manipulam esses dados.
Se há um banco de dados por trás, este diretório contém definições de esquemas e métodos
usando, por exemplo, um ORM/ODM. Em suma, aqui ficam as funções responsáveis por criar, ler,
atualizar e deletar dados (operações CRUD), isolando o código de banco de dados do resto da
aplicação 3 .
• views/ – pasta das views (visões), onde residem os templates ou páginas que serão renderizadas
e enviadas ao cliente. Podem ser arquivos Handlebars (.hbs), EJS (.ejs), Pug (.pug) ou outra
engine de template de sua preferência. A organização interna pode variar (às vezes subdividida
por seções da aplicação), mas em geral cada view corresponde a uma tela ou parte da interface
que o usuário vê. A camada de visão recebe os dados dos controllers e os insere nesses
templates para gerar o HTML final 4 .
• routes/ – (opcional, porém muito comum) diretório para definir as rotas da aplicação de forma
modular. Em projetos maiores, em vez de declarar todas as rotas no arquivo principal, criamos
arquivos separados em routes/ – por exemplo, usuarioRoutes.js – que usam o
express.Router() do Express. Cada arquivo de rota mapeia URLs específicas para funções
do controlador correspondente (por exemplo, router.get('/login',
usuarioController.formLogin) ). Depois, no arquivo principal ou em um arquivo de
carregamento de rotas, conectamos esses routers modulares à aplicação (ex.: app.use('/
usuarios', require('./routes/usuarioRoutes')) ). Essa separação organiza melhor as
definições de endpoints, agrupando-as por contexto (usuários, produtos, etc.) 6 .
• public/ – diretório para arquivos estáticos (como CSS, imagens, scripts do lado do cliente). O
Express pode ser configurado para servir automaticamente qualquer arquivo colocado aqui. Por
convenção, usamos app.use(express.static('public')) para tornar acessível tudo que
estiver nessa pasta. Assim, recursos estáticos referenciados nas views (como <link
rel="stylesheet" href="/style.css"> ) serão entregues diretamente pelo servidor 7 .
• app.js (ou index.js ou server.js): arquivo principal que inicializa a aplicação Express. Nele,
criamos a instância do Express ( const app = express() ), conectamos os middleware
básicos e as rotas, configuramos detalhes como porta do servidor, engine de view e conexão
com banco de dados. Em projetos pequenos, costuma-se usar um único arquivo (app.js ou
index.js) tanto para configurar o app quanto para ligá-lo (app.listen). Já em projetos mais
complexos, é comum separar a responsabilidade: por exemplo, ter um app.js que configura o
Express (exportando app ) e um server.js que importa app e apenas inicia o servidor na
porta especificada 8 9 . Essa separação facilita testes (você pode importar o app em outros
módulos sem já levantar o servidor) e organização.

Essa estrutura não é rígida – como dito, o Express nos deixa livres para organizar “à nossa maneira” 1 .
Entretanto, seguir esse esqueleto ajuda a manter o código arrumado. O próprio Express Application
Generator (ferramenta oficial de geração de projeto Express) sugere algo parecido: ele cria pastas
como routes/, views/ e public/, e configura o view engine padrão (por exemplo, Pug) para você 10 . Ou

3
seja, mesmo um framework minimalista pode ser usado dentro de uma organização MVC bem definida
pelo desenvolvedor.

Configuração do Servidor Principal (app.js ou index.js)


No coração da aplicação Express está o arquivo principal de inicialização – chamaremos aqui de app.js
(mas poderia ser index.js, conforme preferência). Esse arquivo desempenha vários papéis importantes:

• Inicialização do Express: geralmente começa importando o módulo Express e criando a


instância do aplicativo: const express = require('express'); const app =
express(); . A partir desse objeto app , controlamos todo o servidor 11 .
• Middlewares Globais: em seguida, configuram-se middlewares. Por exemplo, usar
app.use(express.json()) e app.use(express.urlencoded({ extended: true }))
para habilitar o parsing de JSON e formulários via body-parser (que no Express 4 já está
incorporado). Middlewares são funções que interceptam as requisições antes dos controllers;
aqui podemos registrar também logs de requisição, configuração de CORS, tratamento de
sessões, autenticação, etc., conforme a necessidade.
• Configuração do View Engine: se a aplicação renderiza páginas dinâmicas, definimos o
mecanismo de template a ser utilizado. Por exemplo, para usar EJS como engine de visão,
fazemos app.set('view engine', 'ejs') . Similarmente, para Handlebars seria algo como
configurar o Express-Handlebars, e para Pug usar app.set('view engine', 'pug') .
Também definimos onde os templates estão localizados: por padrão, Express espera uma pasta
chamada views/ na raiz do projeto, mas podemos alterar com app.set('views',
caminho) . No geral, o gerador do Express define views como "./views"
automaticamente e escolhe um motor (Pug por padrão), mas podemos usar Handlebars, EJS,
Pug ou outros – basta instalar o pacote correspondente (por exemplo, npm install ejs ) e
setar o view engine 10 12 . A engine de template permitirá que chamemos
res.render('nome_da_view', dados) nos controllers para enviar HTML dinâmico ao
cliente.
• Definição de Rotas: após configurar os básicos, registramos as rotas da aplicação. Se estamos
usando arquivos separados de roteamento (como em routes/), aqui importamos e acoplamos
cada roteador ao app principal. Exemplo:

const userRoutes = require('./routes/usuarioRoutes');


app.use('/usuarios', userRoutes);

Isso faria com que todas as rotas definidas em usuarioRoutes.js respondam com prefixo /
usuarios . Caso a aplicação seja pequena, as rotas poderiam ser definidas diretamente no
app.js usando app.get(...) , app.post(...) etc., mas separar em módulos melhora a
organização. No fim do arquivo, também podemos configurar uma rota “catch-all” para tratar
URLs não encontradas (404) ou erros gerais.
• Conexão com Banco de Dados: é comum que o app.js também inicie a conexão com o banco de
dados ou carregue os models. Por exemplo, se usamos o Sequelize (ORM para SQL), podemos
importar o arquivo de configuração/ inicialização dele e sincronizar os models. Se usamos
Mongoose (ODM para MongoDB), podemos chamar mongoose.connect(URL) aqui para
estabelecer a conexão antes de começar a escutar requisições. Essa etapa garante que, quando
um controller chamar o Model, a conexão já esteja ativa. Alternativamente, a lógica de conexão
pode ficar em outro módulo (ex.: em /models ou numa pasta config/ separada) que é
importado pelo app.js.

4
• Inicialização do Servidor (Listen): finalmente, app.js (ou mais comumente, o arquivo server.js)
manda o servidor escutar uma porta: app.listen(port, () => console.log("Servidor
rodando...")) . No Express Generator, essa parte fica em bin/www ou em um server.js ,
mas nada impede de estar no próprio index.js. Separar o “listen” em arquivo próprio (como
server.js ) é, como mencionado, útil para testes e para evitar que ao importar o app em
outro contexto ele já inicie o servidor. De qualquer modo, é aqui que definimos a porta (pode vir
de uma variável de ambiente) e iniciamos de fato o serviço web Express.

Em suma, o arquivo principal configura tudo que é global na aplicação Express – do middleware às
views, das rotas à conexão com banco – garantindo que as camadas definidas (MVC) vão funcionar em
conjunto. No exemplo do nosso projeto de tarefas simples, o app.js foi responsável por iniciar o
servidor e configurar as rotas da aplicação 13 . Em projetos maiores, o app.js exporta o app
configurado, e um server.js cuida apenas do app.listen , como vimos na estrutura acima 8

9 .

Rotas e Roteadores no Express


Um dos pontos fortes do Express.js é o sistema de roteamento. Rotas definem como o aplicativo
responde a determinadas requisições HTTP em endpoints específicos. Por exemplo, podemos querer
que uma requisição GET em /clientes liste clientes, ou um POST em /clientes crie um novo.

No contexto MVC com Express, costuma-se utilizar o express.Router para modularizar essas
definições. Cada conjunto de rotas relacionado pode virar um módulo separado. Por exemplo,
poderíamos ter:

• routes/clienteRoutes.js cuidando de todas as URLs começando com /clientes .


• routes/produtoRoutes.js para URLs de /produtos .
• ...e assim por diante.

Dentro de cada arquivo de rota, usamos o Router do Express assim:

const express = require('express');


const router = express.Router();
const clienteController = require('../controllers/clienteController');

// Definindo rotas relativas a "clientes":


router.get('/', clienteController.listarClientes); // Lista todos os
clientes
router.get('/:id', clienteController.verCliente); // Exibe detalhes de
um cliente
router.post('/', clienteController.criarCliente); // Cria um novo
cliente
router.put('/:id', clienteController.atualizarCliente); // Atualiza cliente
existente
router.delete('/:id', clienteController.removerCliente);// Remove um cliente

module.exports = router;

5
No arquivo principal, montaríamos esse roteador: app.use('/clientes', require('./routes/
clienteRoutes')); . Assim, por exemplo, uma requisição GET em /clientes/123 chamaria o
método verCliente do controller de clientes, passando o id capturado.

Por que usar roteadores? Porque eles facilitam a modularização e a leitura do código. Em vez de um
único arquivo enorme com todas as rotas da aplicação, dividimos por funcionalidade. Além disso,
podemos aplicar middlewares específicos por módulo de rota (por exemplo, exigir autenticação para
todas as rotas de /clientes facilmente, aplicando um middleware no router).

No Express Generator e em muitos tutoriais, você verá um arquivo routes/index.js ou similar, mas
note que isso não é obrigatório – é apenas uma convenção de separar as rotas. O importante é que, de
uma forma ou de outra, nossas rotas no Express irão mapear URLs para funções de controller, que
por sua vez usarão os models. Essa é a cola que une a requisição HTTP à arquitetura MVC.

Para fechar esse assunto, vale lembrar: no Express, uma rota pode tanto renderizar uma view (usando
res.render dentro do controller, para páginas HTML) quanto retornar dados JSON (usando
res.json ), dependendo do tipo de aplicação (tradicional web ou API REST, por exemplo). Mas a
estrutura MVC continua válida para ambos os casos – em uma API REST, por exemplo, talvez a pasta
views/ nem seja utilizada, mas ainda temos controllers e models separando responsabilidades.

Integração com Bancos de Dados na Camada Model


O Express.js, por si só, não vem com nenhuma camada de acesso a dados embutida. Ele deixa
totalmente a cargo do desenvolvedor escolher como integrar um banco de dados, conectando
quaisquer bibliotecas ou ORMs necessários 14 . Assim, na arquitetura MVC a parte de Model
geralmente envolve o uso de ferramentas adicionais para lidar com persistência de dados, tais como:

• Sequelize: um ORM (Object-Relational Mapper) para bancos de dados SQL (MySQL, PostgreSQL,
SQLite, etc.). Com o Sequelize, definimos modelos JavaScript que correspondem a tabelas no
banco de dados, incluindo colunas (atributos) e relações. Esses modelos fornecem métodos
como findAll , create , etc., facilitando operações no banco sem escrever SQL diretamente.
Por exemplo, poderíamos ter um UsuarioModel definido pelo Sequelize, e então no controller
chamar UsuarioModel.create(novosDados) para inserir um usuário. O Sequelize também
suporta migrations, validações de dados e outras conveniências. Em um projeto Express MVC,
arquivos de model usando Sequelize ficam na pasta models/ e a inicialização da conexão (url do
banco, credenciais) pode ficar num arquivo de configuração (às vezes config/database.js ou
mesmo no app.js). A ferramenta CLI do Sequelize até cria uma estrutura própria, com pastas
para models, migrations e seeders – que podem ser integradas ao seu projeto Express
facilmente 15 16 .
• Mongoose: uma biblioteca ODM (Object Data Mapper) popular para MongoDB. O Mongoose
permite definir Schemas que modelam coleções do MongoDB, incluindo tipos de campo e
validações. A interação com o banco é feita através dos modelos Mongoose (por exemplo,
User.findOne({ email: '[email protected]' }) ). Em projetos Express MVC usando Mongo, os
arquivos de model (por ex. User.js ) definem o schema Mongoose e exportam o modelo. A
conexão a uma instância Mongo (com mongoose.connect ) pode ser feita no início da
aplicação. Mongoose encapsula a lógica de banco de dados, fornecendo métodos prontos para
operações CRUD, mantendo a sintaxe do código JavaScript consistente.
• Drivers SQL direto ou outras ORMs: Em alguns casos, pode-se usar drivers diretos (como o
pacote pg para PostgreSQL ou mysql2 para MySQL) e escrever consultas SQL manualmente
dentro dos models. Essa abordagem dá mais controle, mas menos abstração. Ainda assim, a

6
organização MVC se mantém: você teria funções no model que executam certas queries e
retornam os resultados para o controller. Uma variante comum é usar o Knex.js (um query
builder SQL para Node) para facilitar escrever SQL em JavaScript.

Independentemente da tecnologia exata, o ponto é: a camada Model deve isolar a interação com o
banco de dados. Assim, se amanhã decidirmos trocar MySQL por MongoDB, ou alterar a estrutura das
tabelas, em teoria só precisaríamos modificar os models (e talvez configurações), sem impactar as rotas
ou controladores – desde que a interface fornecida pelos modelos continue a mesma.

Express nos dá essa liberdade total: para integrá-lo a um banco, basta carregar o driver ou biblioteca
adequada no projeto 14 . Por exemplo, para MySQL via Sequelize instalamos npm install
sequelize mysql2 e configuramos; para MongoDB via Mongoose, npm install mongoose e
assim por diante. Uma vez configurado, usamos os models dentro dos controllers. Retomando o
exemplo de um controller de usuário: ele não faria SELECT * FROM usuarios diretamente; ao invés
disso, chamaria algo como UsuarioModel.findAll() ou Usuario.find() – e internamente esse
método do model usará o ORM/ODM para falar com o banco. Isso melhora a segurança e manutenção,
pois evita SQL espalhado pelo código e mantém regras de negócio centralizadas nos models.

Além disso, frameworks ORM geralmente permitem definir validações e regras de negócio dentro
dos modelos. Por exemplo, no Sequelize podemos marcar um campo como obrigatório ou único, e
adicionar métodos utilitários. O desenvolvedor Lucas V sugere em seu guia que podemos incluir
validações de dados diretamente no modelo usando Sequelize, garantindo integridade dos dados já
na camada de model (e não apenas no banco) 17 . Essa é uma boa prática: a model layer não apenas
fala com o banco, mas também assegura que os dados façam sentido (por exemplo, não permitir e-
mails duplicados, campos obrigatórios não nulos etc.). Em Mongoose também temos esquemas com
validações (ex: required: true , match: /regex/ para e-mail, etc.).

Resumindo, a integração com bancos de dados no Express MVC se dá por meio da camada Model, e
normalmente utilizamos bibliotecas especializadas (ORM/ODM) para facilitar. O Express não fornece
isso pronto, mas permite usar qualquer mecanismo de banco de dados suportado pelo Node –
essa flexibilidade é poderosa, embora exija do desenvolvedor a escolha das ferramentas certas 14 .

Camada de Visão e Motores de Template


Para aplicativos que renderizam páginas web no servidor (Server-Side Rendering), a camada de Visão
(View) é fundamental. No Express, implementar a view geralmente envolve usar um motor de
template (template engine). Esses motores permitem escrever páginas HTML com placeholders para
dados dinâmicos, que serão preenchidos quando a página for renderizada.

Alguns motores de template populares no ecossistema Express são:

• Pug (antigo Jade): usa uma sintaxe enxuta, baseada em indentação, para representar HTML.
Exemplo: em vez de escrever <ul><li>Item</li></ul> em HTML, num template Pug
escreveríamos algo como:

ul
each item in lista
li= item.nome

7
O Pug é o default do Express Generator, indicando sua popularidade e integração fluida com
Express 10 .
• EJS (Embedded JavaScript): adiciona código JavaScript dentro de templates HTML usando
sintaxe <% %> . É muito semelhante a misturar HTML com pequenos trechos de JS para inserir
valores ou executar loops. Por exemplo, <%= user.name %> insere o nome do usuário, e <%
users.forEach(...) { %> ... <% } %> permite iterar sobre uma lista. É simples para
quem está acostumado com HTML, pois a maior parte do arquivo é HTML puro com algumas
marcas.
• Handlebars (HBS)/Mustache: utilizam sintaxe de chaves duplas, tipo {{variable}} , para
inserir valores, e possuem recursos de loops e condicionais também ( {{#each}} ... {{/
each}} , etc.). O Handlebars se destaca por separar bem a lógica da apresentação, pois dentro
do template não se escreve JavaScript diretamente, apenas expressões permitidas pelo motor. O
Express suporta Handlebars via alguns pacotes como express-handlebars .
• Outros: existem muitos outros (Mustache, EJS e Handlebars derivam do Mustache
conceitualmente; Nunjucks, e até adaptadores para usar motores não originalmente de Node).

Usar um desses motores no Express requer poucos passos, como já citado: instalar o pacote do motor
( npm install ejs por exemplo) e configurar app.set('view engine', 'ejs') . A partir daí, ao
chamar res.render('pagina', { dados }) , o Express procura um arquivo pagina.ejs (note:
não precisamos pôr a extensão se o view engine está configurado) dentro do diretório de views, e o
renderiza passando os { dados } para dentro do template 18 19 . O resultado (HTML final) é
enviado ao cliente.

No exemplo simples do Medium que criamos uma lista de tarefas, o desenvolvedor optou por usar a
engine EJS 20 . Ele instalou o pacote ejs e configurou o Express para usar essa engine. Com isso, nas
controllers ele pôde chamar res.render('index', { tarefas }) e ter o arquivo views/index.ejs
renderizado com a lista de tarefas fornecida 21 . Da mesma forma, uma rota de adicionar tarefa
renderizava uma página adicionarTarefa.ejs contendo um formulário. Esse fluxo ilustra bem a
interação Controller -> View: a função controladora prepara os dados (nesse caso, obtém a lista de
tarefas do model) e invoca a view correspondente passando os dados.

Cada motor de template tem sua sintaxe específica, mas todos cumprem o mesmo papel: pegar um
arquivo modelo (template) estático e preenchê-lo com valores dinâmicos no momento da
requisição, produzindo HTML. Isso torna muito mais fácil elaborar páginas complexas sem concatenar
strings manualmente ou imprimir valores na mão. Inclusive, podemos reutilizar componentes (parciais),
aplicar layouts base, etc., dependendo do motor.

Dica: Mesmo em aplicações que usam front-end separado (SPA com React/Angular, por
exemplo) ou APIs JSON, às vezes o uso de alguma view engine simples no Express é útil
para renderizar páginas de erro customizadas, ou emails, etc. Mas se sua aplicação não
renderiza HTML no servidor (por ser uma API pura ou usar SSR de outro jeito), você
pode ignorar o diretório views/ e o mecanismo de templates, mantendo ainda o MVC
para organizar controllers e models.

Em conclusão desta parte, o Express integra-se facilmente com motores de template populares. Cabe a
nós escolhermos o preferido – Pug, Handlebars, EJS e muitos outros são compatíveis 10 – e estruturar
a pasta views/ de forma organizada (muitas vezes criando subpastas por seção da aplicação, e.g.,
views/usuarios/list.ejs , views/usuarios/detail.ejs , etc., e assim por diante). Isso
completa a camada View do nosso MVC, fechando o ciclo onde controllers pedem ao model dados e
depois pedem à view para exibi-los.

8
Boas Práticas de Modularização e Manutenção com Express
Mesmo sendo minimalista, o Express.js permite (na verdade, exige) que a gente adote boas práticas de
organização para montar uma aplicação robusta. Vamos destacar algumas boas práticas ao usar
Express no padrão MVC:

• Separação de Responsabilidades: Esse é o princípio básico do MVC – cada camada faz uma
coisa. Leve isso a sério no código: não coloque lógica de banco de dados dentro de funções de
controller; não tente renderizar view de dentro do model. Mantenha cada trecho de código "no
seu lugar". Isso aumenta a coesão interna dos módulos e diminui o acoplamento entre eles,
facilitando alterações futuras.
• Controladores Enxutos: Tente deixar as funções dos controllers curtas e objetivas. O papel do
controller é orquestrar a ação, não executar toda a lógica. Se um controller está muito complexo,
considere mover parte da lógica para o model (se for regra de negócio relacionada a dados) ou
até para uma camada de serviço. Alguns projetos criam uma pasta services/ para colocar lógica
de negócio que não seja diretamente de acesso a dados, mas também não pertence à view –
isso evita inflar o controller. Por exemplo, um UsuarioService poderia encapsular a lógica de
cadastro (verificar se email já existe, criptografar senha) usando funções do model e ser
chamado pelo controller. No exemplo do Lucas, ele menciona justamente organizar com
serviços e validações nos modelos para manter a estrutura MVC limpa 22 .
• Modularize além do MVC, se necessário: O trio Model-View-Controller é o núcleo, mas não
significa que tudo deva ir em um desses três pastas. Para manter o código manutenível, pode-se
criar módulos auxiliares: a pasta routes/ já mencionada para organizar endpoints; pasta
middlewares/ para funções de middleware personalizadas (ex: verificação de autenticação,
logging); pasta config/ para configurações (por exemplo, configurações de banco de dados,
definição de variáveis de ambiente, etc.); pasta utils/ ou helpers para funções utilitárias (como
formatação de data, geração de token JWT, etc. – no exemplo do Lucas havia uma pasta utils
para isso 23 ). Essa estrutura adicional também segue o espírito de separar por
responsabilidade, tornando o projeto escalável.
• Organização por Domínio (Feature Modules): Uma abordagem alternativa à divisão estrita por
tipo MVC é dividir por funcionalidade. Por exemplo, uma pasta usuarios/ contendo
model.js , controller.js , routes.js específicos de usuários, outra produtos/ com
seus arquivos, etc. Isso é chamado de organização modular por domínio. Alguns preferem esse
estilo conforme a aplicação cresce muito, porque agrupa tudo relativo a uma funcionalidade
num só lugar. O Express permite isso igualmente. Independente da escolha (por tipo ou por
domínio), mantenha consistência no projeto inteiro.
• Uso de Código Reutilizável e Middlewares: Faça bom uso dos middlewares do Express para
fatores comuns de lógica que se repetiriam em múltiplos controllers. Exemplo: autenticação.
Em vez de checar em cada rota se o usuário está logado, crie um middleware autenticado e
aplique nas rotas que precisam proteção: app.use('/admin', autenticado,
adminRouter) . Assim, os controllers podem assumir que se executaram é porque o
middleware já garantiu as precondições.
• Arquivo de Variáveis de Ambiente: Note que no snippet da estrutura acima há um arquivo
.env listado 24 . É boa prática usar variáveis de ambiente para dados sensíveis ou
dependentes de ambiente (como strings de conexão com banco, portas, chaves secretas). Em
desenvolvimento, um pacote como dotenv pode carregar o .env . Isso torna sua aplicação
mais configurável e segura (evita deixar senhas hardcoded no código).
• Tratamento de Erros Centralizado: Para manter a confiabilidade, implemente um tratamento
global de erros. Você pode ter um middleware especial no Express para capturar erros não
tratados nos controllers (basta definir um middleware com 4 parâmetros,

9
function(err, req, res, next) – o Express o reconhece como handler de erro). Isso evita
duplicar lógica de erro em cada rota e garante uma resposta consistente em caso de falhas.
• Documentação e Consistência: Mantenha seu código consistente. Nomeie arquivos e funções
de maneira previsível (se você tem AlgoController.js , provavelmente as rotas
correspondentes estarão em algoRoutes.js e o model em Algo.js ou similar). Comente
partes complexas e documente como as camadas interagem. Isso é importante especialmente
num framework flexível como Express, onde as convenções são decididas pela equipe de
desenvolvimento.

Seguindo essas práticas, mesmo aplicações Express grandes permanecem compreensíveis. É comum
ouvir que “Express não escala” ou “Para projetos grandes é bagunçado”, mas isso não é verdade quando
usamos padrões sólidos. O Express, por ser enxuto, nos dá liberdade para arquitetar – se
aproveitarmos essa liberdade com sabedoria (MVC, modularização, etc.), conseguimos um projeto tão
escalável e manutenível quanto em frameworks maiores.

Para reforçar: o padrão MVC em conjunto com o Express ajuda a melhorar a qualidade do código e a
manutenibilidade da base 25 . A separação em camadas significa que várias pessoas podem trabalhar
simultaneamente no projeto sem pisar no código uma da outra (um desenvolvedor pode ajustar uma
view enquanto outro otimiza uma query no model, por exemplo). Além disso, testar componentes
isoladamente fica mais fácil – podemos testar funções do model sem envolver servidor web, ou testar
controllers simulando requisições e usando stubs para os models.

Conclusão
O Express.js, apesar de minimalista, é capaz de suportar aplicações de qualquer tamanho – tudo
depende de como organizamos o código. Aplicando o padrão MVC e outras boas práticas de
arquitetura, conseguimos montar uma estrutura em que cada peça da aplicação tem seu lugar
definido: os models cuidam dos dados e da integração com o banco (seja via Sequelize, Mongoose ou
outro driver) 14 , os controllers atuam como maestros coordenando cada requisição e resposta, e as
views apresentam dados de forma amigável ao usuário, muitas vezes com ajuda de motores de
template como Handlebars, EJS ou Pug.

Essa organização traz modularidade (podemos desenvolver e modificar partes da aplicação


isoladamente), reutilização (código bem dividido tende a ser mais reutilizável) e facilidade de
manutenção (encontrar e corrigir bugs fica mais simples quando sabemos onde procurar – um bug na
lógica de cálculo provavelmente está no model, um erro de link quebrado provavelmente está na view,
e assim por diante). Além disso, ao crescer a aplicação, podemos adicionar novas funcionalidades
criando novos controllers, novos models e novas views sem precisar reescrever a estrutura existente,
mantendo o projeto coeso.

Em resumo, usar Express.js com o padrão MVC é como construir uma casa com uma planta bem
definida: o Express fornece as fundações e a liberdade para escolher os materiais, e o MVC define como
os cômodos serão distribuídos. Seguindo essa abordagem, você terá um aplicativo organizado,
escalável e confiável – e o fato de o Express ser minimalista deixa de ser um obstáculo e se torna uma
vantagem, já que você molda a arquitetura conforme as necessidades do seu projeto, ao mesmo tempo
em que se beneficia da enorme quantidade de middleware e bibliotecas disponíveis para Express e
Node (autenticação, segurança, parsing de dados, etc.) 26 .

No fim das contas, Express + MVC é uma combinação poderosa: o framework lida com as requisições
HTTP e fornece os mecanismos básicos (roteamento, middleware, view engine, static files), enquanto o
padrão arquitetural garante que sua lógica de negócio, interface e controle de fluxo estejam

10
organizados de forma limpa. Assim, você pode desenvolver aplicações Node.js profissionais, fáceis de
entender, de manutenção simples e prontas para crescer junto com as demandas dos usuários 2 .

Referências Utilizadas:

• Habbema, Hugo – NodeJS: Padrão MVC (Desenvolvendo uma aplicação Node.js utilizando o padrão
MVC) 2 27 7
• MDN Web Docs – Introdução ao Express/Node (Express é não opinativo; integração com banco de
dados) 1 14
• Lucas V. – Criando uma Aplicação Node.js com Express e Sequelize (Estrutura MVC com serviços,
organização de pastas) 22 16
• Documentação Express.js – Usando Motores de Template com Express 10 (suporte a Pug,
Handlebars, EJS)
• Tutorial LogRocket – Building and structuring a Node.js MVC application (LogRocket, 2024) 28 25
(explicação dos benefícios do MVC e uso de ORM Sequelize com Express)

1 11 14 26 Introdução Express/Node
https://developer.mozilla.org/pt-BR/docs/Learn_web_development/Extensions/Server-side/Express_Nodejs/Introduction

2 3 4 5 7 12 13 20 21 27 NodeJS : Padrão MVC. Desenvolvendo uma Aplicação Node.js… | by


Hugo Habbema | Medium
https://medium.com/@habbema/nodejs-padr%C3%A3o-mvc-2f4e16b79cb8

6 8 9 15 16 17 22 23 24 Criando uma Aplicação Node.js com Express e Sequelize | by Lucas V |


Medium
https://medium.com/@lucas.lvtj/criando-uma-aplica%C3%A7%C3%A3o-node-js-com-express-e-sequelize-e1cd1003a791

10 18 Usando mecanismos de modelo com o Express


https://expressjs.com/pt-br/guide/using-template-engines.html

19 Using template engines with Express


https://expressjs.com/en/guide/using-template-engines.html

25 28 Building and structuring a Node.js MVC application - LogRocket Blog


https://blog.logrocket.com/building-structuring-node-js-mvc-application/

11

Você também pode gostar