DDD: Variantes e Invariantes

O artigo DDD: Variantes e Invariantes explora esses conceitos com exemplos práticos para ajudar os leitores a compreendê-los completamente, sem que seja necessário dominar o Domain Driven Design. Em resumo, entendo que aprender isso eleva seu nível de consciência sobre arquitetura de sistemas.

Quando você começa a se aprofundar no estudo do Domain Driven Design surgem alguns conceitos curiosos que aumentam o seu nível de consciência em relação ao Design. Nesse artigo entitulado DDD: Variantes e Invariantes vamos explicar um pouco sobre esses conceitos e por que são importantes para a boa modelagem de domínios.

Temos aqui no blog uma série de artigos sobre Domain Driven Design e um específico sobre a variância. Nenhum destes artigos é requisito para ler esse, mas é fundamental um conhecimento básico sobre o tema.

Consistência da Entidade

Para começar, é importante entender que o DDD defende que um negócio seja estruturado em domínios. Esses domínios podem ter relações entre si e gerando assim as grandes estruturas de valor da empresa. Eles também possuem estruturas intermediárias que precisam ser cuidadosamente representadas e preservadas. Para tal há Entidades, ValueObjects e outros que não vou focar aqui. Para entender com mais detalhes consulte Domain Driven Design tático.

Uma entidade é uma representação de algo do domínio que possui uma identidade particular, distinta, com ciclo de vida identificável pelo especialista. Na prática uma classe Produto, por exemplo, seria uma entidade.

As entidades precisam possuir uma identificação única, seja por um id string, inteiro, uuid, etc. Mas além disso ela precisa estar sempre consistente. Isso significa que não pode ter um monte de propriedades que podem ser atribuídas sem uma validação que faça sentido.

Portanto é muito comum que no DDD não haja propriedades com setters públicos, apenas privados. E há um construtor que oferece meios para que as propriedades sejam definidas de uma vez só. Além disso, outros métodos podem existir, mas que sejam significativos para o usuário do domínio e, desse modo, as propriedades podem ser atualizadas. Veja a seguir um exemplo de entidade que segue os preceitos do Domain Driven Design.

public class Produto
{
    public Guid Id { get; private set; }
    public string Nome { get; private set; }
    public decimal Preco { get; private set; }
    public int QuantidadeEmEstoque { get; private set; }
    public Categoria Categoria { get; private set; }

    public Produto(string nome, decimal preco, int quantidadeEmEstoque, Categoria categoria)
    {
        Id = Guid.NewGuid();
        Nome = nome;
        Preco = preco;
        QuantidadeEmEstoque = quantidadeEmEstoque;
        Categoria = categoria;
    }


    public void AdicionarQuantidadeEmEstoque(int quantidade)
    {
        QuantidadeEmEstoque += quantidade;
    }


}

Além disso, é fundamental que os métodos sempre validem as entradas todo o momento. Entenda que sempre que a entidade for manipulada ela precisar estar estável e coesa. É comum utilizar estruturas específicas de validação com o DDD. Para mais detalhes veja Validation Pattern.

Variantes e múltiplos domínios

É importante compreender que no DDD há vários domínios que podem ou não ter relação uns com os outros. As entidades não devem acessar diretamente entidades de outros domínios, mas ao invés disso devem ter os dados duplicados nela ou o ID da outra entidade. Isso parece um contrassenso mas não é. Na verdade essa prática dá independência aos domínios ou mesmo a agregados de entidades.

Compreendido isso, vamos considerar hipoteticamente um Streamming de vídeo, como Netflix. Podemos agora assumir que a entidade Cliente para o Domínio Marketing é uma coisa, mas Cliente para o domínio Financeiro é outra, ou ainda, Cliente para o Domínio Filmes é outra.

Para entender melhor, o Cliente para Marketing é estruturado em um fluxo de Prospect, Lead e Cliente. Há métodos específicos para envio de e-mails publicitários. Possui informações relevantes para LGPD e consenso de recebimento de publicidade e afins. Já o Cliente do ponto de vista do domínio Financeiro precisa de outras informações, tais como há débito em conta, forma de pagamento, data de vencimento da fatura, nível de inadimplência, entre outros. São dados e estruturas muito diferentes que para o Domain Driven Design precisam estar representados por entidades diferentes.

Mas note que essas entidades não são tão diferentes assim. Inclusive elas podem ou não ter a mesma identificação única, ainda que em domínios distintos. O fato é que sabemos que são a mesma coisa. A isso damos o nome de variantes.

