> For the complete documentation index, see [llms.txt](https://salvatore-academy.gitbook.io/dev-fullstack-web/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://salvatore-academy.gitbook.io/dev-fullstack-web/academia/missoes-de-backend/organizacao-de-projeto-em-arquitetura-mvc.md).

# Organização de Projeto em Arquitetura MVC

{% hint style="info" %}
Dominar a estruturação de projetos backend através da arquitetura *Model-View-Controller* (MVC), uma abordagem padrão para organizar o código em componentes distintos de *model*, *view* e *controller*. Isso facilita a manutenção e a escalabilidade do projeto.
{% endhint %}

## Vídeo

{% embed url="<https://youtu.be/TGS1ViNOpVw>" %}

**Link direto:** <https://youtu.be/TGS1ViNOpVw>

## Tópicos

1. **Introdução ao MVC:**
   * Explicação teórica sobre a arquitetura MVC e seus benefícios.
   * Análise de como o MVC pode ser implementado em uma aplicação Node.js.
2. **Reestruturação do Projeto:**
   * Organização do código atual em *model*, *view* e *controllers*.
   * Refatoração das rotas para utilizar os *controllers*.
3. **Implementação Prática:**
   * Criação de um exemplo prático de CRUD utilizando a arquitetura MVC.

## Material de Apoio

### Repositório no GitHub

{% embed url="<https://github.com/SalvatoreAcademy/backend-missao-aprendizado-iniciante-organizacao-de-projeto-em-arquitetura-mvc>" %}

### PDF para Download

{% file src="/files/sKPuR5WvZdJou3S0wffs" %}

### Miro

{% embed url="<https://miro.com/app/board/uXjVNQ6PZjQ=/?moveToWidget=3458764587647670383&cot=14>" %}

## Exercícios de Fixação

{% hint style="info" %}
Use esses exercícios para reforçar o que acabou de aprender. Se errar, leia a explicação para entender melhor o conceito.
{% endhint %}

<details>

<summary>Qual é a principal responsabilidade de um Model na arquitetura MVC?</summary>

A) Gerenciar a interface do usuário.

B) Processar as solicitações do usuário.

C) Acessar e manipular os dados da aplicação.

D) Configurar as rotas da aplicação.

</details>

<details>

<summary>Resposta correta</summary>

**Qual é a principal responsabilidade de um Model na arquitetura MVC?**

**Resposta Correta**: C

**Feedback**: O Model é responsável por acessar e manipular os dados da aplicação, incluindo a lógica de negócios associada.

</details>

***

<details>

<summary>Complete o código para definir uma rota que utilize um controller na arquitetura MVC.</summary>

{% code title="src/produto/produto.router.js" lineNumbers="true" %}

```javascript
const express = require('express');
const router = express.Router();
const controller = require('./personagem.controller');

router.____('/', controller.readAll);
router.____('/:id', controller.readById);
router.____('/', controller.create);
router.____('/:id', controller.updateById);
router.____('/:id', controller.deleteById);

module.exports = router;
```

{% endcode %}

</details>

<details>

<summary>Resposta correta</summary>

**Complete o código para definir uma rota que utilize um controller na arquitetura MVC.**

**Resposta Correta**:

```javascript
router.get('/', controller.readAll);
router.get('/:id', controller.readById);
router.post('/', controller.create);
router.put('/:id', controller.updateById);
router.delete('/:id', controller.deleteById);
```

**Feedback**: As rotas são configuradas para utilizar os métodos apropriados do controller, seguindo a estrutura RESTful para operações CRUD.

</details>

***

<details>

<summary>Verdadeiro ou Falso: Na arquitetura MVC, o Controller é responsável por validar os dados recebidos da View.</summary>

A) Verdadeiro

B) Falso

</details>

<details>

<summary>Resposta correta</summary>

**Verdadeiro ou Falso: Na arquitetura MVC, o Controller é responsável por validar os dados recebidos da View.**

**Resposta Correta**: A

**Feedback**: Verdadeiro. O Controller é responsável por processar as solicitações do usuário, incluindo a validação dos dados recebidos e a interação com os Models.

</details>

***

<details>

<summary>Qual comando usamos para instalar o pacote dotenv para carregar variáveis de ambiente?</summary>

A) `npm install dotenv`

B) `npm install env`

C) `npm install dotenv-cli`

D) `npm install environment`

</details>

<details>

<summary>Resposta correta</summary>

**Qual comando usamos para instalar o pacote dotenv para carregar variáveis de ambiente?**

**Resposta Correta**: A

**Feedback**: O comando `npm install dotenv` é usado para instalar o pacote dotenv, que carrega variáveis de ambiente de um arquivo `.env` para `process.env`.

</details>

***

<details>

<summary>Qual é a principal vantagem de usar a arquitetura MVC em um projeto?</summary>

A) Reduzir o tempo de desenvolvimento.

B) Melhorar a organização e a separação de responsabilidades no código.

C) Facilitar a criação de interfaces gráficas.

D) Aumentar a velocidade de execução do código.

</details>

<details>

<summary>Resposta correta</summary>

**Qual é a principal vantagem de usar a arquitetura MVC em um projeto?**

**Resposta Correta**: B

**Feedback**: A arquitetura MVC melhora a organização e a separação de responsabilidades no código, facilitando a manutenção e a escalabilidade do projeto.

</details>

## Exercícios de Validação

{% hint style="info" %}
Esses exercícios testarão sua compreensão prática. Revise o feedback para melhorar suas habilidades.
{% endhint %}

### Desafio de Código

Fornecemos um arquivo `index.js` que contém um endpoint simples. Separe esse código em um projeto seguindo a arquitetura MVC, criando os arquivos necessários para organizar o projeto em Models, Views e Controllers.

#### Arquivo Inicial

{% code title="index.js" lineNumbers="true" %}

```javascript
const express = require('express');
const app = express();
app.use(express.json());

const produtos = [];

app.post('/produtos', (req, res) => {
  const novoProduto = req.body;

  if (!novoProduto.nome || !novoProduto.preco || !novoProduto.categoria) {
    return res.status(400).send('Corpo da requisição deve conter as propriedades nome, preço e categoria.');
  }

  produtos.push(novoProduto);
  res.status(201).send(novoProduto);
});

app.listen(3000, () => {
  console.log(`Servidor rodando em http://localhost:3000`);
});
```

{% endcode %}

#### Tarefas

1. **Configuração Inicial**:
   * Crie a estrutura de pastas conforme o padrão MVC.

     ```
     src
     ├── db
     │   └── database-connection.js
     ├── produto
     │   ├── produto.controller.js
     │   ├── produto.router.js
     │   └── produto.service.js
     ```
2. **Implementação dos Controllers**:
   * Mova a lógica de validação e criação do produto para o controller no arquivo `produto.controller.js`.
3. **Implementação dos Services**:
   * Mova a lógica de manipulação de dados para o service no arquivo `produto.service.js`.
4. **Configuração das Rotas**:
   * Configure as rotas no arquivo `produto.router.js` para utilizar os métodos do controller.
5. **Atualização do Servidor**:
   * Atualize o arquivo `index.js` para usar as rotas configuradas.

#### Código Esperado

{% code title="src/produto/produto.controller.js" lineNumbers="true" %}

```javascript
const service = require('./produto.service');

async function create(req, res) {
  const novoProduto = req.body;

  if (!novoProduto.nome || !novoProduto.preco || !novoProduto.categoria) {
    return res.status(400).send('Corpo da requisição deve conter as propriedades nome, preço e categoria.');
  }

  await service.create(novoProduto);
  res.status(201).send(novoProduto);
}

module.exports = {
  create
};
```

{% endcode %}

{% code title="src/produto/produto.service.js" lineNumbers="true" %}

```javascript
const produtos = [];

function create(novoProduto) {
  produtos.push(novoProduto);
}

module.exports = {
  create
};
```

{% endcode %}

{% code title="src/produto/produto.router.js" lineNumbers="true" %}

```javascript
const express = require('express');
const controller = require('./produto.controller');

const router = express.Router();

router.post('/', controller.create);

module.exports = router;
```

{% endcode %}

{% code title="index.js" lineNumbers="true" %}

```
```

{% endcode %}

```javascript
const express = require('express');
const produtoRouter = require('./src/produto/produto.router');
const app = express();
app.use(express.json());

app.use('/produtos', produtoRouter);

app.listen(3000, () => {
  console.log(`Servidor rodando em http://localhost:${port}`);
});
```

{%

endcode %}

**Feedback**: Certifique-se de que a lógica de validação está no controller e a lógica de manipulação de dados está no service. Configure as rotas corretamente para usar o controller.

### Revisão de Código

{% hint style="danger" %}
**Analise o código a seguir, encontre e corrija o erro relacionado à separação de responsabilidades na arquitetura MVC.**
{% endhint %}

{% code title="index.js" lineNumbers="true" %}

```javascript
const express = require('express');
const { MongoClient, ObjectId } = require('mongodb');
const app = express();
app.use(express.json());

