1. SOLID, em uma frase cada
SOLID é um acrônimo de cinco princípios de design que já apareceram, na prática, em vários exemplos deste módulo:
| Princípio | Em uma frase | Onde você já viu |
|---|---|---|
| Single Responsibility | Uma classe deveria ter um único motivo para mudar | IClienteRepository só cuida de acesso a dados; ClienteService só de regra de negócio |
| Open/Closed | Aberta para extensão, fechada para modificação | Strategy (capítulo 12) — nova estratégia sem alterar código existente |
| Liskov Substitution | Uma subclasse deve poder substituir a classe base sem quebrar o comportamento esperado | Qualquer FormaGeometrica do capítulo 6 funciona onde a base é esperada |
| Interface Segregation | Várias interfaces pequenas e específicas > uma interface gigante | INotificavel/IAuditavel separadas (capítulo 6), em vez de uma única interface "faz tudo" |
| Dependency Inversion | Dependa de abstrações (interfaces), não de implementações concretas | Dependency Injection (capítulo 12) — ClienteService depende de IClienteRepository, não de ClienteRepository |
2. Nomenclatura — convenções C# que valem a pena fixar
| Elemento | Convenção | Exemplo |
|---|---|---|
| Classes, métodos, propriedades | PascalCase | ClienteRepository, ObterPorId |
| Variáveis locais, parâmetros | camelCase | clienteId, nomeCompleto |
| Campos privados | _camelCase | _connectionString |
| Interfaces | Prefixo I | IClienteRepository |
| Métodos assíncronos | Sufixo Async | ObterClienteAsync |
| Constantes | PascalCase (não SCREAMING_CASE como em JS) | const int TamanhoMaximo = 100; |
A diferença mais perceptível: métodos e propriedades públicas usam PascalCase em C#
(ObterCliente), não camelCase (obterCliente)
como em JS. É puramente convenção, mas seguir o padrão da comunidade C# facilita a leitura para
qualquer outro desenvolvedor .NET que olhar seu código — inclusive seus futuros alunos.
3. Métodos pequenos, com um único nível de abstração
public async Task ProcessarPedidoAsync(Pedido pedido)
{
if (pedido.Itens.Count == 0)
throw new ArgumentException("Pedido vazio");
decimal total = 0;
foreach (var item in pedido.Itens)
total += item.Preco * item.Quantidade;
pedido.ValorTotal = total;
await _repositorio.SalvarAsync(pedido);
await _emailService.EnviarConfirmacaoAsync(pedido.ClienteEmail);
}
public async Task ProcessarPedidoAsync(Pedido pedido)
{
ValidarPedido(pedido);
CalcularTotal(pedido);
await _repositorio.SalvarAsync(pedido);
await _emailService.EnviarConfirmacaoAsync(pedido.ClienteEmail);
}
private void ValidarPedido(Pedido pedido)
{
if (pedido.Itens.Count == 0)
throw new ArgumentException("Pedido vazio");
}
private void CalcularTotal(Pedido pedido)
=> pedido.ValorTotal = pedido.Itens.Sum(i => i.Preco * i.Quantidade);
A segunda versão lê quase como uma lista de passos em português: validar, calcular, salvar, notificar. Cada detalhe de como fazer cada passo fica isolado em seu próprio método, no mesmo espírito das funções locais do capítulo 4.
4. Evite números e strings mágicos
if (produto.Estoque < 10)
NotificarReposicao(produto);
if (pedido.Status == "3")
CancelarPedido(pedido);
const int EstoqueMinimo = 10;
if (produto.Estoque < EstoqueMinimo)
NotificarReposicao(produto);
if (pedido.Status == StatusPedido.Cancelado)
CancelarPedido(pedido);
"3" como status não diz nada para quem lê o código seis meses
depois. Um enum StatusPedido {'{'} Pendente, Confirmado, Cancelado {'}'}
transforma um número mágico numa palavra que se explica sozinha — e o compilador ainda impede
valores inválidos.
5. Nullable Reference Types — deixando "pode ser null" explícito
Desde o C# 8, o compilador pode avisar quando uma referência potencialmente null
é usada sem checagem — mas só se o recurso estiver habilitado no projeto:
// no .csproj: <Nullable>enable</Nullable>
public class Cliente
{
public string Nome { get; set; } = string.Empty; // nunca null
public string? Telefone { get; set; } // pode ser null — explícito
}
Cliente? cliente = await repositorio.ObterPorIdAsync(id);
Console.WriteLine(cliente.Nome); // aviso do compilador: cliente pode ser null aqui
if (cliente is not null)
Console.WriteLine(cliente.Nome); // agora sem aviso
6. Comentários: explique o "porquê", não o "o quê"
// incrementa o contador
contador++;
// XACT_ABORT é necessário aqui porque a procedure de origem
// não reverte a transação sozinha em erros de constraint
SET XACT_ABORT ON;
7. Onde isso te leva
Com fundamentos, OOP, LINQ, async e Clean Code cobertos, falta uma última peça antes de fechar o módulo: o capítulo 15 aprofunda ADO.NET puro — como falar com o SQL Server sem Entity Framework nem Dapper, só com as classes base do .NET. É um complemento importante ao que o capítulo 13 já mostrou, e a base de tudo que os módulos seguintes (MVC, APIs, Worker Services, Desktop) fazem por baixo dos panos.