Projeto 9 • Sistemas de Informacao

Protocolos de Comunicacao Web no ASP.NET Core

Capitulo da Aula 7: REST, gRPC, GraphQL, CQRS com MediatR, filas, observabilidade e estrategia de testes end-to-end.

Introducao: Protocolo e Decisao de Arquitetura

Em sistemas corporativos construidos com ASP.NET Core, a escolha do protocolo de comunicacao nao e um detalhe tecnico isolado. Ela influencia diretamente latencia percebida, custo de infraestrutura, estrategia de testes, observabilidade, forma de evolucao de contratos e ate risco de incidentes em producao.

O erro mais comum em times iniciantes e escolher protocolo por preferencia pessoal ("gosto de GraphQL", "REST e mais facil") sem conectar a decisao ao caso de uso. Em arquitetura profissional, o fluxo correto e o inverso: primeiro se modela o comportamento do negocio e as restricoes nao funcionais (SLO, volume, frequencia, acoplamento); depois se escolhe o protocolo mais adequado.

Regra de ouro da aula

Protocolos sao adaptadores de transporte. A regra de negocio deve ficar desacoplada no core da aplicacao (handlers, dominio e servicos), nao no controller/resolver.

1. Mapa de Protocolos e Criterios de Escolha

Opcao Quando usar Principal vantagem Principal risco
REST (HTTP/JSON) CRUD, integracoes externas, APIs publicas Interoperabilidade e debug simples Overfetch/underfetch, contratos menos estritos
gRPC (HTTP/2 + Protobuf) Servico-servico de alta frequencia Baixa latencia, payload menor, contrato forte Debug mais tecnico e integracao browser indireta
GraphQL Front-end com visoes de dados variaveis Cliente pede exatamente os campos N+1, query cara e governanca de schema
WebSocket Atualizacao em tempo real Canal full-duplex persistente Escalar conexoes e estado de sessao

No Projeto 9, o caminho mais realista e adotar combinacao de protocolos: REST/gRPC para comandos e leitura base, GraphQL para composicao de telas ricas e WebSocket para atualizacao live quando fizer sentido. O importante e preservar coerencia arquitetural no core.


2. REST no ASP.NET Core em Profundidade

2.1 Contrato HTTP, semantica e idempotencia

REST em ASP.NET Core deve usar verbos HTTP com semantica consistente. GET para leitura, POST para criacao/comando nao idempotente, PUT para substituicao idempotente e PATCH para alteracao parcial.

Idempotencia e central em resiliencia: se houver retry automatico de rede e o endpoint nao for idempotente, o mesmo comando pode ser executado duas vezes. Em produção, isso gera duplicidade de pagamento, pedido, envio de evento etc.

// DTOs
public record CriarPedidoRequest(string ClienteId, List<ItemDto> Itens);
public record ItemDto(string ProdutoId, int Quantidade);

[ApiController]
[Route("api/pedidos")]
public class PedidosController : ControllerBase
{
    [HttpPost]
    public IActionResult Criar([FromBody] CriarPedidoRequest req)
    {
        // Regra de negocio no core (service/handler), nao no controller.
        return Ok(new { pedidoId = "P123", status = "CRIADO" });
    }
}

2.2 Payload JSON e evolucao de API

POST /api/pedidos
Content-Type: application/json

{
  "clienteId": "C1",
  "itens": [
    { "produtoId": "P10", "quantidade": 2 },
    { "produtoId": "P20", "quantidade": 1 }
  ]
}

Para evoluir API REST sem quebrar clientes: adicionar campos opcionais, manter contrato anterior por periodo de transicao, usar versionamento (URL, header ou media type) quando houver mudanca incompatível e retornar erro padronizado com ProblemDetails.

2.3 Testes e riscos frequentes

Erro comum em REST

Misturar regra de negocio no controller. Resultado: endpoint grande, teste caro, manutencao fraca e acoplamento ao transporte.


3. gRPC no ASP.NET Core em Profundidade

3.1 Contract-first com Protobuf

Em gRPC, o contrato vem primeiro (arquivo .proto). Isso cria uma fronteira forte entre cliente e servidor: o compilador gera stubs e mensagens tipadas para ambas as pontas.

syntax = "proto3";

service PedidoService {
  rpc CriarPedido (CriarPedidoRequest) returns (CriarPedidoResponse);
}

message CriarPedidoRequest {
  string cliente_id = 1;
  repeated Item itens = 2;
}

