John Roest

Package Structure

Package structure, keep it simple!

When you generate a new Spring Boot project, the framework establishes a default package structure. It looks basic. Many developers, particularly those who prefer organizing things their own way, are tempted to move files around, create custom folders, or break from the conventions.

The default structure is not arbitrary. It is designed to work with how Spring Boot scans your code, wires your beans, and loads configurations. Beyond the technical considerations, there is a practical benefit that matters more over time: consistency.

Imagine joining a new team, opening a Spring Boot repository, and recognizing the structure immediately. No questions about where controllers live, why there are three different folders for configuration, or why the main class is buried in an unusual subpackage. You just navigate to the right place, because the structure matches every other Spring Boot project you have ever opened. That recognition shortens onboarding and reduces the cognitive overhead of working in an unfamiliar codebase.

The Default Structure#

A typical Spring Boot project from Spring Initializr looks like this:

src/main/java/com/example/demo
    DemoApplication.java
    controller/
    service/
    repository/
    model/

Each directory has a clear, bounded responsibility:

  • controller — REST controllers and request handling
  • service — business logic
  • repository — data access
  • model — domain objects and entities

Additional packages are common—config, security, dto—but they extend the pattern rather than replace it. Every Spring developer understands this layout. A new team member can open controller/ and immediately understand how endpoints are structured, without needing a walkthrough.

Why Consistency Beats Creativity#

Custom structures tend to start with good intentions. One developer splits controllers into rest/ and web/. Another prefers handlers/. Over months, the project accumulates naming conventions that only the original authors understand.

Fast forward a year. The team has turned over. Nobody remembers why certain packages exist. The structure has become an obstacle rather than a guide.

Sticking to the defaults eliminates this problem. The package names are not perfect English—controller versus controllers, for example, is a minor inconsistency—but they are universal. Anyone familiar with Spring Boot knows what to expect. That predictability is worth more than the short-term satisfaction of a custom layout.

Technical Reasons to Keep the Defaults#

Spring Boot automatically scans for components starting from the package of the main application class. Placing @SpringBootApplication in the root package means every subpackage—controller, service, repository—is included in the component scan without additional configuration.

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

If DemoApplication is in com.example.demo, then com.example.demo.controller, com.example.demo.service, and all other subpackages are scanned automatically.

Move classes outside the base package, and you need explicit @ComponentScan annotations. That is extra configuration to maintain, extra configuration that new developers have to understand, and another way for things to break when someone moves a file without reading the documentation.

Long-Term Maintainability#

A codebase is not just for the developers writing it today. It is for whoever maintains it in three years, when the original authors may be gone. A conventional structure means the answers to common questions—"Where are the service classes? Where is the database layer?"—are always the same, and always correct.

It also means that tutorials, Stack Overflow answers, and framework documentation apply directly. When your project follows the standard layout, the answers you find online do not need translation.

Conclusion#

Spring Boot's default package structure is not just a starting point—it encodes a set of conventions that the entire Spring community understands. Following it costs nothing. Deviating from it creates friction that compounds over the lifetime of the project.

Resist the urge to reinvent the structure. The conventions exist for good reasons, and the benefit of consistency grows with every developer who works in the codebase.