const dbUrl = process.env.DATABASE_URL;
const client = new MongoClient(dbUrl);

async function main() {
  await client.connect();
  const db = client.db('app');
  const collection = db.collection('produtos');

  app.post("/produtos", async function (req, res) {
    const novoItem = req.body;

    if (!novoItem.nome || !novoItem.preco || !novoItem.categoria) {
      return res.status(400).send('Corpo da requisição deve conter as propriedades nome, preço e categoria.');
    }

    await collection.insertOne(novoItem);
    res.status(201).send(novoItem);
  });

  app.listen(3000, function () {
    console.log(`Aplicação rodando em http://localhost:3000`);
  });
}

main();
```

{% endcode %}

<details>

<summary>Resposta Correta</summary>

**Erros:**

1. A lógica de manipulação de dados deve ser movida para o service para manter a separação de responsabilidades.
2. O código de validação e criação de produtos deve ser movido para o controller.

{% code title="src/produto/produto.controller.js" lineNumbers="true" %}

```javascript
const service = require('./produto.service');

async function create(req, res) {
  const novoItem = req.body;

  if (!novoItem.nome || !novoItem.preco || !novoItem.categoria) {
    return res.status(400).send('Corpo da requisição deve conter as propriedades nome, preço e categoria.');
  }

  await service.create(novoItem);
  res.status(201).send(novoItem);
}

module.exports = {
  create
};
```

{% endcode %}

{% code title="src/produto/produto.service.js" lineNumbers="true" %}

```javascript
const { ObjectId } = require('mongodb');
const { getDatabase } = require('../db/database-connection');

function getCollection() {
  return getDatabase().collection('produtos');
}

function create(novoItem) {
  return getCollection().insertOne(novoItem);
}

module.exports = {
  create
};
```

{% endcode %}

{% code title="src/produto/produto.router.js" lineNumbers="true" %}

```javascript
const express = require('express');
const controller = require('./produto.controller');

const router = express.Router();

router.post('/', controller.create);

module.exports = router;
```

{% endcode %}

{% code title="index.js" lineNumbers="true" %}

```javascript
require('dotenv').config();
const express = require('express');
const { connectToDatabase } = require('./src/db/database-connection');
const produtoRouter = require('./src/produto/produto.router');

async function main() {
  await connectToDatabase();

  const app = express();
  app.use(express.json());

  app.use('/produtos', produtoRouter);

  const port = process.env.PORT || 3000;
  app.listen(port, function () {
    console.log(`Servidor rodando em http://localhost:${port}`);
  });
}

main();
```

{% endcode %}

**Feedback**: Certifique-se de que a lógica de manipulação de dados está no service e a lógica de controle está no controller para manter a separação de responsabilidades conforme a arquitetura MVC.

</details>

### Projeto Prático

Reestruture uma aplicação Node.js existente para seguir a arquitetura MVC. A aplicação deve gerenciar uma entidade "Produtos" com as propriedades "nome", "preço" e "categoria". Crie os arquivos necessários para organizar o projeto em Models, Views e Controllers, e implemente operações CRUD para a entidade "Produtos".

#### Tarefas

1. **Configuração Inicial**:
   * Inicialize um novo projeto NodeJS.
   * Instale o ExpressJS e o MongoDB Driver.
   * Configure o Nodemon para reiniciar o servidor automaticamente.
   * Crie um arquivo `.env` e adicione as variáveis de ambiente necessárias.
2. **Estrutura de Pastas**:
   * Crie a estrutura de pastas conforme o padrão MVC.

     ```
     src
     ├── db
     │   └── database-connection.js
     ├── produto
     │   ├── produto.controller.js
     │   ├── produto.router.js
     │   └── produto.service.js
     ```
3. **Conexão com o MongoDB**:
   * Configure a conexão com o MongoDB utilizando o MongoClient e as variáveis de ambiente no arquivo `database-connection.js`.
4. **Implementação dos Controllers**:
   * Implemente os métodos do controller para operações CRUD no arquivo `produto.controller.js`.
5. **Implementação dos Services**:
   * Implemente os métodos de acesso aos dados no arquivo `produto.service.js`.
6. **Configuração das Rotas**:
   * Configure as rotas no arquivo `produto.router.js` para utilizar os métodos do controller.
7. **Inicialização do Servidor**:
   * Configure a inicialização do servidor no arquivo `index.js` e adicione as rotas.

<details>

<summary>Código Esperado</summary>

{% code title="src/db/database-connection.js" lineNumbers="true" %}

```javascript
const { MongoClient } = require('mongodb');

