Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/styles/Vaadin/CrossReference.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ extends: existence
message: "Don't use absolute URLs for cross references within documentation."
link: https://vaadin.com/docs/contributing/docs/styleguide/#xrefs
nonword: true
level: error
level: error
scope: raw
ignorecase: true
# The only accepted case is 'link:/docs/...' which can be used to link across documentation versions
Expand Down
2 changes: 1 addition & 1 deletion articles/building-apps/ai/quickstart-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ section-nav: badge-flow
---


= Quick Start-Guide: Add an AI Chat Bot to a Vaadin + Spring Boot Application [badge-flow]#Flow#
= Quick Start-Guide: Add an AI Chat Bot to a Vaadin + Spring Boot Application [badge-flow]#Flow#

This guide shows how to connect a Large Language Model (LLM) into a Vaadin application using Spring AI and Spring Boot. You'll build a minimal chat UI with Vaadin provided components **MessageList** and **MessageInput**, stream responses token-by-token, and keep a conversational tone in the dialog with the AI.

Expand Down
2 changes: 1 addition & 1 deletion articles/building-apps/architecture/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ order: 100
section-nav: badge-deep-dive
---

= Architecture [badge-deep-dive]#Deep Dive#
= Architecture [badge-deep-dive]#Deep Dive#

.Deep Dive - Recommended Approach
[IMPORTANT]
Expand Down
10 changes: 5 additions & 5 deletions articles/building-apps/architecture/packages.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

This paradigm groups classes with similar responsibilities together. This leads to a *clear separation of concerns*. A class with too many responsibilities doesn't fit into any of the packages. This gives a natural inclination to split the class into smaller parts.

One drawback of this approach is that most classes need to be public. This means classes across layers can directly access each other, potentially violating architectural boundaries. However, the architectural style often dictates a specific dependency flow, such as `ui` -> `service` -> `domain`. You'd have to use something like https://www.archunit.org/[ArchUnit] to ensure the dependencies between classes are according to your architectural style, or put each layer into a <<project-structure/multi-module#,separate JAR file>>. Public visibility also makes it more difficult to separate <<api-spi#,APIs and SPIs>> from internal classes.
One drawback of this approach is that most classes need to be public. This means classes across layers can directly access each other, potentially violating architectural boundaries. However, the architectural style often dictates a specific dependency flow, such as `ui` -> `service` -> `domain`. You'd have to use something like https://www.archunit.org/[ArchUnit] to ensure the dependencies between classes are according to your architectural style, or put each layer into a <<project-structure/multi-module#,separate JAR file>>. Public visibility also makes it more difficult to separate <<api-spi#,APIs and SPIs>> from internal classes.

Check failure on line 39 in articles/building-apps/architecture/packages.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'SPIs'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'SPIs'?", "location": {"path": "articles/building-apps/architecture/packages.adoc", "range": {"start": {"line": 39, "column": 613}}}, "severity": "ERROR"}

Check failure on line 39 in articles/building-apps/architecture/packages.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'APIs'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'APIs'?", "location": {"path": "articles/building-apps/architecture/packages.adoc", "range": {"start": {"line": 39, "column": 604}}}, "severity": "ERROR"}

Another drawback is that feature cohesion suffers. In the example above, all Customer-related code is spread across multiple packages, making it harder to understand the complete feature. This also has an impact on testing: you can't easily mock or isolate a complete feature. You often end up testing dependencies across multiple layers rather than testing a cohesive feature in isolation.

Expand Down Expand Up @@ -66,7 +66,7 @@
├── com.example.application.util
│ └── + StringUtils
└── com.example.application
└── + Application
└── + Application
----

Compared to package by layer, this leads to higher feature cohesion and modularity. The classes that implement the same feature or functionality are grouped together. If you need to make a change to a feature, you only need to touch one package. Your tests can focus on one feature and test it in isolation. And if you need to split your application into modules or microservices, you can do that.
Expand Down Expand Up @@ -98,7 +98,7 @@
├── Application
├── CustomerDTO
├── CustomerRestClient
└── CustomerView
└── CustomerView
----

