Por que usar records em C# para reduzir boilerplate
Em muitas bases de código .NET, grande parte do esforço é gasta criando tipos de dados simples (DTOs, Value Objects, mensagens de eventos) com getters, setters, Equals, GetHashCode e construtores repetitivos. Records em C# reduzem esse boilerplate e tornam a intenção do domínio mais explícita, especialmente quando a imutabilidade é uma regra do jogo.
Neste artigo—dando continuidade à nossa série e mantendo o mesmo padrão visual e textual—vamos focar no uso prático de records para construir APIs mais limpas, modelos mais seguros e domínios mais expressivos, preparando o terreno para integrações, testes e evolução arquitetural.
Conceitos de records: imutabilidade e igualdade por valor
- Record: tipo de referência com igualdade por valor (por padrão), ideal para representar dados imutáveis e significativos no domínio.
- Imutabilidade: estados não mudam após criação; atualizações acontecem via
with(cópia não destrutiva). - Record class vs record struct:
record class: por referência, igualdade por valor (componentes).record struct: por valor, útil para cenários de alto desempenho e stack-friendly.
- Positional records: sintaxe concisa para definir propriedades no cabeçalho do tipo.
- Igualdade por valor: dois records com mesmos componentes são iguais, independentemente de referência.
- Deconstruct: suporte nativo para desconstrução de componentes.
- Compatibilidade arquitetural: encaixa bem em Domain-Driven Design (Value Objects), DTOs e contratos de mensagens.
Onde usar records em uma arquitetura em camadas
Em uma arquitetura em camadas (API → Application → Domain → Infrastructure), os records brilham em:
- Domain: modelagem de Value Objects imutáveis (e.g.,
Email,Money,Document), com validações e sem identidade própria. - Application: DTOs e Commands/Queries (CQRS) com igualdade por valor, simplificando testes e mapping.
- API: contratos de entrada/saída (request/response) concisos, fáceis de serializar.
- Infrastructure: mapeamento para persistência/integrações mantendo as invariantes do domínio.
O fluxo típico: a API recebe um DTO (record), a camada Application orquestra casos de uso e transforma DTOs em Value Objects (records), que são utilizados pelas entidades/serviços do domínio.
Records em código: Value Objects, DTOs, with e pattern matching
5.1. Value Object de Domínio com record
Pontos-chave
- Imutável: sem
set;. O estado é definido no construtor. - Invariantes no factory (
Create): garante validade desde a criação. - Igualdade por valor: dois
Emailcom o mesmoAddresssão iguais sem sobrescrever manualmenteEquals/GetHashCode.
5.2. DTO de Request/Response com record (Positional)
Pontos-chave
- Sintaxe concisa para reduzir boilerplate.
record structpara Money: econômico e sem alocação extra quando passado por valor (cenário ilustrativo).
5.3. Uso de with e pattern matching
5.4. Integração com a Camada de Aplicação
Pontos-chave
- Conversão explícita de DTO → Value Object assegura invariantes.
- O contrato de resposta permanece conciso e expressivo.
5.5. Considerações de Serialização (System.Text.Json)
Observação: records positional e nominal funcionam bem com System.Text.Json quando possuem accessors públicos de leitura; ajuste constructors explícitos se você precisar de parse customizado.
Boas práticas e cuidados ao usar records
- Use records para dados e significado, não para comportamentos ricos. Comportamentos do domínio pertencem a Entidades/Serviços; records são ótimos para Value Objects e contratos.
- Construa invariantes na criação (factory estática ou construtor privado + método estático): evita instâncias inválidas.
- Cuidado com mutabilidade acidental: evite
initexposto quando a semântica exigir verdadeira imutabilidade. - Versionamento de contratos: ao evoluir records usados em APIs/eventos, prefira novos tipos (
CustomerCreatedV2) a alterações breaking. - Interoperabilidade com EF Core: para Value Objects, mapeie como owned types; garanta construtores compatíveis com a infraestrutura quando necessário (por ex., construtor privado + configuration).
- Perf:
record structpode reduzir alocações em cenários hot-path; valide com benchmarks antes de adotar.
Quando usar records e quando preferir classes
Use quando
- Modelar Value Objects (ex.:
Email,CPF,Money). - Definir DTOs, Commands/Queries e event payloads.
- Priorizar imutabilidade e igualdade por valor para clareza e testabilidade.
Evite quando
- O tipo exige identidade e ciclo de vida (entidades ricas): prefira
class. - Há forte dependência de ORMs com requisitos de setters públicos ou proxies (mitigável com owned types/configuração).
- O domínio demanda mutações frequentes e intrínsecas ao tipo (considere classes tradicionais).
Records em C#: menos boilerplate, mais intenção
Records em C# reduzem código repetitivo e deixam explícita a intenção do domínio, oferecendo imutabilidade, igualdade por valor e sintaxe enxuta. Em arquiteturas em camadas, eles tornam DTOs e Value Objects mais expressivos, simplificam testes e favorecem contratos claros entre componentes. Aplique-os onde há dados com semântica forte e invariantes bem definidas; reserve classes tradicionais para entidades e cenários com identidade e mutabilidade inerentes.
Leia também
- Como Mapear DTOs Manualmente em .NET: usa records como DTOs em mapeamentos manuais com testes.
- .NET 10 e C# 14: veja o que o C# 14 acrescenta à linguagem além dos records.
- Clean Architecture em .NET: detalha as camadas de uma solução .NET, onde os records se encaixam.
Precisa de APIs .NET de alta disponibilidade? Conheça o serviço de backend e arquitetura .NET.
