By Jim Shingler | October 25, 2025
Package by Layer vs Package by Feature
How Clean Architecture and Hexagonal Architecture fit in
In Java Spring Boot development, one of the most impactful architectural
choices you’ll make is how to organize your code.
Should you package by layer — like controller, service, and repository
— or by feature — like order, customer, and inventory?
And where do Clean Architecture and Hexagonal Architecture fit into all of this?
Let’s unpack the trade-offs, principles, and evolution path — with a touch of wisdom inspired by Dan Vega, who’s known for his pragmatic approach to clean, maintainable Spring Boot applications.
🧱 Package by Layer — The Traditional Approach
Example Structure
com.example.orders
├── controller
│ └── OrderController.java
├── service
│ └── OrderService.java
├── repository
│ └── OrderRepository.java
└── model
└── Order.java
Philosophy
Group classes by their technical role — all controllers together, all services together, all repositories together.
Pros
-
Clear separation of concerns
-
Familiar and easy for newcomers
-
Works fine for small or simple apps
Cons
-
Low cohesion: Each feature’s code is scattered
-
Tight coupling: Shared services and models blur boundaries
-
Poor scalability: Harder to isolate or modularize features
When to Use
Package-by-layer works well when: - You’re building a small app or
prototype
- The team is small and co-located
- You don’t anticipate splitting into modules or services later
🧩 Package by Feature — The Modern Approach
Example Structure
com.example.orders
├── order
│ ├── OrderController.java
│ ├── OrderService.java
│ ├── OrderRepository.java
│ └── Order.java
├── customer
│ ├── CustomerController.java
│ ├── CustomerService.java
│ ├── CustomerRepository.java
│ └── Customer.java
Philosophy
Group code by business capability or domain concept, not by technical role.
Pros
-
High cohesion — each feature is self-contained
-
Easier to reason about, test, and refactor
-
Natural fit for domain-driven design (DDD)
-
Enables modular ownership and microservice extraction
Cons
-
Less conventional — can confuse new developers
-
Requires discipline to avoid cross-feature coupling
-
May duplicate small utilities across features (by design)
When to Use
Use package-by-feature when: - You’re building a larger or growing
system
- You expect multiple teams or domains
- You want to evolve toward modularity or microservices
⚖️ Package-by-Layer vs Package-by-Feature
| Aspect | Package by Layer | Package by Feature |
|---|---|---|
Cohesion |
Low |
High |
Coupling |
High |
Low |
Refactorability |
Harder |
Easier |
Testing |
Broad integration tests |
Isolated feature tests |
Scalability |
Monolithic |
Modular |
Team Fit |
Centralized ownership |
Cross-functional ownership |
🧭 Enter Clean Architecture
Creator: Robert C. Martin (Uncle Bob)
Core Rule:
> Business logic should not depend on frameworks, databases, or UI.
Dependencies point inward, toward the domain.
Conceptual View
Frameworks & UI (Spring, REST)
↓
Interface Adapters (DTOs, Mappers)
↓
Application Services (Use Cases)
↓
Domain Entities (Business Rules)
Example Layout
com.example.order
├── domain
│ └── Order.java
├── application
│ └── PlaceOrderUseCase.java
├── interfaces
│ └── web
│ └── OrderController.java
└── infrastructure
└── JpaOrderRepository.java
Key Idea
Your domain and application code should know nothing about Spring,
HTTP, or JPA.
They’re just Java. The outer layers handle frameworks and adapters.
🔶 Hexagonal Architecture (Ports & Adapters)
Creator: Alistair Cockburn
Goal:
> Make your application independent of its runtime environment.
Conceptual Diagram
+------------------------+
| Adapters |
| (REST, DB, Kafka, etc) |
+------------------------+
↑ ↓
Inbound Port Outbound Port
↑ ↓
+----------------------+
| Domain Core |
| Entities & UseCases |
+----------------------+
Example Structure
com.example.order
├── application
│ ├── port
│ │ ├── inbound
│ │ │ └── PlaceOrderUseCase.java
│ │ └── outbound
│ │ └── OrderRepository.java
│ └── service
│ └── PlaceOrderService.java
├── domain
│ └── Order.java
├── adapters
│ ├── inbound
│ │ └── OrderController.java
│ └── outbound
│ └── JpaOrderRepository.java
Focus
Clean architecture focuses on dependency direction.
Hexagonal focuses on communication boundaries (ports and adapters).
In practice, most modern teams blend both.
🧩 The Hybrid: Package-by-Feature + Clean/Hexagonal Inside
Here’s the best of both worlds:
com.example.shop
├── order
│ ├── domain
│ ├── application
│ ├── adapters
│ │ ├── inbound
│ │ └── outbound
│ └── infrastructure
├── customer
│ ├── domain
│ ├── application
│ ├── adapters
│ └── infrastructure
└── shared
├── config
└── common
Each feature: - Owns its own domain, application, and adapters
- Respects Clean/Hexagonal layering internally
- Can evolve independently or be extracted later
🧠 Why It Works
| Principle | Benefit |
|---|---|
Feature-first |
Keeps code cohesive and understandable |
Layering within feature |
Enforces clean boundaries |
Isolation |
Easier testing and refactoring |
Scalability |
Each feature can evolve separately |
Spring alignment |
Works naturally with component scanning |
This hybrid structure is exactly what many experienced Spring Boot engineers (Dan Vega included) use for production systems.
🧩 Take on All This
It boils down to practicality over purity.
Remember do not to overcomplicate your first commit.
1. Start Simple
``You don’t earn the right to a Clean Architecture until you have complexity to clean.''
Begin with package-by-feature. Refactor toward layers as needed.
2. Use Spring Idiomatically
Don’t fight the framework — let Spring handle DI and config. Keep your
domain pure, but use @Service, @Repository, and @RestController
where appropriate.
3. Organize for Humans
``Your package structure should tell the story of your app — what it does, not just what tech it uses.''
4. Think in Use Cases
Replace CRUD-style services with explicit, business-driven use cases.
interface PlaceOrderUseCase {
OrderResponse placeOrder(OrderRequest request);
}
5. Design for Testability
``If it’s hard to test, it’s in the wrong place.''
Keep domain logic testable without Spring.
Controllers and repos should have their own slice tests.
6. Optimize Developer Experience
``If your app takes longer to boot than it takes to pour a coffee, fix your architecture.''
Use fast feedback loops, Spring DevTools, and modular scanning.
7. Refactor Toward Clean
``Good architecture is what lets you keep shipping features without hating yourself in six months.''
🧩 The Evolution Path
| Stage | Structure | Pros | Cons |
|---|---|---|---|
1. Package-by-layer |
|
Simple |
Scattered logic |
2. Package-by-feature (flat) |
|
Feature cohesion |
Weak boundaries |
3. Package-by-feature + internal layers |
|
Cohesion + discipline |
Slightly more setup |
That’s the natural journey — and it’s exactly what Dan Vega (and most senior Spring engineers) would recommend.
💡 Key Takeaways
-
Package by feature for cohesion.
-
Layer within each feature for clean boundaries.
-
Evolve gradually — architecture maturity comes with complexity.
-
Use Spring idiomatically — it already supports this style.
-
Design for humans and testability — that’s the real ``clean.''
``Group by business capability, structure by architectural boundary.''
— A principle for every scalable Spring Boot codebase.
Additional Resources
The Book (Primary Source)
Clean Architecture: A Craftsman’s Guide to Software Structure and Design by Robert C. Martin (Uncle Bob) O’Reilly Media · Google Books
This is the definitive reference — covering architecture goals, design principles, component boundaries, and proven patterns for maintainable systems.
Blog Post by Uncle Bob
“The Clean Architecture” — by Robert C. Martin Published on the Clean Coder Blog
Good for hearing the ideas in his own voice — frameworks, rules, and why the dependency direction matters.
-
Clean Coder Blog:: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
Original Article by the Creator (Hexagonal Architecture)
Hexagonal Architecture — by Alistair Cockburn
Alistair Cockburn’s write-up is a must-read — it provides the foundational thinking behind the Ports and Adapters pattern and is ideal for grounding your understanding.
-
alistair.cockburn.us:: https://alistair.cockburn.us/hexagonal-architecture/
-
alistaircockburn.com:: https://alistaircockburn.com/hexagonal-architecture/
AWS Prescriptive Guidance
Hexagonal Architecture – AWS Prescriptive Guidance
The Amazon Web Services documentation offers a practical overview of Hexagonal Architecture and when to apply it.
Useful for seeing how the pattern is applied in scalable, cloud-native contexts.