Note também que uma variante não precisa nem mesmo ter o mesmo nome. Por exemplo, em um hospital o domínio Clínica Médica tem uma entidade chamada Paciente, mas no domínio Financeiro há a entidade Cliente. Não faz sentido o médico te chamar de cliente, por exemplo. Veja que nesse caso ainda temos variantes da mesma coisa.

Invariantes

Vamos retomar o que comentamos anteriormente: as entidades precisam ser consistentes. Ocorre que uma mesma entidade pode estar consistente de formas diferentes, de acordo com suas características. Por exemplo como já abordamos, uma entidade Cliente para o domínio Marketing poderia ter uma enumeração que indica o estado dele: Prospect, Lead, Customer. Um prospect pode ter apenas um ID e hora acessando uma determinada página do site por um tempo. Já o Lead tem o e-mail obrigatóriamente preenchido. Por fim o Customer tem uma assinatura paga, quais e-mails deseja ser notificado e data da primeira assinatura. Veja o código como exemplo.

public class Cliente
{
    public enum TipoDeCliente
    {
        Prospect,
        Lead,
        Customer
    }

    public int Id { get; private set; }
    public DateTime DataDeAcesso { get; private set; }
    public string PaginaAcessada { get; private set; }
    public string Email { get; private set; }
    public DateTime DataDePagamentoDaAssinatura { get; private set; }
    public bool DesejaReceberNotificacoes { get; private set; }

    public TipoDeCliente Tipo { get; }

    public Cliente(int id, DateTime dataDeAcesso, string paginaAcessada)
    {
        Tipo = TipoDeCliente.Prospect;
        PreencherDadosDeProspect(id, dataDeAcesso, paginaAcessada);
    }

    public Cliente(int id, DateTime dataDeAcesso, string paginaAcessada, string email)
    {
        Tipo = TipoDeCliente.Lead;
        PreencherDadosDeLead(id, dataDeAcesso, paginaAcessada, email);
    }

    public Cliente(DateTime dataDePagamentoDaAssinatura, bool desejaReceberNotificacoes)
    {
        Tipo = TipoDeCliente.Customer;
        PreencherDadosDeCustomer(dataDePagamentoDaAssinatura, desejaReceberNotificacoes);
    }

    public void PreencherDadosDeProspect(int id, DateTime dataDeAcesso, string paginaAcessada)
    {
        if (Tipo != TipoDeCliente.Prospect)
            throw new InvalidOperationException("Os dados de prospect só podem ser preenchidos para clientes do tipo Prospect.");

        Id = id;
        DataDeAcesso = dataDeAcesso;
        PaginaAcessada = paginaAcessada;
    }

    public void PreencherDadosDeLead(int id, DateTime dataDeAcesso, string paginaAcessada, string email)
    {
        if (Tipo != TipoDeCliente.Lead)
            throw new InvalidOperationException("Os dados de lead só podem ser preenchidos para clientes do tipo Lead.");

        PreencherDadosDeProspect(id, dataDeAcesso, paginaAcessada);
        Email = email;
    }

    public void PreencherDadosDeCustomer(DateTime dataDePagamentoDaAssinatura, bool desejaReceberNotificacoes)
    {
        if (Tipo != TipoDeCliente.Customer)
            throw new InvalidOperationException("Os dados de customer só podem ser preenchidos para clientes do tipo Customer.");

        DataDePagamentoDaAssinatura = dataDePagamentoDaAssinatura;
        DesejaReceberNotificacoes = desejaReceberNotificacoes;
    }
}

Então, note que a mesma entidade possui 3 diferentes formas de ser. Entretanto, embora sejam 3 formas diferentes a entidade é a mesma e ela se manter estável. Chamamos isso de invariantes no Domain Driven Design.

Conclusão de DDD: Variantes e Invariantes

O artigo aborda um conceito bastante específico e sofisticado do Domain Driven Design. Variantes e invariantes são, para mim, um divisor de águas, separando o que é arquitetura de sistemas e Design de sistemas. Esses conceitos se baseiam numa interpretação particular de matérias como estatística e matemática.

O artigo DDD: Variantes e Invariantes explora esses conceitos com exemplos práticos para ajudar os leitores a compreendê-los completamente, sem que seja necessário dominar o Domain Driven Design. Em resumo, entendo que aprender isso eleva seu nível de consciência sobre arquitetura de sistemas.


Thiago Anselme
Thiago Anselme - Gerente de TI - Arquiteto de Soluções

Ele atua/atuou como Dev Full Stack C# .NET / Angular / Kubernetes e afins. Ele possui certificações Microsoft MCTS (6x), MCPD em Web, ITIL v3 e CKAD (Kubernetes) . Thiago é apaixonado por tecnologia, entusiasta de TI desde a infância bem como amante de aprendizado contínuo.

Deixe um comentário