Código como transmissor de conhecimento

Escrever código de forma clara é uma tarefa difícil.

Neste artigo, veremos algumas sugestões de como escrever código de negócio de uma forma que ele “fale a língua do domínio”, comunicando seu propósito para o time de desenvolvimento e também para os domain experts.

PERSIGA A LINGUAGEM UBÍQUA

A Linguagem Ubíqua (Ubiquitous Language), termo oriundo do Domain-Driven Design, é uma linguagem estabelecida entre o time de desenvolvimento e domain experts, de forma que haja uma comunicação clara entre todos (sem “tecniquês” e mais focada no negócio).

Os termos dessa linguagem devem se refletir no código, de forma que não sejam necessárias traduções do mesmo para a linguagem usada com os especialistas de negócio. Sendo assim, se “Política de Retenção de Clientes” é um termo empregado no seu domínio atual, não há porque inventar outro termo para colocar no código.

Vale lembrar que não é raro que pessoas de negócio não sejam capazes de nos dar um termo mais apropriado para aquela “coisa” que estamos implementando. Isso é ainda mais comum em domínios novos ou pouco explorados. Nessas situações, encontre um termo mais adequado em conjunto com as pessoas de negócio e mantenha-se consistente a ele (se for preciso, consulte outros especialistas fora de sua organização e materiais diversos sobre o domínio em questão).

ATENTE-SE AO ESTEREÓTIPO DOS OBJETOS

De acordo com o estereótipo de um objeto, isto é, o que esse objeto representa dentro do software, é possível estabelecer algumas convenções de nomes (sempre considerando-se a Linguagem Ubíqua antes mencionada).

Entidades e Valores

Nomeie “coisas” como substantivos, sejam elas “Entidades” (grande parte dos conceitos de domínio) ou “Value Objects (VOs)” (“coisas” que dão características às “Entidades”). Ex.: “Imovel”, “CPF”, “Venda”, etc.

Eventos

Nomeie “Domain Events”, objetos que representam um acontecimento dentro do domínio, como substantivo + verbo no particípio passado. Ex.: “VendaCancelada”, “PagamentoConcluido”, “UsuarioAtivado”, etc. Use um verbo auxiliar, caso o nome leve a confundi-lo com uma Entidade ou VO. Ex. “VendaFoiCancelada”.

Serviços (de domínio)

Nomeie “Domain Services”, operações de negócio, no infinitivo. Ex: “DespacharCarregamento”, “TransferirFundos”, etc.

O problema desta abordagem é dar bons nomes aos métodos: (1) poderá haver certa redundância nos nomes (“transferirFundos.TransferirPara(destino)”), (2) nomes de métodos genéricos demais (“transferirFundos.Executar(destino)”) ou (3) nomes de métodos fora da convenção de usarmos verbos para eles (“transferirFundos.Para(destino)”).

Uma segunda abordagem é a de nomear os “Domain Services” também como substantivos. Uma desvantagem flagrante é que seus nomes soariam mais como nomes de Entidades/VOs (ex.: “TransferenciaDeFundos”). Sendo assim, teríamos que acrescentar um prefixo/sufixo ao nome, como “ServicoDeTransferenciaDeFundos” (mais sobre isso no próximo tópico).

Dito tudo isso, considere fortemente a abordagem 1 – no infinitivo – e escolha algo próximo às opções 1 e 3 para os nomes de métodos.

EVITE ACRESCENTAR O NOME DE UM PATTERN AO NOME…

Pense no conceito expressado pelo negócio ao dar um nome e não em que design pattern ele se encaixa. Se o conceito é “Venda”, é o que basta para o nome da classe. Evite construções como “VendaEntidade”, “VendaValueObject”, “VendaAggregate”. Keep it simple!

Em outras palavras, mantenha-se, o máximo que puder, consistente com a Linguagem Ubíqua, mesmo para elementos que não estejam nos famosos “building blocks” do DDD, como algoritmos e validações, que usem,  respectivamente, os padrões “Strategy” e “Specification”, por exemplo.

Falando em “Strategy”, imagine que você implemente diferentes políticas de desconto usando este design pattern (também conhecido por “Policy”). Se “política de desconto” é o termo mais apropriado para o negócio, use-o: “PremiumCustomerDiscountPolicy” (uma implementação) e “DiscountPolicy” (a abstração). Neste caso, o pattern estar no nome é mera coincidência!

… MAS ABRA EXCEÇÕES QUANDO FIZER SENTIDO

Mesmo no domain model, pode haver objetos com um viés técnico, tais como factories e repositórios. Sendo assim, é perfeitamente aceitável dar nomes como “ContractFactory” e “ContractRepository”.

No casos dos repositórios, que representam uma coleção de entidades  – agregados, para ser mais preciso – de um mesmo tipo, o uso do pattern no nome pode ser mais questionável. Você pode argumentar que “Arquivo” faz parte da Linguagem Ubíqua e representa perfeitamente uma coleção de contratos.

