Por que adotar um padrão de convenção de nomes em um projeto?
A convenção de nome(naming convention) é um conjunto de regras para serem utilizadas para definir padrões de, por exemplo, variáveis, classes, atributos, métodos, outras entidades e documentações.
Quando aprendo uma nova linguagem gosto de aprender também as convenções de nomes daquela linguagem, para que não acabe fazendo um mix dos nomes entre elas.
Existem diversas vantagens de se criar um documento de conversão de nomes para seu projeto, time ou companhia:
- Diminuí o esforço para entendimento de trechos de código.
- Faz Code Reviewers focarem em problemas mais importantes do código, do que em padrões de nomes e sintaxes.
- Ajuda na hora de definir nomes de classes, métodos e variáveis, caso estejam no documento, por exemplo.
- Torna mais fácil a procura de arquivos no projeto, como classes por exemplo.
- Adiciona mais consistência ao código a longo prazo.
- Apresenta um profissionalismo ao produto ou projeto, por exemplo, evitando o uso de nomes extensos para parâmetros, abreviações, entre outros.
- Ajuda a evitar colisões de nomes, para responsabilidades parecidas. Por exemplo, User, UserDTO, UserRequest.
- Dificilmente qualquer software é mantido, em toda sua vida, pelos menos desenvolvedores iniciais, tendo um documento de conversões de nomes, ajuda na padronização e consistência do código.
- Melhora a readbility do código, permitindo que novos desenvolvedores entendam melhor e mais rapidamente novos trechos de códigos.
Conversões que funcionaram comigo
Trabalhei em projetos onde propus a criação de um documento com convenções de nomes. A equipe apoiou a ideia, e a longo prazo, isso se mostrou muito eficaz. Por isso, decidi compartilhar algumas dessas convenções de nomes.
O que evitar?
- Evite o uso excessivo do sufixo DTO em todas as situações. Opte por nomes com sufixos específicos que indiquem claramente suas responsabilidades, e, se possível, limite a classe ou record ao nível do pacote.
Evite:
@Data
public class UserDTO {
private Long id;
private String name;
}
Prefira:
record UserRequest(Long id, String name) {}
- Evite adicionar o sufixo Enum para enums. Isso só causa redundância ao objeto e não agrega na legibilidade do código.
Evite:
public class StatusEnum {
ACTIVE;
INACTIVE;
PENDING;
}
Prefira:
public class Status {
ACTIVE;
INACTIVE;
PENDING;
}
- De preferência por usar record à criar classes, por causa da imutabilidade e a redução da verbosidade das classes(getters/setters).
- Se optar por usar Lombok, evite usar a anotação @Data, principalmente em entidades. Essa prática, além de criar métodos desnecessários no bytecode, pode resultar em LazyExceptions, uma vez que a anotação @Data automaticamente gera os métodos hashCode e equals para atributos definidos como Lazy.
- Evite usar final para tudo com a intenção de fazer os objetos ou variáveis imutáveis. Em vez disso, é aconselhável utilizar record do Java ou aplicar encapsulamento, não implementando os métodos set para os atributos que devem permanecer inalterados.
- Evite utilizar o sufixo Service para tudo que não seja uma classe mapeada com
@Service
. O sufixo Service pode ser muito genérico e pode não deixar claro a real intenção da classe. - Evite deixar a declaração de uma variável e a utilização dela muito distantes em um método. Prefira respeitar a Rule of Thumbs.
Nomes de classes
Para nomes de classes, tenho preferência por adicionar sufixos que ficam claro sua responsabilidade ou representação.
Para classes que possuem uma responsabilidade específica, e é utilizado em apenas um cenário, tentar restringir a classe a nível de pacote.
Classes que representam Request e Response:
Evite:
@NoArgsConstructor
@AllArgsConstructor
class NotificationUrlDTO {
private String url;
private LocalDateTime notifiedAt;
}
Prefira:
record NotificationUrlRequest(String url, LocalDateTime notifiedAt) {}
Para classes que representam objetos relacionadas a mensageria:
- As mensagens enviadas ou recebidas utilizar o sufixo Message:
record NotificationMessage(String url, LocalDateTime notifiedAt) {}
- Para classes que representam um consumidor e um produtor de mensagens, usa os sufixos Consumer e Producer respectivamente:
@Component
public class StatusCancelConsumer { // or StatusCancelProducer
private final StatusCancelService service;
@RabbitListener(queues = "${spring.rabbitmq.custom.cancel-item.queue}")
public void consume(Message message) {
var cancelMessage = toObject(message, StatusCancelMessage.class);
service.handle(cancelMessage);
}
Para classes que representam clientes REST ou Proxies para outros serviços, utilizar o sufixo Client:
@FeignClient(name = "status-client", url = "${feign.status.url}")
public interface StatusClient {
@PostMapping
NotificationResponse send(NotificationUrlRequest request);
}
Para o tamanho de nomes de parâmetros, atributos e variáveis, gosto muito da prática recomendada no Golang:
- Consistente (fácil de adivinhar).
- Pequeno (fácil de digitar).
- Preciso (fácil de entender).
E a Rule of Thumb:
Quanto maior a distância entre a declaração e o uso de uma variável, maior o nome deve ser.
Conclusão
Essas convenções de nomes são apenas recomendações e podem ser constantemente adaptadas e evoluídas pelo time.
Ter esse documento de conversões de nomes ajudará a deixar o código mais consistente a longo prazo, mesmo que vários desenvolvedores, com várias experiências diferentes, trabalhem nele. Podendo também facilitar para o desenvolvedor na definição dos nomes dentro do projeto.
Se você gostou do artigo, por favor não deixe de bater palmas 👏 (você pode fazer várias vezes), me seguir, ou até mesmo me comprar um café ☕️https://www.buymeacoffee.com/valdemarjuniorr