Minimal APIs vs. MVC: A Batalha dos Códigos no Desenvolvimento .NET
No mundo do ASP.NET Core, construir APIs e aplicações web se tornou mais flexível do que nunca. No entanto, essa flexibilidade trouxe uma nova e crucial escolha: devemos seguir a abordagem tradicional dos Controladores MVC/API Web ou abraçar a modernidade e a simplicidade das Minimal APIs?
A verdade é que essa decisão vai muito além da sintaxe; ela molda a arquitetura, a performance e a manutenibilidade de todo o seu projeto em C#.
Por Que Essa Escolha é Vital?
Escolher entre Minimal APIs e MVC é determinar o DNA do seu backend.
Agilidade e Performance (Minimal APIs): Se você busca construir microsserviços leves, lambdas ou APIs simples que precisam ser extremamente rápidas e consumir o mínimo de memória, as Minimal APIs são o caminho. Elas eliminam boilerplate (código repetitivo) e colocam a lógica do C# diretamente no ponto de execução.
Estrutura e Escala (MVC/API Web): Se o seu projeto é um monolito complexo, uma aplicação web completa (que renderiza HTML) ou exige uma divisão de responsabilidades rigorosa para acomodar grandes equipes, a estrutura rígida e familiar dos Controladores MVC oferece a melhor organização e testabilidade.
O Que Você Vai Ver a Seguir: Como Usar Cada Abordagem
Para ajudá-lo a tomar a decisão certa, vamos desmistificar essa escolha com o que realmente importa: o código.
A seguir, você verá exemplos práticos de como implementar um CRUD (Criar, Ler, Atualizar, Excluir) simples para uma entidade Produto
em ambas as abordagens. Preste atenção em como o C# é escrito em cada cenário, pois é aí que as diferenças arquiteturais se tornam mais claras:
Minimal API: Usando a sintaxe concisa de
app.MapXxx
e funções lambda.Controller MVC: Usando a estrutura de classes, herança de
ControllerBase
e atributos como[HttpGet]
.
Prepare-se para comparar a simplicidade funcional contra a estrutura orientada a objetos e definir qual delas será a espinha dorsal do seu próximo projeto em .NET!
Exemplos práticos e códigos, e é exatamente isso que você vai ter! A melhor forma de entender a diferença entre Minimal APIs e Controladores MVC/API Web é vendo a arquitetura e o código lado a lado.
Vamos simular o desenvolvimento de um CRUD simples (Criar, Ler, Atualizar, Excluir) para uma entidade chamada Produto.
A Base: Nosso Modelo de Dados (Produto)
Em ambos os projetos, teremos uma classe de modelo simples, muitas vezes chamada de Entidade ou Model.
Arquivo: Produto.cs
public class Produto
{
public int Id { get; set; }
public string Nome { get; set; } = string.Empty;
public decimal Preco { get; set; }
}
1. Abordagem: Minimal APIs (Simplicidade e Performance)
O grande diferencial aqui é a ausência de classes de Controller. Toda a lógica de roteamento e endpoint é definida no arquivo de inicialização, geralmente o Program.cs
.
Cenário Ideal: Microsserviços, APIs simples e de alta performance, prototipagem.
Estrutura do Projeto Minimal API:
Program.cs
: Contém toda a configuração da aplicação, injeção de dependência e endpoints da API.Produto.cs
: Classe do modelo.
Código de Exemplo (CRUD Completo)
Para simplificar o exemplo e focar na diferença arquitetural, usaremos uma lista estática (in-memory) para simular o banco de dados.
Arquivo: Program.cs
// 1. Configuração e Injeção de Dependência (Mais Enxuta)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); // Adiciona o Swagger para testar
// Simulação de "Banco de Dados" (Lista em memória)
var produtos = new List<Produto>
{
new Produto { Id = 1, Nome = "Notebook Gamer", Preco = 5500.00m },
new Produto { Id = 2, Nome = "Mouse Sem Fio", Preco = 120.00m }
};
var app = builder.Build();
// Configura o middleware
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// 2. Definição dos Endpoints CRUD (Sintaxe Concisa com MapXxx)
// CREATE (POST)
app.MapPost("/produtos", (Produto produto) =>
{
produto.Id = produtos.Count > 0 ? produtos.Max(p => p.Id) + 1 : 1;
produtos.Add(produto);
// Retorna 201 Created com o item criado
return Results.Created($"/produtos/{produto.Id}", produto);
}).WithOpenApi(); // Ajuda o Swagger a documentar o endpoint
// READ All (GET)
app.MapGet("/produtos", () =>
{
return Results.Ok(produtos); // Retorna 200 OK com a lista
}).WithOpenApi();
// READ By ID (GET)
app.MapGet("/produtos/{id}", (int id) =>
{
var produto = produtos.FirstOrDefault(p => p.Id == id);
return produto is null ? Results.NotFound() : Results.Ok(produto);
}).WithOpenApi();
// UPDATE (PUT)
app.MapPut("/produtos/{id}", (int id, Produto produtoAtualizado) =>
{
var index = produtos.FindIndex(p => p.Id == id);
if (index == -1)
{
return Results.NotFound();
}
produtoAtualizado.Id = id;
produtos[index] = produtoAtualizado;
return Results.NoContent(); // Retorna 204 No Content
}).WithOpenApi();
// DELETE (DELETE)
app.MapDelete("/produtos/{id}", (int id) =>
{
var produto = produtos.FirstOrDefault(p => p.Id == id);
if (produto is null)
{
return Results.NotFound();
}
produtos.Remove(produto);
return Results.NoContent();
}).WithOpenApi();
app.Run();
2. Abordagem: Controladores MVC (Estrutura e Organização)
Nesta abordagem, a lógica de manipulação das requisições é encapsulada em classes dedicadas (os Controllers). Cada método no Controller é uma Action que responde a uma requisição HTTP.
Cenário Ideal: Aplicações Web que precisam de Views (renderização de HTML), APIs complexas, projetos grandes que exigem forte separação de responsabilidades.
Estrutura do Projeto MVC/API Web:
Program.cs
: Configuração da aplicação (mais enxuta).Controllers/ProdutosController.cs
: Contém toda a lógica de endpoints para a entidadeProduto
.Produto.cs
: Classe do modelo.
Código de Exemplo (CRUD Completo)
Arquivo: Program.cs
(Configuração Central)
A principal diferença é que o Program.cs
foca apenas em habilitar o uso de Controladores. A lógica de rotas fica neles.
var builder = WebApplication.CreateBuilder(args);
// Adiciona o suporte a Controllers e Views (se fosse MVC)
// Para API pura, é comum usar AddControllers
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
// Mapeia os Controladores para que as rotas sejam encontradas
app.MapControllers();
app.Run();
Arquivo: Controllers/ProdutosController.cs
(A Lógica da API)
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using SeuProjeto.Models; // Assumindo que Produto.cs está em Models
// O atributo [ApiController] é padrão para Web APIs e habilita funcionalidades
[ApiController]
[Route("api/[controller]")] // Define a rota base /api/produtos
public class ProdutosController : ControllerBase
{
// Simulação do "Banco de Dados" (Lista estática)
private static readonly List<Produto> _produtos = new List<Produto>
{
new Produto { Id = 1, Nome = "Notebook Gamer", Preco = 5500.00m },
new Produto { Id = 2, Nome = "Mouse Sem Fio", Preco = 120.00m }
};
// CREATE (POST)
[HttpPost]
public ActionResult<Produto> PostProduto(Produto produto)
{
produto.Id = _produtos.Count > 0 ? _produtos.Max(p => p.Id) + 1 : 1;
_produtos.Add(produto);
// Retorna 201 Created com a URL para o novo recurso
return CreatedAtAction(nameof(GetProduto), new { id = produto.Id }, produto);
}
// READ All (GET)
[HttpGet]
public ActionResult<IEnumerable<Produto>> GetProdutos()
{
return Ok(_produtos); // Retorna 200 OK
}
// READ By ID (GET)
[HttpGet("{id}")] // Define o parâmetro {id} na rota
public ActionResult<Produto> GetProduto(int id)
{
var produto = _produtos.FirstOrDefault(p => p.Id == id);
// O ControllerBase já possui métodos auxiliares (NotFound, Ok)
if (produto == null)
{
return NotFound();
}
return Ok(produto);
}
// UPDATE (PUT)
[HttpPut("{id}")]
public IActionResult PutProduto(int id, Produto produtoAtualizado)
{
if (id != produtoAtualizado.Id)
{
// Validação explícita de consistência
return BadRequest();
}
var produtoExistente = _produtos.FirstOrDefault(p => p.Id == id);
if (produtoExistente == null)
{
return NotFound();
}
produtoExistente.Nome = produtoAtualizado.Nome;
produtoExistente.Preco = produtoAtualizado.Preco;
return NoContent(); // Retorna 204 No Content
}
// DELETE (DELETE)
[HttpDelete("{id}")]
public IActionResult DeleteProduto(int id)
{
var produto = _produtos.FirstOrDefault(p => p.Id == id);
if (produto == null)
{
return NotFound();
}
_produtos.Remove(produto);
return NoContent();
}
}
Comparação Direta: O Ponto de Virada
Critério | Minimal APIs | Controladores MVC |
Arquitetura | Fluxo Linear (Program.cs ): Os endpoints são funções lambda. | Estrutura de Classes: Lógica dividida em classes (Controller ). |
Roteamento | Definido com métodos de extensão: app.MapGet() , app.MapPost() . | Definido com Atributos: [Route] , [HttpGet] , [HttpPost] . |
Verbosidade | Baixa. Mínimo de boilerplate (código repetitivo). | Média/Alta. Requer a criação de classes e a adição de atributos. |
Injeção de Dependência | Parâmetros injetados diretamente nas funções lambda: (MeuServico service) => ... | Construtor da classe Controller: public Controller(MeuServico service) . |
Escalabilidade | Pode virar um "Spaghetti Code" em Program.cs se não for modularizado manualmente (ex: Extension Methods). | Alta. A separação de classes e o padrão facilitam a organização em milhares de linhas de código. |
Conclusão Prática
A escolha resume-se a:
Se você valoriza velocidade de escrita, menos arquivos e a melhor performance possível para um serviço pequeno e bem definido (microsserviço): Vá de Minimal APIs.
Se você precisa de uma aplicação completa com interface web (Views), quer usar o padrão MVC rígido para manter a organização em grandes equipes e projetos, ou se beneficia da validação de modelo "out-of-the-box": Vá de Controladores MVC/API Web.
Ambas são ferramentas excelentes. Escolha a que melhor se adapta à escala e à complexidade que seu projeto precisa hoje e amanhã!
Comentários
Postar um comentário