Skip to content
Spring Boot fundamentals 2 min read

Project Structure

A consistent project layout makes Spring Boot applications easy to navigate as they grow. While Spring imposes few rules, the community has converged on a layered structure that the framework’s defaults reward.

Typical layout

A standard Maven project separates source, resources, and tests.

demo/
├── pom.xml
├── mvnw
└── src
    ├── main
    │   ├── java
    │   │   └── com/devcraftly/demo
    │   │       ├── DemoApplication.java
    │   │       ├── controller/   # HTTP endpoints
    │   │       ├── service/      # business logic
    │   │       ├── repository/   # data access
    │   │       └── model/        # entities & DTOs
    │   └── resources
    │       ├── application.yml
    │       ├── static/           # served assets
    │       └── templates/        # server-rendered views
    └── test
        └── java/...

Each layer has one job. Controllers translate HTTP to method calls, services hold business rules and transactions, repositories talk to the database, and models carry data. This separation keeps each class testable in isolation.

LayerResponsibilityTypical annotation
ControllerHTTP request/response@RestController
ServiceBusiness logic, transactions@Service
RepositoryPersistence@Repository
ModelData shape@Entity / plain DTO

The main application class

The class annotated with @SpringBootApplication is the entry point and the root of component scanning.

package com.devcraftly.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

Warning: Keep this class in the top-level package (e.g. com.devcraftly.demo). Component scanning starts at its package and descends. A class placed in a sibling or parent package will be invisible to the container.

Component scanning

@SpringBootApplication includes @ComponentScan, which discovers @Component, @Service, @Repository, and @Controller classes beneath the main package and registers them as beans. You rarely configure it explicitly, but you can widen or narrow the scan:

@SpringBootApplication
@ComponentScan(basePackages = {"com.devcraftly.demo", "com.devcraftly.shared"})
public class DemoApplication { }

Configuration files

Spring Boot reads externalized configuration from application.properties or application.yml in src/main/resources. YAML is preferred for nested structures.

server:
  port: 8080
spring:
  application:
    name: demo
  datasource:
    url: jdbc:h2:mem:devdb

The same settings in properties form:

server.port=8080
spring.application.name=demo
spring.datasource.url=jdbc:h2:mem:devdb

Tip: Use application-<profile>.yml files (for example application-prod.yml) to layer environment-specific settings on top of the base file. Profiles are covered in the Configuration page.

Best Practices

  • Keep the @SpringBootApplication class in the root package so component scanning works without extra configuration.
  • Organize by layer (or by feature for larger apps), and depend downward only: controller → service → repository.
  • Never let controllers touch repositories directly, route data access through a service so business rules and transactions live in one place.
  • Prefer application.yml over .properties for readable, hierarchical configuration.
  • Keep configuration out of code; favor properties and @ConfigurationProperties over hard-coded values.
  • Mirror the main package structure under src/test/java so tests sit beside the code they exercise.
Last updated June 1, 2026
Was this helpful?