message Item {
  string produto_id = 1;
  int32 quantidade = 2;
}

message CriarPedidoResponse {
  string pedido_id = 1;
  string status = 2;
}

3.2 Unary, streaming, deadlines e cancellation

Unary e a chamada mais simples: 1 request e 1 response. Em termos didaticos, e o equivalente gRPC de um endpoint REST tradicional.

Alem de unary, gRPC oferece streaming server, streaming client e streaming bidirecional. Isso reduz overhead em cenarios de telemetria, eventos e sincronizacao continua.

public class PedidoGrpcService : PedidoService.PedidoServiceBase
{
    public override Task<CriarPedidoResponse> CriarPedido(
        CriarPedidoRequest request, ServerCallContext context)
    {
        return Task.FromResult(new CriarPedidoResponse
        {
            PedidoId = "P123",
            Status = "CRIADO"
        });
    }
}
Teste unary (definicao pratica)

Teste unary verifica 1 requisicao -> 1 resposta, validando payload tipado, StatusCode gRPC e regras de erro (ex.: DeadlineExceeded).

3.3 Compatibilidade e versionamento


4. GraphQL no ASP.NET Core em Profundidade

4.1 Schema, query e mutation

Em GraphQL, o cliente manda uma consulta declarando os campos desejados. O servidor retorna exatamente essa forma de dado. No ASP.NET Core, o stack comum e HotChocolate.

// Program.cs
builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>()
    .AddMutationType<Mutation>();

var app = builder.Build();
app.MapGraphQL("/graphql");
app.Run();
// Query (request)
POST /graphql
Content-Type: application/json

{
  "query": "query Pedido($id: String!) { pedidoPorId(id: $id) { pedidoId clienteId status itens { produtoId quantidade } } }",
  "variables": { "id": "P123" }
}
// Mutation (request)
POST /graphql
Content-Type: application/json

{
  "query": "mutation Criar($input: CriarPedidoInput!) { criarPedido(input: $input) { pedidoId status clienteId } }",
  "variables": {
    "input": {
      "clienteId": "C1",
      "itens": [
        { "produtoId": "P10", "quantidade": 2 },
        { "produtoId": "P20", "quantidade": 1 }
      ]
    }
  }
}

4.2 Autorizacao por campo e caso sensivel

Diferente de REST puro, GraphQL permite granularidade por campo. Isso e poderoso, mas exige politicas claras de autorizacao para evitar vazamento de dados sensiveis.

// Caso de teste: campo sensivel sem permissao
POST /graphql
Content-Type: application/json

{
  "query": "query { usuario(id:\"U1\") { id nome salario } }"
}

// Resposta esperada (resumo)
{
  "errors": [
    {
      "message": "Nao autorizado para acessar o campo 'salario'.",
      "path": ["usuario", "salario"]
    }
  ],
  "data": {
    "usuario": {
      "id": "U1",
      "nome": "Ana",
      "salario": null
    }
  }
}

4.3 Performance: N+1, DataLoader e complexidade

Trade-off real do GraphQL

Flexibilidade para cliente aumenta responsabilidade do backend em governanca de schema, autorizacao fina e controle de performance.


5. Estrategia de Testing (Black Box Primeiro)

Nesta aula, o modelo central e black box: validar entrada, saida e comportamento observavel, sem depender de detalhes internos de implementacao. Isso aumenta robustez dos testes frente a refatoracoes.

Tecnologia O que validar primeiro Tipos de teste Falha comum
REST Status code + payload + validacao Unit + Integration + Contract Quebra de contrato sem aviso
GraphQL Schema + auth por campo + custo query Schema tests + Integration + E2E query Exposicao de campo sensivel
gRPC Compatibilidade .proto + StatusCode Contract + Integration + Streaming Breaking change de campo
MediatR Comportamento de handler/pipeline Unit de handlers + behavior tests Regra vazando para controller
Filas Idempotencia + retry + DLQ Integration com broker + falha induzida Duplicidade e perda silenciosa

Exemplos objetivos de casos de teste

Situacao Entrada de teste Resultado esperado
REST com payload invalido POST sem clienteId HTTP 400 com erro de validacao
GraphQL com campo nao autorizado Query pedindo salario sem permissao Erro de autorizacao + campo nulo/bloqueado
gRPC com timeout curto Deadline menor que tempo do metodo DeadlineExceeded
Fila com reentrega Mesma mensagem processada 2x Estado final sem duplicidade (idempotente)
Fila com falha persistente Consumidor falha ate esgotar retry Mensagem em DLQ + metrica incrementada
Distribuicao recomendada de suite

