Skip to main content

Chapter 2: Structure of a Spring Boot Project

Introduction​

A Spring Boot project follows a standardized structure based on Maven conventions. This organization facilitates code navigation and allows developers to quickly find the necessary files.

Chapter Objectives​

By the end of this chapter, you will be able to:

  • Understand the directory organization of a Spring Boot project
  • Identify the role of each important file
  • Organize your code according to best practices
  • Navigate efficiently in a Spring Boot project

Overview of the Structure​

Complete Tree​

greeting-app/
├── .mvn/ # Maven Wrapper
│ └── wrapper/
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── src/
│ ├── main/
│ │ ├── java/ # Java source code
│ │ │ └── com/example/greeting/
│ │ │ └── GreetingApplication.java
│ │ └── resources/ # Application resources
│ │ ├── static/ # Static files (CSS, JS, images)
│ │ ├── templates/ # Templates (Thymeleaf, Freemarker)
│ │ ├── application.properties # Main configuration
│ │ └── application.yml # YAML configuration (alternative)
│ └── test/
│ ├── java/ # Unit and integration tests
│ │ └── com/example/greeting/
│ │ └── GreetingApplicationTests.java
│ └── resources/ # Test resources
│ └── application-test.properties
├── target/ # Compiled files (generated)
├── .gitignore # Files to ignore by Git
├── mvnw # Maven wrapper (Unix/macOS)
├── mvnw.cmd # Maven wrapper (Windows)
├── pom.xml # Maven configuration
├── HELP.md # Generated documentation
└── README.md # Project documentation

Root Directory​

1. pom.xml File​

Role: Maven configuration file (Project Object Model)

Main Content:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!-- Parent Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>

<!-- Project Identifiers -->
<groupId>com.example</groupId>
<artifactId>greeting</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>greeting</name>
<description>Greeting application</description>

<!-- Properties -->
<properties>
<java.version>17</java.version>
</properties>

<!-- Dependencies -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<!-- Build Plugin -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Important Sections:

SectionDescription
<parent>Inherits Spring Boot configuration
<groupId>, <artifactId>, <version>Maven project coordinates
<properties>Global configuration (Java version, encoding, etc.)
<dependencies>List of project dependencies
<build>Build process configuration

2. Maven Wrapper (mvnw, mvnw.cmd)​

Role: Allows running Maven without installing it globally

Advantages:

  • Guaranteed Maven version for all developers
  • No need to install Maven manually
  • Scripts included in the project

Usage:

Linux/macOS:

./mvnw clean install
./mvnw spring-boot:run

Windows:

mvnw.cmd clean install
mvnw.cmd spring-boot:run

3. .gitignore File​

Role: List of files not to be versioned with Git

Typical Content:

# Compiled files
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

# IDE
.idea/
*.iml
.vscode/
.classpath
.project
.settings/

# System
.DS_Store
Thumbs.db

# Logs
*.log

# Temporary files
*.tmp
*.bak

src/main/java Directory​

Package Structure​

Convention: Organize code in functional layers

src/main/java/com/example/greeting/
├── GreetingApplication.java # Main class
├── controller/ # REST/MVC controllers
│ ├── GreetingController.java
│ └── UserController.java
├── service/ # Business logic
│ ├── GreetingService.java
│ └── UserService.java
├── repository/ # Data access
│ ├── UserRepository.java
│ └── ProductRepository.java
├── model/ # Entities and DTOs
│ ├── entity/
│ │ ├── User.java
│ │ └── Product.java
│ └── dto/
│ ├── UserDTO.java
│ └── ProductDTO.java
├── config/ # Spring configuration
│ ├── SecurityConfig.java
│ └── DatabaseConfig.java
├── exception/ # Error handling
│ ├── ResourceNotFoundException.java
│ └── GlobalExceptionHandler.java
└── util/ # Utility classes
└── DateUtil.java

Layered Architecture​

Responsibilities of each layer:

LayerRoleAnnotations
ControllerHandles HTTP requests@RestController, @Controller
ServiceBusiness logic@Service
RepositoryData access@Repository, JPA interface
ModelData representation@Entity, POJO

Main Class: GreetingApplication.java​

Content:

package com.example.greeting;

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

