Single-Module Projects
A single-module project consists of a single directory, with a single POM file and a single source directory. During the build, Maven packages the module either into a self-contained executable JAR file, or into a WAR file.
Single-module projects are simple and easy to understand. They’re best suited for smaller applications, and ones that are maintained by small development teams. They’re also suitable for new projects, since it’s easy to migrate from a single-module project to a multi-module one — if the need arises.
Note
| Your architecture imposes constraints on how parts of your code can interact. In a single-module project, all of the code is in the same place. There are no technical safeguards to prevent breaking these constraints — intentionally or otherwise. The risk of this happening grows with the codebase or as the number of people involved increases. This can lead to the code becoming unmaintainable: known as a "Big Ball of Mud". When it seems this may be approaching, consider adding ArchUnit tests to your project, or converting to a multi-module project. |
You can use various starters to create new single-module Vaadin projects (see Getting Started). Or you can create a new project without starters, by using a basic Maven POM file and adding the necessary sections to it.
POM Structure
For all Spring Boot projects, much of the configuration is imported through the spring-boot-starter-parent
:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version> 1
<relativePath/>
</parent>
-
You can check for the latest version on the MVN Repository.
The parent imports all of the Spring Boot dependencies and configures the most common Maven plugins (e.g., the compiler) using sensible defaults. The parent configuration is the only part of the project configuration where you need to refer explicitly to the dependency version.
Declare all other dependency versions as project properties, like this:
<properties>
<java.version>21</java.version> 1
<vaadin.version>24.5.4</vaadin.version> 2
</properties>
-
This property is used by
spring-boot-starter-parent
to configure the Java compiler plugin. -
You can check for the latest version on the MVN Repository.
Declaring the dependency versions as properties makes it easier to upgrade. It also helps to get an overview of which dependencies your application has — other than the Spring Boot dependencies.
Like Spring Boot, Vaadin comes with its own set of dependencies. These are imported through a Bill of Materials (BOM) like this:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Importing the BOM makes the dependencies known to your project. When you use them, you don’t have to declare their versions. To use the dependencies, add them to your project like so:
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
The only dependency you need for building a simple Vaadin application is vaadin-spring-boot-starter
. The spring-boot-devtools
dependency enables Spring Boot Developer Tools for easier development. In a real application, you would also add dependencies for at least persistence and database access — and for running tests.
Next, to build your project, you’ll need to add two more Maven plugins:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>
<goal>prepare-frontend</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
The spring-boot-maven-plugin
does plenty, but for now think of it as the plugin that packages a project into a single, executable JAR file. For more information about this plugin, see the Spring Boot documentation.
The vaadin-maven-plugin
also does plenty. The prepare-frontend
goal checks that you have sufficiently recent versions of the node
and npm
tools. It installs them if they’re missing. It also reviews all of the resources used by the application, generates some missing source files, and moves them into the correct locations.
Depending on how much the plugin has to do, the first execution of this goal may take some time. However, later executions are often fast. Therefore, include this goal in every build. For more information, see Production Build and Maven Configuration Properties.
After you’ve executed prepare-frontend
, you’re ready to run your application in development mode. To make a production build, you also have to run the build-frontend
goal. This goal takes much longer to complete, which is why it’s often configured inside its own production
Maven profile:
<profiles>
<profile>
<id>production</id>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-core</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-dev</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>
<goal>build-frontend</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
With this, the build-frontend
goal runs only when the production
profile is active. This profile also excludes the vaadin-dev
dependency. It’s only needed in development.
A fully configured POM file for a single-module Vaadin application looks like this:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.application</groupId>
<artifactId>application</artifactId>
<name>application</name>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/>
</parent>
<properties>
<java.version>21</java.version>
<vaadin.version>24.5.4</vaadin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-bom</artifactId>
<version>${vaadin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>
<goal>prepare-frontend</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>production</id>
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-core</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-dev</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.version}</version>
<executions>
<execution>
<goals>
<goal>build-frontend</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Code Structure
The code structure of a Vaadin application should resemble its architecture. This is illustrated here by a fictional Vaadin Flow application. This application has a Views component, a Services component, and an Entities component.
-
The Views component is the user interface of the application. It communicates with the Services component. Since the views are built with Flow, Spring is able to inject directly instances of the services into the views. No remote invocation, like a REST API, is needed.
-
The Services component contains the business logic of the application. It also enforces security and manages transactions using Spring. It may be returning entities directly to the Views component, or it may use dedicated Data Transfer Objects (DTO).
-
The Entities component contains the data of the application in the form of JPA entities. It also contains repositories or data access objects that allow the Services component to fetch data from a relational database and store data there.
As mentioned in System Components, this architecture corresponds to the following Java packages:
-
com.example.application.views
-
com.example.application.services
-
com.example.application.entities
Nevertheless, in many Vaadin applications, there’s code that doesn’t fit into any particular system component. This code is often cross cutting: it’s used by many system components. This could can be placed in utility packages, but keep these packages to a minimum. If you’re adding too many classes to utility packages, there may be a problem with the separation of concerns in the application. If that happens, you may have to introduce a new system component, split an existing component into smaller components, or merge multiple components into one.
Utility Packages
In most Vaadin applications, you should start with two utility packages: security
; and utils
.
-
The
security
package contains all of the code that relates to security. It contains the Security Configuration class and any custom security utilities you may need. -
The
utils
package is for miscellaneous classes. For example, if your project needed a customStringUtils
class, you would put it in this package.
With these packages, and an application theme, you’ll get a code structure that looks like this:
src/main/
├── frontend/
│ └── themes/
│ └── my-theme/
│ ├── styles.css
│ └── theme.json
├── java/
│ └── com/example/application/
│ ├── entities/
│ │ └── ...
│ ├── security/
│ │ └── SecurityConfiguration.java
│ ├── services/
│ │ └── ...
│ ├── utils/
│ │ └── ...
│ ├── views/
│ │ └── ...
│ └── Application.java
└── resources/
├── META-INF/
│ └── resources/
│ └── images/ (1)
│ └── ...
└── application.properties
Note the somewhat strange location of image files (1). You can find more information about this on the Images & Icons documentation page.