=== Layers Inside Features
Expand All @@ -117,7 +117,7 @@
│ ├── - CustomerForm
│ └── - CustomerView
└── com.example.application
└── + Application
└── + Application
----

Now, the UI-related classes is in a separate `ui` package. The classes can have package visibility since they are only called by the web browser, not by other feature packages. They call the API of the root feature package, which has public visibility.
Expand All @@ -136,7 +136,7 @@

== Final Thoughts

Package structure plays a big role in the readability and maintainability of your application. You know your package structure is right when you find classes where you expected them to be, and have no problems deciding where to put new classes.
Package structure plays a big role in the readability and maintainability of your application. You know your package structure is right when you find classes where you expected them to be, and have no problems deciding where to put new classes.

When you package by layer, classes that belong to the same architectural layer (like "UI", "Service", "Domain") end up in the same package. When you package by feature, classes that belong to the same feature (like "Customer Onboarding", "Dashboard", "Order Processing") end up in the same package.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Whenever you build a web application with Spring Boot, you have to decide whethe

=== JAR File Packaging

Packaging a Vaadin application as a JAR file is the recommended choice. It contains everything needed to run an application, except for the Java Virtual Machine. The web server -- like https://tomcat.apache.org/[Tomcat], https://jetty.org/index.html[Jetty], or https://undertow.io/[Undertow] -- is embedded. Therefore, you can start an application by running this command:
Packaging a Vaadin application as a JAR file is the recommended choice. It contains everything needed to run an application, except for the Java Virtual Machine. The web server -- like https://tomcat.apache.org/[Tomcat], https://jetty.org/index.html[Jetty], or https://undertow.io/[Undertow] -- is embedded. Therefore, you can start an application by running this command:

[source,terminal]
----
Expand All @@ -37,7 +37,7 @@ This makes the deployment simple and with a few options. To deploy an applicatio

// TODO Mention GraalVM

Another advantage to packaging an application as a JAR file is that it'll run inside its own Java Virtual Machine, and therefore its own operating system process. This allow you to configure and restart it without affecting other applications. Plus, if the application is compromised or crashes, other applications running on the same physical server are better protected since they run in their own processes. This feature, though, comes with a cost.
Another advantage to packaging an application as a JAR file is that it'll run inside its own Java Virtual Machine, and therefore its own operating system process. This allow you to configure and restart it without affecting other applications. Plus, if the application is compromised or crashes, other applications running on the same physical server are better protected since they run in their own processes. This feature, though, comes with a cost.

Since every application runs an embedded web server, they consume more memory and disk space than a traditional Java web application. However, since memory and disk space are quite cheap, the cost is usually well worth the benefits.

Expand All @@ -53,6 +53,6 @@ You should only consider WAR packaging if you already have a web application ser

== Multi vs. Single-Module

You can build your Vaadin applications as single-module Maven projects, or as multi-module Maven projects. Both options have advantages and disadvantages. You should familiarize yourself with them before making a decision.
You can build your Vaadin applications as single-module Maven projects, or as multi-module Maven projects. Both options have advantages and disadvantages. You should familiarize yourself with them before making a decision.

You can find more information on the <<single-module#,Single-Module Projects>>, and the <<multi-module#,Multi-Module Projects>> documentation pages.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ include::{articles}/_vaadin-version.adoc[]

= Multi-Module Projects

A multi-module project consists of multiple directories, each with its own POM file and source directory. The modules can depend on each other; Maven builds them in the correct order.
A multi-module project consists of multiple directories, each with its own POM file and source directory. The modules can depend on each other; Maven builds them in the correct order.

[IMPORTANT]
Even though the project consists of many modules, it is still a single application and is packaged into a single JAR file or WAR file.
Expand Down Expand Up @@ -92,7 +92,7 @@ To be able to use basic Spring features such as dependency injection, all module
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependency>
</dependencies>
----

Expand Down Expand Up @@ -148,7 +148,7 @@ Below is how a fully configured POM file for an empty multi-module Vaadin applic
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependency>
</dependencies>