Como baseline: ~70% testes unitarios, ~20% integracao e ~10% E2E. Ajustar conforme criticidade do dominio.


6. CQRS com MediatR e Filas

O papel de CQRS aqui e separar comandos (escrita) de consultas (leitura), permitindo evoluir performance, observabilidade e estrategia de consistencia sem contaminar o transporte.

// Command
public record RequestReportCommand(Guid DashboardId) : IRequest<Guid>;

// Handler
public class RequestReportHandler : IRequestHandler<RequestReportCommand, Guid>
{
    private readonly IReportQueue _queue;
    public RequestReportHandler(IReportQueue queue) => _queue = queue;

    public async Task<Guid> Handle(RequestReportCommand cmd, CancellationToken ct)
    {
        var operationId = Guid.NewGuid();
        await _queue.PublishAsync(new ReportRequested(operationId, cmd.DashboardId), ct);
        return operationId;
    }
}

Padroes obrigatorios em mensageria

Falha classica em filas

Consumidor sem idempotencia parece funcionar em homologacao e quebra em producao quando ocorre redelivery.


7. Observabilidade Operacional com OpenTelemetry + Prometheus

Sem telemetria, incidentes viram suposicao. Com OpenTelemetry, registramos traces, metrics e logs correlacionados por trace-id/correlation-id. Com Prometheus, coletamos series temporais para SLO, alertas e capacidade.

// Exemplo conceitual de setup no ASP.NET Core
builder.Services.AddOpenTelemetry()
    .WithTracing(t => t
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation())
    .WithMetrics(m => m
        .AddAspNetCoreInstrumentation()
        .AddRuntimeInstrumentation()
        .AddPrometheusExporter());

app.MapPrometheusScrapingEndpoint(); // /metrics

Metricas minimas recomendadas

Metrica Leitura operacional Sinal de risco
request_duration_ms (p95/p99) Experiencia de cliente em chamadas sincronas Crescimento continuo sem ganho de throughput
queue_depth / consumer_lag Saude do processamento assincrono Fila cresce por janela prolongada
dlq_messages_total Falhas nao recuperaveis Pico apos deploy/schema change
handler_failures_total Confiabilidade do core (CQRS) Taxa acima do error budget
retry_attempts_total Instabilidade transiente de dependencia Aumento cronico com latencia alta

8. Fluxo End-to-End Consolidado

A arquitetura final trabalhada na aula integra transporte, core de negocio e operacao:

Fluxo principal

Cliente -> REST/gRPC/GraphQL -> MediatR (Command/Query) -> Outbox -> Broker (Kafka/RabbitMQ) -> Worker -> Banco/Read Model -> WebSocket para atualizacao live.

Fluxo de observabilidade

OpenTelemetry (API + Worker + headers) -> Prometheus (/metrics) -> Alertas por SLO (latencia, erro, lag, DLQ).

Note que o protocolo pode mudar sem reescrever a regra de negocio, desde que o core permaneça isolado. Esse e o ganho arquitetural central do modelo.


9. Atividade de Sala: Entrega e Rubrica

Instrucao oficial da entrega

Implementar um fluxo end-to-end funcional em uma rota ja existente do Projeto 9, com validacao black box (entrada/saida), usando o protocolo escolhido pelo grupo (REST, gRPC ou GraphQL).

Tempo de execucao: 40 minutos para desenvolvimento e 20 minutos para apresentacao.

Item avaliado Descricao objetiva Evidencia esperada
Fluxo principal Codigo end-to-end passando por rota existente Execucao completa sem quebra de fluxo
Teste black box Foco em entrada/saida e comportamento Request, resposta e criterio de aceite claro
Core da aplicacao Uso de MediatR no command/query Handler executando regra de negocio
Assincrono Fila quando aplicavel ao caso Publicacao/consumo + controle de falha
Observabilidade Metricas Prometheus minimas Evidencia de latencia, erro e/ou lag
Defesa tecnica Justificar escolha do protocolo Trade-off explicado de forma objetiva
Meta pedagogica

Nao basta "funcionar". O objetivo e demonstrar criterio de arquitetura, previsibilidade operacional e qualidade de teste em um recorte realista de backend Core.