Pular para conteúdo

Padrões Java

Este documento define os padrões oficiais de desenvolvimento Java para projetos da Arkorizon.

O foco deste guia é linguagem, estilo, nomenclatura, organização de código e boas práticas gerais. Regras arquiteturais completas ficam em Arquitetura Hexagonal. Testes ficam em Padrões de Testes Java.

Versão padrão

A versão padrão para novos projetos Java é:

Java 21 LTS

Projetos existentes podem permanecer em versões anteriores apenas quando houver restrição técnica ou operacional documentada.

Princípios

  • Código Java deve ser escrito em inglês.
  • Código deve ser simples, explícito e fácil de revisar.
  • Nomes devem expressar intenção de negócio ou responsabilidade técnica.
  • Evite abstrações antes de existir necessidade real.
  • Prefira imutabilidade sempre que possível.
  • Não misture regras de negócio com frameworks.
  • Erros devem ser explícitos e rastreáveis.
  • Código deve favorecer testes unitários simples.

Idioma do código

Tudo no código Java deve estar em inglês:

  • classes;
  • interfaces;
  • records;
  • métodos;
  • variáveis;
  • pacotes;
  • exceptions;
  • mensagens de exception;
  • comentários;
  • Javadocs.

Exceção: arquivos de internacionalização (i18n) podem conter traduções.

Nomenclatura

Elemento Padrão Exemplo
Classe PascalCase AuthTokenGenerator
Interface PascalCase LoginUseCase
Método camelCase generateToken
Variável camelCase userId
Constante UPPER_SNAKE_CASE MAX_LOGIN_ATTEMPTS
Package minúsculo com pontos com.arkorizon.iam.auth.domain
Enum PascalCase OrderStatus
Exception sufixo Exception UserNotFoundException
DTO sufixo DTO LoginRequestDTO
Command sufixo Command CreateUserCommand
Result sufixo Result UserResult
JPA Entity sufixo Entity UserEntity
Mapper sufixo Mapper UserMapper
Adapter sufixo Adapter UserPersistenceAdapter
Port sufixo Port FindUserByEmailPort

Abreviações

Não use abreviações obscuras.

Ruim:

UsrCtrl
AuthSvc
PwdVal
RespDTO

Bom:

UserController
AuthService
PasswordValidator
AuthTokenResponseDTO

Abreviações amplamente conhecidas são permitidas quando tornam o nome mais claro:

DTO
API
URL
URI
HTTP
JWT
UUID

Records

Use record para objetos imutáveis de dados.

Bons usos:

  • Command;
  • Result;
  • DTOs;
  • value objects simples;
  • query parameters;
  • mensagens internas imutáveis.

Exemplo:

public record CreateUserCommand(
        String name,
        String email
) {
}

Evite record quando:

  • o objeto possui ciclo de vida complexo;
  • precisa proteger mutações;
  • possui muitas invariantes internas;
  • representa entidade de domínio rica;
  • representa entidade JPA.

Classes

Use class quando existir comportamento, ciclo de vida ou invariantes relevantes.

public final class LoginAttemptPolicy {

    private final int maxAttempts;

    public LoginAttemptPolicy(final int maxAttempts) {
        if (maxAttempts <= 0) {
            throw new IllegalArgumentException("Max attempts must be positive");
        }

        this.maxAttempts = maxAttempts;
    }

    public boolean isBlocked(final int failedAttempts) {
        return failedAttempts >= maxAttempts;
    }
}

Imutabilidade

Prefira objetos imutáveis.

  • Use final em campos sempre que possível.
  • Evite setters sem necessidade.
  • Prefira construtores ou factories para criar objetos válidos.
  • Use coleções imutáveis quando retornar dados.
public final class UserProfile {

    private final String displayName;

    public UserProfile(final String displayName) {
        this.displayName = displayName;
    }

    public String displayName() {
        return displayName;
    }
}

Uso de final

Use final quando ajudar a deixar a intenção clara.

Recomendado:

public AuthTokenResult execute(final LoginCommand command) {
    final User user = findUserByEmailPort.findByEmail(command.email())
            .orElseThrow(InvalidCredentialsException::new);

    return tokenIssuerPort.issueFor(user);
}

Não transforme final em ruído. Em código muito simples, a clareza vem primeiro.

Uso de var

Use var apenas para variáveis locais quando o tipo for óbvio.

Bom:

var users = userRepository.findAll();

Evite quando o tipo não for claro:

var result = externalClient.execute(request);

Nesse caso, prefira tipo explícito.

Null handling

Evite retornar null.

Use:

  • Optional<T> para ausência em retorno;
  • collections vazias em vez de null;
  • exceptions para falhas de negócio;
  • validação explícita em fronteiras.
public interface FindUserByEmailPort {

    Optional<User> findByEmail(String email);
}

Não use Optional em campos, DTOs ou parâmetros sem motivo forte.

Exceptions

Exceptions devem ter significado claro.

public final class UserNotFoundException extends RuntimeException {

    public UserNotFoundException(final String email) {
        super("User not found by email: " + email);
    }
}

Regras:

  • Use RuntimeException para falhas de domínio e aplicação.
  • Mensagens devem estar em inglês.
  • Não engula exceptions silenciosamente.
  • Não use Exception genérica quando existe significado de negócio.
  • Não retorne null para representar erro.

Logging

Use logs para diagnóstico, não para substituir tratamento de erro.

Regras:

  • Não logar secrets, tokens, senhas ou dados sensíveis.
  • Logs devem ter contexto suficiente.
  • Evite logs excessivos em loops ou fluxos de alto volume.
  • Use níveis corretamente.
