Meet Your Walking Skeleton
Open your newly generated walking skeleton in your Java IDE. The walking skeleton is a single-module Maven project. For Maven newcomers, see the Maven in 5 Minutes tutorial before proceeding.
The most important files and directories in your skeleton are the following:
my-application/
├── src/
│ ├── main/
│ │ ├── frontend/ (1)
│ │ │ └── ...
│ │ ├── java/ (2)
│ │ │ └── ...
│ │ └── resources/ (3)
│ │ │ └── ...
│ └── test/
│ └── java/ (4)
│ └── ...
└── pom.xml (5)
-
Frontend files, such as CSS and TypeScript files.
-
Java source files.
-
Configuration files and other files needed by the application.
-
Java test files.
-
Maven POM-file for your application.
To make building easier, the skeleton includes the Maven Wrapper. Because of this, you don’t have to install Maven on your computer. Instead, you can use the mvnw
(macOS and Linux) or mvnw.cmd
(Windows) scripts to run Maven on the command line.
The skeleton includes a .gitignore
file optimized for Vaadin projects, and configuration files for the Spotless code formatter. The code formatter is needed when you generate code with Vaadin Copilot, since it does not format the code itself.
The LICENSE.md
file is a placeholder for your application’s license. Replace with your own license, or delete if you don’t need it.
Next, you’ll have a closer look at the Java files, the frontend files, and the POM-file.
Java Files
A walking skeleton always contains the following Java files, regardless of whether you chose to generate a Flow view or a Hilla view:
src
├── main/java
│ ├── [application package]
│ │ ├── base (1)
│ │ │ └── domain
│ │ │ └── AbstractEntity.java
│ │ ├── security (2)
│ │ │ └── ...
│ │ └── taskmanagement (3)
│ │ ├── domain
│ │ │ ├── Task.java
│ │ │ └── TaskRepository.java
│ │ └── service
│ │ └── TaskService.java
│ └── Application.java (4)
│
└── test/java
└── [application package]
├── taskmanagement
│ └── service
│ └── TaskServiceIT.java (5)
├── ArchitectureTest.java (6)
├── TestApplication.java (7)
└── TestcontainersConfiguration.java (8)
-
The
base
feature package. -
A complete security configuration.
-
The
taskmanagement
feature package. -
Main entry point into the application.
-
Example integration test for the
TaskService
. -
ArchUnit architecture test for the entire application.
-
Test application that starts the application with Testcontainers enabled.
-
Configuration class for Testcontainers service connections.
If you generated a Flow view, the project contains more Java files. You’ll learn about those later.
The main entry point into the application is Application.java
. This class contains the main()
method that start up the Spring Boot application.
The skeleton follows a feature-based package structure, organizing code by functional units rather than traditional architectural layers. It includes two feature packages: base
and taskmanagement
.
-
The
base
package contains classes meant for reuse across different features, either through composition or inheritance. -
The
taskmanagement
package is an example feature package that demonstrates the structure. It represents a self-contained unit of functionality, including UI components, business logic, and data access. Once you create your own features, you’ll remove this package.
This feature-driven approach keeps related code together, making it easier to maintain, extend, and understand. A feature package could represent anything from a specific use case (e.g., "User Registration"), a UI view (e.g., "Dashboard"), or even a business subdomain (e.g., "Billing"). By grouping everything needed for a feature into a single package structure, you avoid scattering logic across layers and reduce unnecessary coupling.
You’ll find package-info.java
files in every package. These files add the @NullMarked
annotation from JSpecify to each package. This instructs static analysis tools that every return value and method parameter can never be null
unless explicitly stated with a @Nullable
annotation. This is a good practice that reduces bugs caused by NullPointerException
.
ArchitectureTest.java
is an ArchUnit test that guards against unintentional dependencies between classes. As your application grows, it helps keep your code base in shape.
Testcontainers for Development and Testing
Starting with Vaadin 24.8, the skeleton includes support for Testcontainers — an open-source library that provides lightweight, disposable containers for databases and other services via Docker.
The skeleton includes a TestcontainersConfiguration.java
class that defines container-based service connections. By default, it configures a PostgreSQL container, ready for both testing and development use. For guidance on customizing this file, refer to the Spring Boot Testcontainers documentation.
Integration tests automatically use Testcontainers. You can also run the application itself with Testcontainers using the TestApplication.java
class, which includes a main()
method to start the Spring Boot application and initialize the configured containers. Run this class from your IDE or via Maven:
./mvnw spring-boot:test-run
Built-in Security Configuration
The skeleton also includes a production-grade Spring Security setup, featuring:
-
A custom API for accessing user details, independent of the identity provider.
-
A user ID domain primitive for type-safe identity modeling. See the Domain Primitives deep dive for details.
-
Method-level security for application services.
-
A development-mode configuration with in-memory users and a simple login screen.
-
A production-mode configuration compatible with Control Center’s Identity Management feature.
All components in the security
package include detailed Javadoc comments. You can use them as-is or adapt them to fit your application’s requirements.
If you prefer to write your own security setup from scratch, simply delete the entire security
package.
Database Configuration: H2 vs PostgreSQL
By default, the skeleton supports both H2 and PostgreSQL:
-
In development mode, it uses H2 for convenience — no setup required.
-
In production mode, it uses PostgreSQL.
This setup lets you get started quickly without Docker or PostgreSQL installed. However, for real-world applications, you should use the same database in all environments to avoid surprises. Testcontainers is an ideal solution, and the skeleton includes first-class support for it.
To switch fully to PostgreSQL:
-
Open
pom.xml
. -
Remove the
h2-local-development
profile. -
Set the
defaultGoal
tospring-boot:test-run
.
This ensures PostgreSQL is used consistently in both development and production.
Caution
|
The skeleton uses Hibernate’s update DDL-auto mode to manage the database schema. While convenient for prototyping, it’s not suitable for production use. Instead, use a proper migration tool like Flyway. See the Add Flyway guide for setup instructions.
|
The Task Management Feature
The taskmanagement
feature consists of a JPA entity, a Spring Data JPA repository interface, and an application service.
The repository stores and fetches entities from a relational database.
The application service acts as the API of the feature and is the boundary between the presentation layer and the application layer. Its main purpose in the skeleton is to show how an application service interacts with the domain model in a Vaadin application.
The task service has a sample integration test. It starts up the application and a PostgreSQL test container, and checks that the service works as expected. Its main purpose in the skeleton is to show how to write integration tests for application services.
Java Views Flow
If you generated a Flow view, you’ll find some extra Java files in the skeleton:
src
└── main/java
└── [application package]
├── base
│ └── ui
│ ├── component
│ │ └── ViewToolbar.java
│ └── view
│ ├── MainErrorHandler.java
│ ├── MainLayout.java
│ └── MainView.java
└── taskmanagement
└── ui
└── view
└── TaskListView.java
The base
feature package contains one user interface package with two sub-packages: component
and view
.
The component
package contains custom UI components that can be reused throughout the entire application. The skeleton only contains one, but as your application grows, you’ll add more components to this package.
The view
package contains view-related classes that cut across multiple views in multiple features. The skeleton contains an error handler, a main layout, and a simple main view. You’ll want to replace the main view with your own as the application grows.
The error handler receives all exceptions that reach the user interface, logs them, and shows an error notification to the user. You’ll want to customize this as the application grows.
Your application shows all the views inside the main layout by default. It contains the application’s name, a navigation menu, and a user menu that allows the user to log out. You’ll want to at least change the application name.
The taskmanagement
feature package contains one UI-related package. It contains the view that allows users to create and list tasks to do.
Frontend Files
A walking skeleton always contains the following frontend files, regardless of whether you chose to generate a Flow view or a Hilla view:
src
└── main/frontend
└── themes
└── default
├── styles.css
└── theme.json
This is an empty theme called default
, based on the Lumo theme. It is activated in the Application
class, using the @Theme
annotation.
If you’ve started up your application, you’ll see some auto-generated files in the frontend
directory as well. You’ll find an index.html
file, and a generated
directory. You don’t have to touch these for now.
React Views Hilla
If you generated a Hilla view, you’ll find more frontend files in the skeleton:
src
└── main/frontend
├── components
│ └── ViewToolbar.tsx
├── security
│ └── auth.ts
├── views
│ ├── @index.tsx
│ ├── @layout.tsx
│ ├── _ErrorHandler.ts
│ └── task-list.tsx
└── index.tsx
The components
directory contains custom UI components that can be reused throughout the entire application. The skeleton only contains one, but as your application grows, you’ll add more components to this directory.
The security
directory contains an auth.ts
file that sets up a React context to store authentication details and connects it to the server-side security configuration.
The views
directory contains a main view, a main layout, an error handler, and an example view. The file names in this directory all have special meaning. You’ll learn about it later.
The example view - task-list.tsx
- allows users to add and list tasks to do.
Your application shows all the views inside the main layout - @layout.tsx
- by default. It contains the application’s name, a navigation menu, and a user menu that allows the user to logout. You’ll want to at least change the application name.
The error handler is a TypeScript function that logs the error to the console and shows a notification to the user. The error handler is not a React error boundary. It is designed to handle errors that occur when calling application services. Because of this, you have to manually catch the errors you want to handle, and call the error handler. The example view shows you how to do this.
The index.tsx
file enables the authentication context, and creates and renders the root application component.
The POM File
The POM file is a typical Spring Boot, single-module Maven project file. It uses the spring-boot-starter-parent
, so all the Spring Boot dependencies are available for use. It also brings in the Vaadin dependencies, Testcontainers, PostgreSQL, and ArchUnit.
The spring-boot-maven-plugin
is used to package the application into a single, executable JAR file.
The spotless-maven-plugin
is used to format the Java and TypeScript source files.
The vaadin-maven-plugin
is used to prepare and build the frontend files. Under the hood it is using npm and Vite.
The POM file defines three build profiles:
-
The
h2-local-development
profile is automatically activated whenever no other profile has been activated. It configures the application to use the H2 database in local development mode, as discussed earlier in this guide. -
The
production
profile triggers a production build, and is deactivated by default. You’ll learn more about making a production build on the Build a Project page. -
The
integration-test
profile runs integration tests during theverify
phase, and is deactivated by default.