const dbUrl = process.env.DATABASE_URL;
const dbName = 'mvc-produtos';

const client = new MongoClient(dbUrl);

async function connectToDatabase() {
  console.log('Conectando ao banco de dados...');
  await client.connect();
  console.log('Banco de dados conectado com sucesso!');
}

function getDatabase() {
  return client.db(dbName);
}

module.exports = {
  connectToDatabase,
  getDatabase
};
```

{% endcode %}

{% code title="src/produto/produto.controller.js" lineNumbers="true" %}

```javascript
const service = require('./produto.service');

async function readAll(req, res) {
  const items = await service.readAll();
  res.send(items);
}

async function readById(req, res) {
  const id = req.params.id;
  const item = await service.readById(id);

  if (!item) {
    return res.status(404).send('Produto não encontrado.');
  }

  res.send(item);
}

async function create(req, res) {
  const novoItem = req.body;

  if (!novoItem || !novoItem.nome || !novoItem.preco || !novoItem.categoria) {
    return res.status(400).send('Corpo da requisição deve conter as propriedades nome, preço e categoria.');
  }

  await service.create(novoItem);
  res.status(201).send(novoItem);
}

async function updateById(req, res) {
  const id = req.params.id;
  const novoItem = req.body;

  if (!novoItem || !novoItem.nome || !novoItem.preco || !novoItem.categoria) {
    return res.status(400).send('Corpo da requisição deve conter as propriedades nome, preço e categoria.');
  }

  await service.updateById(id, novoItem);
  res.send(novoItem);
}

async function deleteById(req, res) {
  const id = req.params.id;
  await service.deleteById(id);
  res.send('Produto removido com sucesso: ' + id);
}

module.exports = {
  readAll,
  readById,
  create,
  updateById,
  deleteById
};
```

{% endcode %}

{% code title="src/produto/produto.service.js" lineNumbers="true" %}

```javascript
const { ObjectId } = require('mongodb');
const { getDatabase } = require('../db/database-connection');

function getCollection() {
  return getDatabase().collection('produtos');
}

function readAll() {
  return getCollection().find().toArray();
}

function readById(id) {
  return getCollection().findOne({ _id: new ObjectId(id) });
}

function create(novoItem) {
  return getCollection().insertOne(novoItem);
}

function updateById(id, novoItem) {
  return getCollection().updateOne(
    { _id: new ObjectId(id) },
    { $set: novoItem }
  );
}

function deleteById(id) {
  return getCollection().deleteOne({ _id: new ObjectId(id) });
}

module.exports = {
  readAll,
  readById,
  create,
  updateById,
  deleteById
};
```

{% endcode %}

{% code title="src/produto/produto.router.js" lineNumbers="true" %}

```javascript
const express = require('express');
const controller = require('./produto.controller');

const router = express.Router();

router.get('/', controller.readAll);
router.get('/:id', controller.readById);
router.post('/', controller.create);
router.put('/:id', controller.updateById);
router.delete('/:id', controller.deleteById);

module.exports = router;
```

{% endcode %}

{% code title="index.js" lineNumbers="true" %}

```javascript
require('dotenv').config();
const express = require('express');
const { connectToDatabase } = require('./src/db/database-connection');

const produtoRouter = require('./src/produto/produto.router');

async function main() {
  await connectToDatabase();

  const app = express();
  app.use(express.json());

  app.get('/', function (req, res) {
    res.send('Hello World');
  });

  app.use('/produtos', produtoRouter);

  const port = process.env.PORT || 3000;
  app.listen(port, function () {
    console.log(`Servidor rodando em http://localhost:${port}`);
  });
}

main();
```

{% endcode %}

**Feedback Detalhado**

* **Verificação dos Endpoints**: Certifique-se de testar cada rota (`/produtos`, `/produtos/:id`, `/produtos` com POST, PUT e DELETE) acessando `http://localhost:3000` e verificando as respostas.
* **Estrutura do Projeto**: Verifique se a estrutura de pastas segue o padrão MVC, com Models, Views e Controllers bem definidos.
* **Uso de Variáveis de Ambiente**: Certifique-se de que as informações sensíveis, como a URL do banco de dados, estão sendo carregadas do arquivo `.env`.

</details>
