Organizando Seu Projeto Java com Package by Feature: Um Guia para Iniciantes

Tempo de leitura: 6 minutos

Se você já trabalhou em um projeto Java, provavelmente já viu a estrutura tradicional Package by Layer”, onde o código é organizado por camadas técnicas:

com.example.projeto

├── controller/
├── service/
├── repository/
├── model/
├── dto/

Embora essa abordagem seja comum, ela pode trazer problemas à medida que o projeto cresce. Imagine ter que alterar uma funcionalidade e precisar navegar por várias pastas diferentes só para encontrar todas as classes relacionadas. Isso pode ser frustrante e levar a erros, especialmente em projetos grandes com muitos desenvolvedores.

É aí que entra o Package by Feature, uma maneira mais eficiente de organizar seu código, agrupando tudo o que pertence a uma mesma funcionalidade em um único lugar. Essa abordagem promove uma visão mais centrada no domínio do negócio, facilitando o entendimento e a manutenção do código.

Neste artigo, vamos explorar:  

  1. O que é Package by Feature?
  2. Por que ele é melhor que Package by Layer?
  3. Como aplicar na prática em um projeto Quarkus?
  4. A Rule of Three e quando compartilhar código entre features.
  5. Dicas para evitar armadilhas comuns ao adotar Package by Feature.

1. O Que é Package by Feature?

Em vez de separar o código por camadas (controller, service, repository), o Package by Feature organiza o projeto por funcionalidades de negócio. Por exemplo, em um sistema de e-commerce, teríamos:

com.example.ecommerce  

├── user/
├── product/
├── order/
├── payment/

Dentro de cada pacote, ficam todas as classes necessárias para aquela funcionalidade:

  • Controller (API REST)
  • Service (regras de negócio)
  • Repository (banco de dados)
  • DTOs e entidades

Isso torna o código mais fácil de entender e manter, pois tudo relacionado a uma funcionalidade está no mesmo lugar. Além disso, essa organização reflete melhor o domínio do negócio, alinhando o código com os requisitos reais do sistema.

2. Por Que Package by Feature é Melhor?

A abordagem Package by Feature oferece várias vantagens em comparação com Package by Layer. Aqui estão os principais benefícios:

  • Facilita a Manutenção : Se você precisa alterar algo em Pedidos, tudo está no pacote order. Não precisa procurar em várias pastas, o que economiza tempo e reduz erros.
  • Melhora a Coesão: Classes que trabalham juntas ficam juntas, o que aumenta a clareza e reduz o acoplamento entre diferentes partes do sistema.
  • Reduz Conflitos no Git: Diferentes times podem trabalhar em features diferentes sem se atrapalhar, já que cada funcionalidade está isolada em seu próprio pacote.
  • Prepara para Microsserviços: Se um dia o projeto crescer, cada feature pode ser extraída para um serviço independente com mínimo esforço, já que o código já está organizado por funcionalidade.
  • Alinhamento com o Domínio: Essa abordagem reflete diretamente o domínio do negócio, facilitando a comunicação entre desenvolvedores e stakeholders não técnicos.

3. Como Aplicar em um Projeto Quarkus?

Vamos criar um exemplo simples de um sistema de loja online usando Quarkus, um framework Java moderno otimizado para ambientes nativos e em nuvem:

Estrutura do Projeto

src/main/java/com/ecommerce

└── core/
├── exceptions/
├── config/
└── security/
└── user/
├── UserResource.java (Controller)
├── UserService.java
├── UserRepository.java
└── User.java (Entidade)
└── product/
├── ProductResource.java
├── ProductService.java
├── ProductRepository.java
└── Product.java (Entidade)
└── order/
├── OrderResource.java
├── OrderService.java
├── OrderRepository.java
└── Order.java (Entidade)

Exemplo: Pacote “User