Em minha experiência, não é comum acharmos nomes na Linguagem Ubíqua para representar essas coleções, ou porque não fazem sentido ou porque os domain experts geralmente usam o plural (“os contratos” ou “todos os contratos”). Se fizer sentido no seu domínio, use o termo que expresse a coleção (e deixe-me saber!).

CUIDADO COM ALGUNS HÁBITOS

Falamos até agora dos nomes dos objetos, mas é fundamental que consideremos as dicas anteriores para os dados e comportamentos dos objetos.

Um erro comum é darmos nomes aos métodos seguindo um estilo “CRUD”, isto é, nomes começando por “Criar<…>”, “Excluir<…>”, “Atualizar<….>”, etc, sem fazer esforço em pensar em um nome mais adequado.

Considere “pagamento.Cancelar(…)” e “pagamento.AtribuirDataDeCancelamento(…)”. O primeiro nome é bem mais claro quanto à intenção (“quero cancelar este pagamento”) do que o segundo, que tem mais “cara” de um setter, além de expor detalhes de implementação (“cancelar” é implementado “setando” a data de cancelamento).

Para os nomes dos atributos, não use abreviações como “DtCancel” ou notação húngara, como “strComentario”.

(Abreviações podem ser necessárias, caso o termo seja extremamente grande, mas evite o máximo que puder e, se usar, seja criativo para causar o mínimo possível de ruído.)

MANTENHA CONSISTÊNCIA

Seja consistente por todo o código. Como eu costumo dizer: código é exemplo, para o bem e para o mal. Se houver 10 padrões distintos de nomear um evento, por exemplo, ficará mais difícil para novos programadores saberem qual padrão usar, além de dar o seguinte recado: “faça aí do jeito que você bem entender”. Isto leva ao surgimento de mais padrões, imagináveis e inimagináveis!

CONCLUSÃO

Seguindo as sugestões vistas neste artigo, seu código representará de forma mais clara e limpa o domínio em questão, tendo grande valor para transmitir conhecimento sobre o negócio para os demais membros do time, incluindo pessoas de negócio!

(Você deve ter notado que, nos exemplos mostrados, utilizei nomes em português e em inglês. Afinal, qual das duas línguas utilizar no seu código? Esse assunto será tratado no próximo artigo.)

15 comentários em “Código como transmissor de conhecimento

Adicione o seu

    1. Oi, Mauro,
      Não recomendo, por:
      1 – misturar português com inglês
      2 – colocar o nome do pattern como sufixo da classe. PedidoFoiCancelado é tão expressivo. Por qual razão colocar esse sufixo?

      Curtir

      1. Eu tb odeio misturar português com inglês.
        Exemplos de nomes de repositórios que uso num sisteminha de exemplo num contexto de Contabilidade: ElencoContas (elenco de contas) para Grupos, Subgrupos, Contas e Subcontas contábeis e LivroDiario para os Lançamentos.

        Curtir

      2. Eu gosto tanto de preposições quanto de tirar o prefixo I das interfaces. Mas no trabalho evito. Nos meus códigos pessoais até rola.

        Curtir

  1. Então Robson, acompanho muitos seus posts, e este é um dos que mais abriram minha mente.

    Percebi que tenho adotado uma forma muito estereotipada para nomear os objetos.

    Imagine que o sistema é para gerenciar pedidos. Eu teria um PedidoService para especificar todas as operações relacionadas ao pedido:
    pedidoService.CancelarPedido( ), pedidoService.RegistrarPedido( ). Este service recebe as instâncias da entidade Pedido, e também interage com IPedidoRepository.

    No seu entendimento, você optar por criar uma classe para cada operação:
    registrarPedido.Executar(), cancelarPedido.EfetuarCancelamento().

    Na sua visão, quais são as desvantagens da minha implementação ?

    Curtir

    1. Oi, Lano

      No seu entendimento, você optar por criar uma classe para cada operação:
      registrarPedido.Executar(), cancelarPedido.EfetuarCancelamento().”

      Na verdade, não. Não existe um DOMAIN service para cada operação. Ele deve fazer sentido pro negocio (só pelo nome “PedidoService”, fica claro que seu serviço está meramente – e desnecessariamente – servindo de fachada para o seu domínio).

      Essa abordagem “1 classe por operação” faz mais sentido em uma camada acima, caso você esteja implementando “UseCases/Application Services”. Estes sim, podem encapsular 1 única operação (https://robsoncastilho.com.br/2014/04/16/introduzindo-a-camada-de-aplicacao/). Em seu formato mais simples, o UC irá receber um Command/DTO, recuperar uma entidade do domínio via repositório, chamar seu método, como: pedido.Cancelar() e persistir a alteração tb via repositório.

      Sacou?
      Qualquer coisa, dá um alô.
      []s

      Curtir

  2. Parabéns Robison, sempre bom ressaltar essas técnicas para melhorar nosso código. Continue escrevendo sobre. Seria bom um sobre Contextos Delimitados e Design Estratégico (Quem sabe com exemplo prático, hehe.)

    Curtir

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Blog no WordPress.com.

Acima ↑