Leaders Logo
Connascence em .NET: Como Reduzir Acoplamento Sem Perder Velocidade de Entrega

Connascence em .NET: Como Reduzir Acoplamento Sem Perder Velocidade de Entrega

Introdução

Embora seja um conceito antigo, Connascence ainda não é bem compreendida por alguns desenvolvedores. Isso é relevante porque o acoplamento excessivo continua sendo uma das principais causas de fragilidade em sistemas corporativos. Em plataformas modernas como .NET, o desafio não é apenas construir rápido, mas permitir evolução segura ao longo do tempo. O conceito de Connascence oferece um vocabulário técnico para diagnosticar dependências e orientar refatorações com critério arquitetural (PAGE-JONES, 1992).

Neste artigo, exploramos como aplicar Connascence em APIs, serviços e fluxos orientados a eventos em .NET 10, mostrando práticas para deslocar acoplamento de áreas distribuídas para pontos locais e mais controláveis.

Fundamentos de Connascence

Definição e motivação

Connascence descreve o grau de conhecimento compartilhado exigido entre partes de um sistema para que ele funcione corretamente. Quanto maior o conhecimento compartilhado e mais distante ele estiver no sistema, maior o risco de regressão em mudanças futuras (PAGE-JONES, 1992).

Principais tipos de Connascence

No dia a dia com .NET, os tipos mais comuns aparecem como:

  • Connascence of Name (CoN): dependência de nomes exatos (métodos, propriedades, campos JSON).
  • Connascence of Type (CoT): dependência de tipos e formatos específicos.
  • Connascence of Meaning (CoM): dependência de convenções implícitas, sem documentação explícita.
  • Connascence of Execution/Timing (CoE/CoTg): dependência de ordem e tempo de execução.
  • Connascence of Algorithm (CoA): múltiplos pontos replicando o mesmo algoritmo.
  • Connascence of Position (CoP): dependência da posição de argumentos ou campos.
Imagem SVG do Artigo

O objetivo não é eliminar toda connascence, mas reduzir suas formas mais nocivas e, quando inevitável, mantê-las em escopos locais e altamente testáveis (FOWLER, 2002).

Connascence em APIs .NET

Exemplo de acoplamento por posição e significado

Um antipattern recorrente é expor APIs com parâmetros posicionais sem semântica explícita:

// Antipattern: alto risco de CoP e CoM
public sealed class PricingService
{
    public decimal Calculate(decimal value, decimal discount, decimal tax)
        => (value - discount) * (1 + tax);
}

// Chamada frágil: a ordem errada compila e falha apenas em runtime/lógica
var total = pricing.Calculate(100m, 0.2m, 10m);

A refatoração para um contrato explícito reduz ambiguidades e facilita evolução:

public sealed record class PricingInput
{
    public required decimal GrossAmount { get; init; }
    public required decimal DiscountAmount { get; init; }
    public required decimal TaxRate { get; init; }
}

public sealed class PricingService
{
    public decimal Calculate(PricingInput input)
        => (input.GrossAmount - input.DiscountAmount) * (1 + input.TaxRate);
}

var total = pricing.Calculate(new PricingInput
{
    GrossAmount = 100m,
    DiscountAmount = 10m,
    TaxRate = 0.2m
});

O resultado da análise de connascence executada no terminal é apresentado a seguir.

Imagem GIF do Artigo

Versionamento e connascence de nome

Alterar nomes de campos em contratos públicos cria CoN distribuída entre provedores e consumidores. Em .NET 10, o caminho mais seguro continua sendo evolução aditiva com DTOs versionados por namespace e serialização explícita, mantendo retrocompatibilidade (NEWMAN, 2021).

namespace Contracts.V1;

public sealed record class OrderDto
{
    public required Guid Id { get; init; }
    public required decimal TotalAmount { get; init; }
}

namespace Contracts.V2;

public sealed record class OrderDto
{
    public required Guid Id { get; init; }
    public required decimal TotalAmount { get; init; }
    public string? Currency { get; init; }
}

Connascence em Arquiteturas Distribuídas

Eventos e connascence de tempo

Em mensageria, assumir ordem fixa de eventos entre serviços diferentes é uma forma perigosa de CoE/CoTg. Em vez disso, prefira mensagens autoexplicativas, idempotência e processamento orientado a estado, não à sequência estrita (VERNON, 2013).

public sealed record class PaymentReceivedV1
{
    public required Guid OrderId { get; init; }
    public required decimal Amount { get; init; }
    public required DateTimeOffset OccurredAt { get; init; }
    public required string EventId { get; init; }
}

public sealed class PaymentProjection
{
    private readonly HashSet<string> _processed = [];

    public bool TryApply(PaymentReceivedV1 evt)
    {
        if (!_processed.Add(evt.EventId))
        {
            return false; // Idempotência evita dependência de timing e repetição
        }

        // Atualiza estado de forma determinística
        return true;
    }
}

Contratos canônicos como redutores de acoplamento

Quando vários sistemas trocam dados com semânticas próximas, um modelo canônico reduz CoM e CoN porque explicita linguagem ubíqua e regras de transformação em pontos conhecidos. Esse arranjo costuma funcionar melhor quando combinado com mapeadores dedicados e testes de contrato automatizados (EVANS, 2003).

Estratégias Práticas para Times .NET

1. Medir connascence nas revisões técnicas

Durante code review, questione: esta mudança adiciona dependência de nome, ordem, tipo ou tempo entre módulos distantes? Se sim, reavalie o desenho antes de escalar para produção.

2. Localizar connascence forte dentro do mesmo modulo

Connascence forte é menos danosa quando confinada ao mesmo componente, pois a mudança coordenada ocorre no mesmo ciclo de deploy. Esse princípio alinha com a recomendação de aumentar coesão e reduzir acoplamento transversal (MARTIN, 2017).

3. Automatizar verificações de contrato

Em pipelines .NET, testes de serialização e compatibilidade entre versões previnem regressão silenciosa:

public sealed class ContractCompatibilityTests
{
    [Fact]
    public void V2_Should_Deserialize_V1_Payload()
    {
        var v1Json = """
        {
          "Id": "7a6a16de-31c0-4770-9413-6f6cefcf7a3a",
          "TotalAmount": 125.50
        }
        """;

        var dto = JsonSerializer.Deserialize<Contracts.V2.OrderDto>(v1Json);

        Assert.NotNull(dto);
        Assert.Equal(125.50m, dto!.TotalAmount);
        Assert.Null(dto.Currency);
    }
}

4. Reduzir connascence de algoritmo com serviços únicos

Quando a mesma regra aparece em vários pontos, centralize em um único serviço de domínio para evitar CoA distribuída e divergência semântica entre times.

Conclusão

Connascence oferece uma lente pragmática para evoluir sistemas .NET com menos regressão e mais previsibilidade. Ao identificar dependências por nome, tipo, significado, tempo e algoritmo, equipes técnicas tomam decisões de design com base em risco real de manutenção, não apenas em preferência estilística.

Em cenários corporativos, combinar contratos canônicos, versionamento aditivo, idempotência e testes de compatibilidade transforma o acoplamento de um problema oculto em um ativo gerenciável. O resultado é uma arquitetura mais resiliente, capaz de crescer sem sacrificar autonomia dos times nem estabilidade de integrações existentes.

Referências

  • PAGE-JONES, Meilir. The practical guide to structured systems design. 2. ed. Englewood Cliffs: Yourdon Press, 1992. reference.Description
  • EVANS, Eric. Domain-driven design: tackling complexity in the heart of software. Boston: Addison-Wesley, 2003. reference.Description
  • FOWLER, Martin. Patterns of enterprise application architecture. Boston: Addison-Wesley, 2002. reference.Description
  • NEWMAN, Sam. Building microservices: designing fine-grained systems. 2. ed. Sebastopol: O'Reilly Media, 2021. reference.Description
  • VERNON, Vaughn. Implementing domain-driven design. Boston: Addison-Wesley, 2013. reference.Description
  • MARTIN, Robert C. Clean architecture: a craftsman's guide to software structure and design. Boston: Pearson, 2017. reference.Description
Sobre o autor