01// UserResource.java (Controller)
02package com.ecommerce.user;
03import jakarta.inject.Inject;
04import jakarta.ws.rs.GET;
05import jakarta.ws.rs.Path;
06import java.util.List;
07
08@Path(“/users”)
09public class UserResource {
10 @Inject
11 UserService userService;
12 @GET
13 public List getAll() {
14 return userService.findAll();
15 }
16}
01// UserService.java (Regras de negócio)
02package com.ecommerce.user;
03import jakarta.enterprise.context.ApplicationScoped;
04import jakarta.inject.Inject;
05import java.util.List;
06
07@ApplicationScoped
08public class UserService {
09
10 @Inject
11 UserRepository userRepository;
12 public List findAll() {
13 return userRepository.listAll();
14 }
15}
01// UserRepository.java (Banco de dados)
02package com.ecommerce.user;
03import io.quarkus.hibernate.orm.panache.PanacheRepository;
04import jakarta.enterprise.context.ApplicationScoped;
05
06@ApplicationScoped
07public class UserRepository implements PanacheRepository {
08 // Métodos customizados (se necessário)
09}
01// User.java (Entidade)
02package com.ecommerce.user;
03import jakarta.persistence.Entity;
04import jakarta.persistence.Id;
05
06@Entity
07public class User {
08 @Id
09 private Long id;
10 private String name;
11 private String email;
12
13 // Getters e setters
14}

Perceba que tudo sobre usuários está em um único lugar! Essa organização torna mais fácil adicionar novas funcionalidades, como autenticação ou gerenciamento de perfis, sem afetar outras partes do sistema.

4. A Rule of Three – Quando Compartilhar Código?

Uma dúvida comum é: E se várias features precisarem do mesmo código?  

Aqui entra a “Rule of Three (Regra do Três):  

Se você repetir o mesmo código 3 vezes, extraia para um lugar comum.

Essa regra ajuda a evitar a criação prematura de código compartilhado, que pode levar a abstrações desnecessárias e acoplamento indesejado.

Exemplo Prático

Suponha que três features (order, payment, invoice) precisem gerar um código aleatório:

1. Primeira vez (em OrderService):  

01public String generateOrderCode() {
02 return “PAY-” + UUID.randomUUID().toString().substring(0, 8);
03}

2. Segunda vez (em PaymentService)

01public String generatePaymentRef() {
02 return “PAY-” + UUID.randomUUID().toString().substring(0, 8);
03}

3. Terceira vez (em InvoiceService):  

01public String generateInvoiceNumber() {
02 return “INV-” + UUID.randomUUID().toString().substring(0, 8);
03}

Agora, em vez de repetir, extraímos para core:  

01// core/util/CodeGenerator.java
02package com.ecommerce.core.util;
03import java.util.UUID;
04public class CodeGenerator {
05 public static String generate(String prefix) {
06 return prefix + “-” + UUID.randomUUID().toString().substring(0, 8);
07 }
08}

Agora, cada feature pode usar:

01String orderCode = CodeGenerator.generate(“ORD”);
02String paymentRef = CodeGenerator.generate(“PAY”);
03String invoiceNumber = CodeGenerator.generate(“INV”);

Assim, evitamos duplicação sem criar compartilhamento desnecessário.

5. Dicas para Evitar Armadilhas Comuns

Ao adotar Package by Feature, algumas armadilhas podem surgir. Aqui estão algumas dicas para evitá-las:

Não Crie Pacotes Demasiado Genéricos

Evite pacotes como `utils` ou `common` para tudo. Use a Rule of Three para decidir o que realmente deve ser compartilhado.

Evite Acoplamento Excessivo

Mantenha as features o mais independentes possível. Se uma feature depende muito de outra, talvez elas devam ser uma única feature.

Não Ignore o Domínio

Antes de criar pacotes, entenda bem o domínio do negócio. Converse com stakeholders para garantir que as features reflitam os conceitos reais do sistema.

Use Ferramentas de Refatoração

Ferramentas como IntelliJ IDEA ou Eclipse facilitam a reorganização do código para Package by Feature sem quebrar dependências.

Teste a Organização

À medida que o projeto cresce, revise a estrutura dos pacotes para garantir que ela ainda faz sentido. Ajuste conforme necessário.

6. Conclusão

O Package by Feature é uma maneira mais intuitiva e sustentável de organizar projetos Java, especialmente com frameworks modernos como Quarkus. Ele alinha o código com o domínio do negócio, facilita a manutenção e prepara o projeto para escalar.

  • Benefícios principais:
    • Código mais fácil de encontrar e manter.
    • Menos conflitos no time.
    • Preparado para escalar.
    • Melhor alinhamento com o domínio do negócio

Dica final:

Comece com features isoladas e, usando a Rule of Three, identifique o que realmente deve ser compartilhado. Se possível, valide a estrutura com o time para garantir que todos estão alinhados. Caso você tenha gostado desse artigo e ele tenha sido útil, e quiser contribuir com nosso trabalho, faça uma contribuição, use o QRCode a seguir!!!

QRCode

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *