Spring Boot Project Structure — Wrong Package Causes 404
404 on all endpoints with clean startup? The @SpringBootApplication was in a sibling package.
- @SpringBootApplication placement determines what component scan finds — it must be in the root package, not a sub-package
- Layered Architecture separates Controllers, Services, and Repositories — a change in DB schema does not break the API contract if you use DTOs correctly
- DTOs decouple your API contract from your database entities — never expose @Entity directly in controller responses
- Package-by-Layer works for small projects (fewer than 20 entities, fewer than 5 developers) — switch to Package-by-Feature when adding a single feature requires touching more than 3 packages
- Placing the main class in a sub-package is the most common startup failure — Spring scans downward from the main class location, not sideways
- Business logic in controllers is the fastest path to unmaintainable code — keep controllers thin, push logic to @Service where it is testable without an HTTP context
- Add ArchUnit tests to enforce structural rules — catches package placement violations before they reach production
Think of Spring Boot Project Structure as the floor plan of a professional kitchen. A well-designed kitchen has a prep station, a cooking station, a plating station, and a cleaning station. Every chef who walks in knows where to find the knives, where to fire the orders, and where the plates go. Nobody puts the trash cans on the prep counter.
A Spring Boot project works the same way. The Controller layer is the front-of-house — it takes requests from the outside world. The Service layer is the kitchen — that is where the actual work happens. The Repository layer is the walk-in cooler — it stores and retrieves ingredients (data). Each station has a defined job, and the rule is that the front-of-house does not go into the cooler directly. Requests flow in one direction: Controller → Service → Repository.
Spring Boot does not force this layout on you at gunpoint, but it provides a 'Convention over Configuration' baseline that every experienced Java team gravitates toward — because the alternative, which is everyone organizing code however they feel that day, produces kitchens where nobody can find anything and nothing ever gets cooked on time.
Spring Boot Project Structure is a foundational concept that determines how maintainable, testable, and navigable your codebase remains as it grows from a proof-of-concept to a production system with 50+ classes and multiple developers.
While Spring Boot is deliberately unopinionated about folder layout — it does not force a specific package hierarchy — it provides a 'Convention over Configuration' baseline that the vast majority of professional teams adopt. Understanding why that convention exists, what it buys you, and when to deviate from it is what separates engineers who can maintain large codebases from engineers who build things that nobody else can safely touch.
In this guide we break down exactly what a production-grade Spring Boot project structure looks like, why it was designed this way, and how to scale it correctly as your project grows from a handful of entities to dozens of bounded contexts owned by different teams. We cover the mechanics of component scanning, the DTO firewall pattern, the Package-by-Layer versus Package-by-Feature decision, multi-stage Docker builds, and how to use ArchUnit to enforce structural rules automatically.
By the end you will have both the conceptual understanding and practical code examples to structure a Spring Boot project confidently — one that a new engineer can navigate in an hour rather than a week.
What Is Spring Boot Project Structure and Why Does It Exist?
Spring Boot Project Structure is a set of conventions for organizing code that emerged from years of painful experience with the alternative — Java EE applications where business logic lived in JSPs, database queries were embedded in UI controllers, and the only way to understand what a class did was to read all of it.
The structure exists to enforce a property called 'low coupling, high cohesion': classes that work together are near each other, and classes that work at different levels of abstraction are separated by clearly defined boundaries. The Controller layer handles HTTP translation. The Service layer handles business rules. The Repository layer handles data access. No layer reaches past the one directly below it.
Spring Boot's 'Convention over Configuration' principle means that if you put the @SpringBootApplication class in the root package, the entire scan-and-wire mechanism happens automatically. Spring discovers @RestController, @Service, @Repository, and @Component beans by scanning downward from the main class location. No XML. No explicit bean registration. No manual wiring. The package structure is the configuration.
This is why @SpringBootApplication placement is not a style preference — it is a functional requirement. Move the main class to a sub-package and component scan becomes a partial scan, silently skipping every class that is not beneath it.
The structure below represents the standard layout for a production Spring Boot application. Every directory has a defined purpose and a defined scope of responsibility.
- @SpringBootApplication in the root package means component scan covers all sub-packages automatically — no @ComponentScan annotation needed unless you span multiple package trees
- Layered Architecture is a one-way dependency rule: Controller → Service → Repository. Services never import from controllers. Repositories never import from services. Violations create circular dependencies and destroy testability.
- DTOs are the contract firewall between your API and your database schema — the ProductResponse DTO defines what clients receive, and changing the Product @Entity does not break that contract as long as the mapper bridges the difference
- The config/ package holds @Configuration classes for cross-cutting infrastructure: security, CORS, OpenAPI documentation, scheduled tasks, async configuration. It does not hold business logic — that belongs in service/
- The exception/ package centralizes error handling — @RestControllerAdvice in GlobalExceptionHandler catches exceptions thrown anywhere in the service layer and translates them to consistent HTTP error responses
- The test/ mirror of the main/ structure enforces that every layer is tested at the right level: controllers with MockMvc, services with plain JUnit, repositories with @DataJpaTest
Common Mistakes and How to Avoid Them
The mistakes teams make with Spring Boot project structure tend to cluster around three root causes: incorrect @SpringBootApplication placement (structural), leaking internals through the API layer (security and coupling), and not adapting the package strategy as the project grows (organizational).
The first mistake is the most immediately damaging because it is invisible. A main class in the wrong package produces a clean startup with a broken application — no errors, no warnings, just 404s for every endpoint. The second mistake is gradual — exposing @Entity objects works fine until a security audit or a schema change proves it does not. The third mistake is a slow tax that compounds over months.
Below is the production-grade Dockerfile that belongs at the root of the project. The multi-stage build pattern is the standard for 2026 Spring Boot deployments — it keeps the final image lean by excluding Maven, source code, and build artifacts, and it uses layer caching to avoid re-downloading dependencies when only application code changes.
The Bean That Could Not Be Found — @SpringBootApplication in the Wrong Package
- @SpringBootApplication must be in the root package that is the direct parent of all other packages — component scan goes downward into sub-packages, not sideways into sibling packages
- A successful startup with no errors does not mean all beans were created — Spring does not know what you intended to register, only what it found during the scan
- The absence of 'Mapped' lines in the startup log is a reliable symptom of missing controller registration — check this before debugging routing configuration
- Add ArchUnit tests that enforce the main class package location — structural violations should fail the build, not fail a production deployment
- When every endpoint returns 404 with a healthy application, check component scan coverage before checking routing, security, or serialization
Key takeaways
Common mistakes to avoid
6 patternsPlacing the @SpringBootApplication class in a sub-package instead of the root package
classes().that().areAnnotatedWith(SpringBootApplication.class).should().resideInAPackage("io.thecodeforge.myapp"). If code legitimately spans multiple package trees, add @ComponentScan(basePackages = {"io.thecodeforge", "com.other"}) explicitly rather than relying on the default scan.Returning @Entity objects directly from @RestController methods — leaking database structure as the API contract
Embedding business logic in @RestController methods — fat controllers
Circular dependencies between @Service classes — ServiceA depends on ServiceB, ServiceB depends on ServiceA
Not separating environment-specific configuration — hardcoded database URLs, API keys, and feature flags in application.properties
Over-engineering with Package-by-Feature too early — 15 packages for a 5-entity application
Interview Questions on This Topic
Why is it recommended to place the main application class in the root package? Explain the mechanics of @ComponentScan in this context.
Frequently Asked Questions
That's Spring Boot. Mark it forged?
3 min read · try the examples if you haven't