Se tem uma coisa que diferencia o dev sênior do júnior não é saber mais frameworks, é pensar diferente.
Enquanto o júnior quer “fazer funcionar”, o sênior quer “fazer funcionar bem — e continuar funcionando daqui seis meses sem ninguém xingar o autor”. 😅
E é sobre isso que a gente vai falar hoje: como deixar seu código Spring Boot mais limpo, modular e fácil de manter.
Nada de teoria chata — aqui é Borapraticar com exemplos reais e dicas aplicáveis no dia a dia.
🧭 Por que código limpo importa e muito
A verdade é simples:
Código limpo não é luxo — é sobrevivência em time.
Quando o projeto cresce, cada método bagunçado vira uma bomba-relógio.
E quem já pegou um service com 1000 linhas sabe… 😬
Código limpo ajuda em três coisas:
- Legibilidade: qualquer dev entende rápido o que está acontecendo.
- Manutenibilidade: mudar algo não vira um pesadelo.
- Testabilidade: dá pra testar partes pequenas e previsíveis.
No fundo, a meta é uma só:
Cada classe deve ter uma única responsabilidade — e fazer isso bem feito.
💩 O service verboso ou o que não fazer
Vamos ver um exemplo que parece inofensivo, mas é o começo da bagunça:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void createUser(String name, String email, String role) {
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email");
}
var user = new User();
user.setName(name);
user.setEmail(email);
user.setRole(role);
user.setCreatedAt(LocalDateTime.now());
userRepository.save(user);
System.out.println("User created successfully!");
}
}
Parece ok, né? Mas olha só o tanto de problema aqui:
- A validação tá dentro do service — deveria estar isolada.
- O service faz tudo: valida, cria entidade, salva e ainda imprime log (😅).
System.out.printlnem código de produção é pecado mortal.- E se amanhã mudar a regra de email? Vai ter que caçar o método no meio de tudo.
O resultado é um service gordo, difícil de testar e que quebra fácil quando o sistema cresce.
🧰 Borapraticar refatorando: do caos à clareza
Agora sim, Borapraticar e deixar isso bonito.
A primeira coisa é dar estrutura ao projeto.
🗂️ Estrutura de pacotes limpa:
com.example.app ├── controller ├── service ├── domain │ ├── entity │ └── validator ├── repository ├── exception └── dto
Essa estrutura já força uma mentalidade:
Cada camada tem seu papel — e nada de misturar responsabilidades.
🧩 Passo 1 — Criar o validador
UserValidator.java
@Component
public class UserValidator {
public void validate(User user) {
if (user.getEmail() == null || !user.getEmail().contains("@")) {
throw new InvalidEmailException();
}
}
}
Agora a validação tem um dono: o UserValidator.
Ficou coeso e testável.
⚙️ Passo 2 — Deixar o service menos verboso e elegante
UserService.java
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final UserValidator userValidator;
public User create(User user) {
userValidator.validate(user);
user.setCreatedAt(LocalDateTime.now());
return userRepository.save(user);
}
}
Agora o service faz só o que é dele: orquestrar o fluxo.
Nada de regras de negócio ou prints aleatórios.
🌐 Passo 3 — Controller limpo e direto ao ponto
UserController.java
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity<User> create(@RequestBody User user) {
var savedUser = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
}
Simples, legível, e fácil de testar.
O controller só recebe a requisição, delega pro service e retorna a resposta.
No fim, cada classe faz uma coisa bem feita:
UserValidator→ valida.UserService→ coordena.UserController→ expõe o endpoint.
E o resultado é um código que qualquer dev lê e entende em segundos.
Clean, modular e pronto pra crescer.
🧠 Mentalidade Sênior — Coesão, Acoplamento e Responsabilidade Única
Agora que o código ficou limpo, é hora de entender o porquê disso tudo.
Essa é a parte onde o júnior vira pleno e o pleno começa a pensar como sênior.
Vamos falar dos três pilares dessa mentalidade:
🧩 Coesão
Coesão é o quanto os métodos de uma classe estão relacionados entre si.
Alta coesão significa que a classe faz uma coisa só e bem feita.
🗣️ Exemplo prático:
Se sua classe “UserService” está cuidando de email, senha, logs e notificações…
ela tá com baixa coesão. 😬
🔗 Acoplamento
Acoplamento é o quanto uma classe depende das outras.
Baixo acoplamento significa que você pode mudar uma parte sem quebrar o resto.
🗣️ Exemplo prático:
Se mudar uma linha no
UserValidatorquebra oUserService,
seu código tá acoplado demais.
🧱 Responsabilidade Única (SRP do SOLID)
É o princípio que amarra tudo:
Cada classe deve ter um único motivo para mudar.
O UserValidator muda se a regra de validação mudar.
O UserService muda se o processo de criação mudar.
E o UserController muda se o endpoint ou o retorno mudar.
Simples assim.
Quando o código segue esses três princípios:
- Ele é fácil de testar
- Fácil de entender
- E o mais importante: fácil de evoluir sem medo.
🧩 No próximo BoraPraticar, a gente vai fazer um mini workshop de revisão de PRs:
ver código ruim, sugerir melhorias com empatia e entender como uma boa revisão ajuda o time inteiro a crescer.
Mas por hoje, missão cumprida: teu código ficou limpo, modular e com mentalidade de sênior 🚀