<modules>
Expand Down Expand Up @@ -364,11 +364,11 @@ You'll need to add the Vaadin Maven plugin somewhere in your multi-module projec
</plugins>
</build>
</profile>
</profiles>
</profiles>
</project>
----

Store frontend sources, such as CSS files and JavaScript, in the `src/main/frontend` directory of the UI module. Store other resources, such as images, in `src/main/resources/META-INF/resources`.
Store frontend sources, such as CSS files and JavaScript, in the `src/main/frontend` directory of the UI module. Store other resources, such as images, in `src/main/resources/META-INF/resources`.


=== Multiple UI Modules
Expand Down Expand Up @@ -420,7 +420,7 @@ The application module is an ordinary Maven module that contains at least the `A
<groupId>${project.groupId}</groupId>
<artifactId>util</artifactId>
<version>${project.version}</version>
</dependency>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -472,7 +472,7 @@ If you're using package by feature, the POM-file would also include the Vaadin M
<groupId>${project.groupId}</groupId>
<artifactId>shopping-cart</artifactId>
<version>${project.version}</version>
</dependency>
</dependency>
</dependencies>

<build>
Expand All @@ -492,7 +492,7 @@ If you're using package by feature, the POM-file would also include the Vaadin M
</goals>
</execution>
</executions>
</plugin>
</plugin>
</plugins>
</build>

Expand Down Expand Up @@ -529,7 +529,7 @@ If you're using package by feature, the POM-file would also include the Vaadin M
</plugins>
</build>
</profile>
</profiles>
</profiles>
</project>
----

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ Next, to build your project, you'll need to add two more Maven plugins:

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 https://docs.spring.io/spring-boot/maven-plugin[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.
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 <<{articles}/flow/production/production-build#,Production Build>> and <<{articles}/flow/configuration/maven#,Maven Configuration Properties>>.

After you've executed `prepare-frontend`, you're ready to run your application in <<{articles}/flow/configuration/development-mode#,development mode>>.
After you've executed `prepare-frontend`, you're ready to run your application in <<{articles}/flow/configuration/development-mode#,development mode>>.


== Production Build Profile
Expand Down
14 changes: 7 additions & 7 deletions articles/building-apps/business-logic/add-service.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Add a Service
page-title: How to add an application service to a Vaadin application
page-title: How to add an application service to a Vaadin application
description: Learn how to add an application service to a Vaadin application.
meta-description: Learn how to design and implement application services in Vaadin. This guide covers best practices, security, naming conventions, and calling services from Vaadin views.
order: 5
Expand All @@ -14,13 +14,13 @@ In a Vaadin application, the _application layer_ contains the business, the data

image::images/application-layer-api.png[A diagram of the UI layer calling the application layer through an API]

This API is implemented by _application services_. In practice, application services are *Spring beans* that you can call from Vaadin views.
This API is implemented by _application services_. In practice, application services are *Spring beans* that you can call from Vaadin views.


== Design Guidelines

You can design application services according to your preferred architectural style, but following these best practices helps prevent common issues:

* The application services should have *high cohesion*. This means that all the methods in your service should relate to the same thing.
* The application services should be *stateless*.
* Application services should *initiate and complete <<../forms-data/consistency/transactions#,database transactions>>* before returning results.
Expand All @@ -43,8 +43,8 @@ public class OrderCreationService {
private final OrderRepository orderRepository;
private final ApplicationEventPublisher eventPublisher;

OrderCreationService(Validator validator,
OrderRepository orderRepository,
OrderCreationService(Validator validator,
OrderRepository orderRepository,
ApplicationEventPublisher eventPublisher) {
this.validator = validator;
this.orderRepository = orderRepository;
Expand All @@ -58,7 +58,7 @@ public class OrderCreationService {
throw new ConstraintViolationException(validationErrors);
}
var order = orderRepository.saveAndFlush(createOrderFromForm(orderForm));
eventPublisher.publishEvent(new OrderCreatedEvent(order)); // Notify other
eventPublisher.publishEvent(new OrderCreatedEvent(order)); // Notify other
// components of
// the new order
return order.orderId();
Expand Down Expand Up @@ -247,7 +247,7 @@ public class CustomerOnboardingView extends Main {
// end::snippet[]
add(createCustomerBtn);
}

private void createCustomer() {
// tag::snippet[]
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ order: 35
---


= Background Jobs [badge-deep-dive]#Deep Dive#
= Background Jobs [badge-deep-dive]#Deep Dive#

Many business applications need to perform in background threads. These tasks might be long-running operations triggered by the user, or scheduled jobs that run at specific times or intervals.

Expand Down Expand Up @@ -42,7 +42,7 @@ To use virtual threads, you can enable them by setting the `spring.threads.virtu

