# 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>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://salvatore-academy.gitbook.io/dev-fullstack-web/academia/missoes-de-backend/organizacao-de-projeto-em-arquitetura-mvc.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