@SpringBootApplication
public class GreetingApplication {

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

@SpringBootApplication Annotation:

This annotation is a shortcut that combines three annotations:

@SpringBootApplication
=
@SpringBootConfiguration // Spring configuration
+ @EnableAutoConfiguration // Auto-configuration
+ @ComponentScan // Component scanning

Role of each annotation:


src/main/resources Directory​

Overview​

src/main/resources/
├── static/ # Static files
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── script.js
│ └── images/
│ └── logo.png
├── templates/ # Templates (Thymeleaf)
│ ├── index.html
│ └── user.html
├── application.properties # Main configuration
├── application-dev.properties # Development configuration
├── application-prod.properties # Production configuration
├── application.yml # YAML configuration (alternative)
├── banner.txt # Custom startup banner
└── data.sql # Initialization SQL script

1. static/ Directory​

Role: Contains static files served directly

File types:

  • CSS (stylesheets)
  • JavaScript (client-side scripts)
  • Images (PNG, JPG, SVG)
  • Fonts (custom fonts)

Access:

http://localhost:8080/css/style.css
http://localhost:8080/js/script.js
http://localhost:8080/images/logo.png

Recommended structure:

static/
├── css/
├── js/
├── images/
├── fonts/
└── favicon.ico

2. templates/ Directory​

Role: Contains view templates (if using a template engine)

Supported template engines:

  • Thymeleaf (recommended)
  • Freemarker
  • Mustache
  • Groovy Templates

Thymeleaf example:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Greeting</title>
</head>
<body>
<h1 th:text="${message}">Default message</h1>
</body>
</html>

3. application.properties File​

Role: Main application configuration

Format: key=value

Configuration examples:

# Server port
server.port=8080

# Application name
spring.application.name=greeting-app

# Database configuration
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=postgres
spring.datasource.password=secret

# JPA/Hibernate
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# Logs
logging.level.root=INFO
logging.level.com.example=DEBUG

# Date format
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=America/Montreal

4. application.yml File (Alternative)​

Role: Same as application.properties but in YAML format

Format: Hierarchical with indentation

Equivalent example:

server:
port: 8080

spring:
application:
name: greeting-app

datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: postgres
password: secret

jpa:
hibernate:
ddl-auto: update
show-sql: true

logging:
level:
root: INFO
com.example: DEBUG

Comparison:

FormatAdvantagesDisadvantages
propertiesSimple, familiarRepetitive for complex structures
ymlReadable, hierarchicalSensitive to indentation

5. Configuration Profiles​

Concept: Different configurations depending on the environment

Profile files:

application.properties           # Default configuration
application-dev.properties # Development
application-test.properties # Tests
application-prod.properties # Production

Activating a profile:

Via application.properties:

spring.profiles.active=dev

Via command line:

java -jar app.jar --spring.profiles.active=prod

Via environment variable:

export SPRING_PROFILES_ACTIVE=prod

Environment-specific configuration example:

application-dev.properties:

server.port=8080
spring.datasource.url=jdbc:h2:mem:testdb
logging.level.root=DEBUG

application-prod.properties:

server.port=80
spring.datasource.url=jdbc:postgresql://prod-server:5432/mydb
logging.level.root=WARN

src/test/java Directory​

Test Structure​

src/test/java/com/example/greeting/
├── GreetingApplicationTests.java # Startup test
├── controller/
│ └── GreetingControllerTest.java # Controller tests
├── service/
│ └── GreetingServiceTest.java # Service tests
└── repository/
└── UserRepositoryTest.java # Repository tests

Default Startup Test​

package com.example.greeting;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class GreetingApplicationTests {

@Test
void contextLoads() {
// Checks that the Spring context loads correctly
}
}

Role: Verify that the application starts without error


target/ Directory​

Role: Contains compiled files and the final JAR

Generated by: Maven during the build

Content:

target/
├── classes/ # Compiled classes (.class)
├── test-classes/ # Compiled tests
├── generated-sources/ # Generated sources
├── maven-status/ # Maven status
└── greeting-0.0.1-SNAPSHOT.jar # Final executable JAR

Important: This directory is ignored by Git (in .gitignore)

Cleaning:

mvn clean

Organization by Feature vs by Layer​

com.example.greeting/
├── controller/
│ ├── UserController.java
│ └── ProductController.java
├── service/
│ ├── UserService.java
│ └── ProductService.java
├── repository/
│ ├── UserRepository.java
│ └── ProductRepository.java
└── model/
├── User.java
└── Product.java

Advantages:

  • Simple and clear
  • Easy to understand for beginners
  • Standard Spring Boot

Disadvantages:

  • Difficult to scale for large projects
  • Coupling between features
com.example.greeting/
├── user/
│ ├── UserController.java
│ ├── UserService.java
│ ├── UserRepository.java
│ └── User.java
├── product/
│ ├── ProductController.java
│ ├── ProductService.java
│ ├── ProductRepository.java
│ └── Product.java
└── order/
├── OrderController.java
├── OrderService.java
├── OrderRepository.java
└── Order.java

Advantages:

  • High cohesion
  • Low coupling
  • Facilitates transition to microservices architecture

Disadvantages:

  • Less standard
  • Can be confusing at first

Naming Conventions​

Packages​

Convention: All lowercase, words separated by dots

com.example.greeting           // ✅ Good
com.example.greeting.service // ✅ Good
com.Example.Greeting // ❌ Bad (uppercase)
com.example.greeting_service // ❌ Bad (underscore)

Classes​

Convention: PascalCase (first letter of each word capitalized)

UserController        // ✅ Good
GreetingService // ✅ Good
userController // ❌ Bad (starts with lowercase)
user_controller // ❌ Bad (underscore)

Recommended suffixes:

TypeSuffixExample
ControllerControllerUserController
ServiceServiceUserService
RepositoryRepositoryUserRepository
EntityNoneUser, Product
DTODTOUserDTO, CreateUserDTO
ExceptionExceptionResourceNotFoundException
ConfigurationConfigSecurityConfig

Methods​

Convention: camelCase (first letter lowercase)

getUserById()         // ✅ Good
findAllUsers() // ✅ Good
GetUserById() // ❌ Bad (starts with uppercase)
get_user_by_id() // ❌ Bad (underscore)

Variables and Attributes​

Convention: camelCase

String userName;      // ✅ Good
int userId; // ✅ Good
String UserName; // ❌ Bad (starts with uppercase)
String user_name; // ❌ Bad (underscore)

Constants​

Convention: SCREAMING_SNAKE_CASE (all uppercase, underscores)

public static final int MAX_USERS = 100;              // ✅ Good
public static final String API_BASE_URL = "/api/v1"; // ✅ Good
public static final int maxUsers = 100; // ❌ Bad (camelCase)

Practical Exercises​

Exercise 1: Explore the Structure​

Objective: Familiarize yourself with the structure of a project

Instructions:

  1. Create a project with Spring Initializr (Spring Web only)
  2. Import it into VS Code
  3. Identify:
    • The pom.xml file
    • The main class
    • The application.properties file
    • The test directory
  4. Draw the tree structure on paper

Exercise 2: Create a Package Structure​

Objective: Organize code into layers

Instructions:

  1. In the previous project, create the structure:
    com.example.demo/
    ├── controller/
    ├── service/
    ├── repository/
    └── model/
  2. Create empty classes in each package
  3. Check the compilation

Exercise 3: Configure Profiles​

Objective: Create environment-specific configurations

Instructions:

  1. Create application-dev.properties:
    server.port=8080
    logging.level.root=DEBUG
  2. Create application-prod.properties:
    server.port=80
    logging.level.root=WARN
  3. Test activating each profile

Review Questions​

  1. Which file contains the Maven dependencies?

  2. What is the purpose of the target/ directory?

  3. What is the difference between static/ and templates/?

  4. What does the @SpringBootApplication annotation contain?

  5. How do you name a configuration file for the development environment?

  6. What is the naming convention for controller classes?

  7. Where do you place CSS and JavaScript files?

  8. What is the difference between application.properties and application.yml?


Chapter Summary​

Key Points​

  • Spring Boot structure follows Maven conventions
  • Source code is in src/main/java
  • Resources are in src/main/resources
  • Layered organization: Controller → Service → Repository
  • Tests are in src/test/java
  • Configuration files use profiles

Best Practices​

  • Follow Java naming conventions
  • Organize code into logical packages
  • Separate configurations by environment
  • Use descriptive suffixes for classes
  • Comment complex code
  • Maintain a clear and consistent structure

Next Steps​

In the next chapter, we will delve into:

Chapter 3: Configuration with application.properties

  • Available Spring Boot properties
  • Database configuration
  • Log configuration
  • Custom properties

Back to Module 2