You can interact with the `TaskExecutor` either directly, or declaratively through annotations.

When interacting with it directly, you would inject an instance of `TaskExecutor` into your code, and submit work to it.
When interacting with it directly, you would inject an instance of `TaskExecutor` into your code, and submit work to it.

Here is an example of a class that uses the `TaskExecutor`:

Expand Down Expand Up @@ -70,7 +70,7 @@ public class MyWorker {
[IMPORTANT]
When you inject the `TaskExecutor`, you have to name the parameter `taskExecutor`. The application context may contain more than one bean that implements the `TaskExecutor` interface. If the parameter name doesn't match the name of the bean, Spring doesn't know which instance to inject.

If you want to use annotations, you have to enable them first. Do this by adding the `@EnableAsync` annotation to your main application class, or any other `@Configuration` class.
If you want to use annotations, you have to enable them first. Do this by adding the `@EnableAsync` annotation to your main application class, or any other `@Configuration` class.

Here's an example that adds the annotation to the main application class:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: Callbacks
page-title: How to use callbacks to interact with your UI | Vaadin
page-title: How to use callbacks to interact with your UI | Vaadin
description: How to use callbacks to interact with the user interface.
meta-description: When using a Flow user interface, the simplest way of allowing background jobs to interact with it is through callbacks. Learn more here.
order: 10
section-nav: badge-flow
---


= Callbacks [badge-flow]#Flow#
= Callbacks [badge-flow]#Flow#

When using a Flow user interface, the simplest way of allowing background jobs to interact with it is through callbacks. You can use `Consumer`, `Runnable`, and `Supplier` as callback interfaces, depending on how you want to interact with the background job.

Expand Down Expand Up @@ -41,7 +41,7 @@ A background job that returns a string or an exception could be implemented like
[source,java]
----
@Async
public void startBackgroundJob(Consumer<String> onComplete,
public void startBackgroundJob(Consumer<String> onComplete,
Consumer<Exception> onError) {
try {
var result = doSomethingThatTakesALongTime();
Expand All @@ -60,8 +60,8 @@ When the background job is also reporting its progress, for instance as a percen
[source,java]
----
@Async
public void startBackgroundJob(Consumer<String> onComplete,
Consumer<Double> onProgress,
public void startBackgroundJob(Consumer<String> onComplete,
Consumer<Double> onProgress,
Consumer<Exception> onError) {
try {
onProgress.apply(0.0);
Expand Down Expand Up @@ -93,8 +93,8 @@ A job can be cancelled. To do that, it would look like this:
[source,java]
----
@Async
public void startBackgroundJob(Consumer<String> onComplete,
Consumer<Double> onProgress,
public void startBackgroundJob(Consumer<String> onComplete,
Consumer<Double> onProgress,
Consumer<Exception> onError,
Supplier<Boolean> isCancelled) {
try {
Expand Down Expand Up @@ -150,7 +150,7 @@ Next, implement the service method like this:

[source,java]
----
public CancellableJob startBackgroundJob(Consumer<String> onComplete,
public CancellableJob startBackgroundJob(Consumer<String> onComplete,
Consumer<Double> onProgress
Consumer<Exception> onError) {
var cancelled = new AtomicBoolean(false);
Expand Down
Loading
Loading