Nível Uso
trace Diagnóstico muito detalhado, raro em produção.
debug Diagnóstico técnico.
info Evento relevante de negócio ou operação.
warn Situação inesperada, mas recuperável.
error Falha que exige atenção ou interrompe fluxo.

Exemplo:

log.warn("Login rejected for inactive user: userId={}", user.id());

Collections

Prefira interfaces nos contratos:

List<User> users
Set<Role> roles
Map<String, String> metadata

Evite expor implementação sem necessidade:

ArrayList<User> users
HashMap<String, String> metadata

Retorne coleções vazias em vez de null.

return List.of();

Datas e horários

Use Java Time API.

Caso Tipo recomendado
Instante absoluto Instant
Data sem horário LocalDate
Data e hora local LocalDateTime
Duração Duration
Período calendário Period

Evite Date e Calendar em código novo.

Valores monetários

Use BigDecimal para dinheiro.

Não use double ou float para valores monetários.

public record Money(
        BigDecimal amount,
        String currency
) {
}

Streams

Use streams quando melhorarem legibilidade.

Bom:

final List<String> activeEmails = users.stream()
        .filter(User::active)
        .map(User::email)
        .toList();

Evite streams complexas demais. Se a cadeia exige muitos passos, extraia métodos ou use código imperativo claro.

Anotações de framework

Anotações Spring, JPA, Jackson e Bean Validation devem ficar nas bordas.

Permitido em adapters:

@RestController
@Entity
@JsonProperty
@NotBlank

Proibido no domínio:

@Service
@Entity
@RequestBody
@NotBlank

O domínio deve permanecer Java puro, conforme Arquitetura Hexagonal.

Hexagonal Architecture

Projetos backend da Arkorizon devem seguir Arquitetura Hexagonal quando houver regra de negócio relevante.

Este documento Java define estilo de código. A regra arquitetural completa fica em:

Resumo prático:

Conceito Onde fica
Use case domain.service
Input port domain.port.in
Output port domain.port.out
Domain model domain.model
Command domain.command
Result domain.result
Controller adapter.in.web.controller
DTO adapter.in.web.dto
JPA Entity adapter.out.persistence.entity
Repository adapter.out.persistence.repository
Persistence adapter adapter.out.persistence.adapter
Bean config config

Use cases

Use cases devem ser interfaces no input port e implementações em Java puro.

public interface CreateUserUseCase {

    UserResult execute(CreateUserCommand command);
}
public final class CreateUserUseCaseImpl implements CreateUserUseCase {

    @Override
    public UserResult execute(final CreateUserCommand command) {
        // Business behavior
    }
}

Regras:

  • Não usar @Service no use case do núcleo.
  • Não injetar repository no use case.
  • Não receber DTO no use case.
  • Não retornar JPA entity do use case.
  • Usar ports para dependências externas.

DTOs

DTOs pertencem aos adapters.

public record LoginRequestDTO(
        @NotBlank @Email String email,
        @NotBlank String password
) {
}

Regras:

  • DTO pode usar Bean Validation.
  • DTO pode usar anotações Jackson quando necessário.
  • DTO não deve entrar no domínio.
  • DTO não deve conter regra de negócio.

Entities JPA

Entities JPA pertencem ao adapter de persistência.

@Entity
@Table(name = "tb_user")
public class UserEntity {

    @Id
    private UUID id;

    @Column(name = "email", nullable = false)
    private String email;

    protected UserEntity() {
    }
}

Regras:

  • Entity JPA deve ter sufixo Entity.
  • Entity JPA não é domain model.
  • Entity JPA não deve aparecer em controller.
  • Entity JPA não deve ser retornada por use case.
  • Entity JPA não deve conter regra de negócio.

Mappers

Use mappers para atravessar bordas.

@Mapper(componentModel = "spring")
public interface UserWebMapper {

    CreateUserCommand toCommand(CreateUserRequestDTO request);

    UserResponseDTO toResponse(UserResult result);
}

Regras:

  • Web mapper traduz DTO para command/result.
  • Persistence mapper traduz entity para domain model.
  • Não use um mapper genérico para tudo.
  • Mapper deve ter sufixo Mapper.

Comentários e Javadocs

Comentários devem explicar intenção, regra ou decisão, não repetir o código.

Ruim:

// Sets the user name
user.setName(name);

Bom:

// External providers may return uppercase emails; normalize before lookup.
final String normalizedEmail = email.toLowerCase(Locale.ROOT);

Use Javadocs em APIs públicas, SDKs, bibliotecas e contratos que serão consumidos por outros times.

O que é proibido

  • Código Java em português.
  • Abreviações obscuras.
  • null como retorno para ausência comum.
  • Regras de negócio em controller.
  • Repository sendo chamado diretamente pelo controller.
  • JPA entity usada como response DTO.
  • DTO entrando em use case.
  • Spring/JPA no domínio.
  • Utils ou Helper genéricos sem responsabilidade clara.
  • catch (Exception ignored).
  • Logs com tokens, senhas ou secrets.

Checklist de revisão Java

  • O código está em inglês.
  • Nomes expressam intenção.
  • Não há abreviações obscuras.
  • Objetos são imutáveis quando possível.
  • record foi usado apenas onde faz sentido.
  • Ausência é modelada sem null desnecessário.
  • Exceptions têm significado claro.
  • Logs não expõem dados sensíveis.
  • Domínio não depende de framework.
  • DTOs, entities e mappers ficam nas bordas corretas.
  • Use cases são testáveis sem Spring.