You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
page-title: How to add an application service to a Vaadin application
4
4
description: Learn how to add an application service to a Vaadin application.
5
-
meta-description: Learn how to design and implement application services in Vaadin. This guide covers best practices, security, naming conventions, and calling services from Flow and Hilla views.
6
-
layout: tabbed-page
7
-
tab-title: Overview
5
+
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.
8
6
order: 5
9
7
---
10
8
11
9
12
10
= Add an Application Service
11
+
:toclevels: 2
13
12
14
-
In a Vaadin application, the _application layer_ contains the business, the data, and any integrations to external systems. The application layer exposes an <<../../architecture/api-spi#,API>> that the _UI layer_ (i.e., the views) can call:
13
+
In a Vaadin application, the _application layer_ contains the business, the data, and any integrations to external systems. The application layer exposes an <<../architecture/api-spi#,API>> that the _UI layer_ (i.e., the views) can call:
15
14
16
15
image::images/application-layer-api.png[A diagram of the UI layer calling the application layer through an API]
17
16
18
-
This API is implemented by _application services_. In practice, application services are *Spring beans* that you can call from Vaadin Flow and Hilla views.
17
+
This API is implemented by _application services_. In practice, application services are *Spring beans* that you can call from Vaadin views.
19
18
20
19
21
20
== Design Guidelines
@@ -24,8 +23,8 @@ You can design application services according to your preferred architectural st
24
23
25
24
* The application services should have *high cohesion*. This means that all the methods in your service should relate to the same thing.
26
25
* The application services should be *stateless*.
27
-
* Application services should *initiate and complete <<../../forms-data/consistency/transactions#,database transactions>>* before returning results.
28
-
* The application services should be *<<../../security/protect-services#,secure>>*.
26
+
* Application services should *initiate and complete <<../forms-data/consistency/transactions#,database transactions>>* before returning results.
27
+
* The application services should be *<<../security/protect-services#,secure>>*.
29
28
* Views should invoke application services, but application services *should not have dependencies on views*.
30
29
31
30
[NOTE]
@@ -88,7 +87,7 @@ Vaadin does not enforce a specific naming convention for application services. W
88
87
89
88
== Input & Output
90
89
91
-
Application services often need to communicate with <<../../forms-data/repositories#,repositories>> to fetch and store data. They also need to pass this data to the UI layer. For this, there are two options: pass the entities directly; or pass Data Transfer Objects (DTO:s). Both have advantages and disadvantages.
90
+
Application services often need to communicate with <<../forms-data/repositories#,repositories>> to fetch and store data. They also need to pass this data to the UI layer. For this, there are two options: pass the entities directly; or pass Data Transfer Objects (DTO:s). Both have advantages and disadvantages.
92
91
93
92
94
93
=== Entities
@@ -116,9 +115,6 @@ public class CustomerCrudService {
116
115
}
117
116
----
118
117
119
-
[CAUTION]
120
-
When most of your service methods delegate to a repository, it may be tempting to skip the service and have the UI layer communicate directly with the repository. However, this isn't a good idea because of the cross-cutting concerns that the application service has to handle. This is explained later on this page.
121
-
122
118
Using entities in your application service is a good idea when your user interface and entities match each other, closely. For example, you could have a form with fields that match the fields of the entity -- or a grid with columns that match them.
123
119
124
120
Your entities should be _anemic_, which means that they only contain data and little to no business logic.
@@ -138,7 +134,7 @@ In this case, the application services should accept DTO:s as input, and return
138
134
139
135
This adds another responsibility to the application service: mapping between entities and DTO:s.
140
136
141
-
When using <<../../forms-data/repositories#query-classes,query classes>>, you can do the mapping in them by returning their DTO:s, directly. The query DTO:s become part of the application layer API.
137
+
When using <<../forms-data/repositories#query-classes,query classes>>, you can do the mapping in them by returning their DTO:s, directly. The query DTO:s become part of the application layer API.
142
138
143
139
For storing data, services typically have to copy data from the DTO to the entity. For example, like this:
144
140
@@ -176,14 +172,14 @@ When using DTO:s, you have more code to maintain. Some changes, like adding a ne
176
172
177
173
=== Domain Payload Objects
178
174
179
-
When using <</building-apps/forms-data/consistency/domain-primitives#,domain primitives>>, you should use them in your DTO:s, as well. In this case, the DTO:s are called _Domain Payload Objects_ (DPO). They're used in the exact same way as DTO:s.
175
+
When using <<../forms-data/consistency/domain-primitives#,domain primitives>>, you should use them in your DTO:s, as well. In this case, the DTO:s are called _Domain Payload Objects_ (DPO). They're used in the exact same way as DTO:s.
180
176
181
177
182
178
=== Validation
183
179
184
180
All input should be validated by the application services before they do anything else with it. This is important for security, integrity, and consistency. Even if you use input validation in your user interface, you should still validate the data in the application services.
185
181
186
-
You can validate the input in different ways. For more information, see the <</building-apps/forms-data/consistency/validation#,Validation>> documentation page.
182
+
You can validate the input in different ways. For more information, see the <<../forms-data/consistency/validation#,Validation>> documentation page.
187
183
188
184
189
185
== Package Naming
@@ -194,14 +190,169 @@ For example, services related to "customer relationship management" would be pla
194
190
195
191
This structure keeps services well-organized, easy to find, and clearly associated with their purpose.
196
192
197
-
See the <<../../architecture/packages#,Package Structure>> documentation page for more information.
193
+
See the <<../architecture/packages#,Package Structure>> documentation page for more information.
194
+
195
+
196
+
== Injecting a Service into a View
197
+
198
+
Since application services are Spring beans, you can inject them directly into your Vaadin views through constructor injection.
199
+
200
+
In the following example, [classname]`CustomerOnboardingService` is injected into [classname]`CustomerOnboardingView`:
201
+
202
+
[source,java]
203
+
----
204
+
@Route
205
+
public class CustomerOnboardingView extends Main {
206
+
207
+
private final CustomerOnboardingService service; // <1>
208
+
209
+
// tag::snippet[]
210
+
public CustomerOnboardingView(CustomerOnboardingService service) { // <2>
211
+
// end::snippet[]
212
+
this.service = service;
213
+
// ...
214
+
}
215
+
...
216
+
}
217
+
----
218
+
<1> Store the service in a `final` variable for future reference.
219
+
<2> Inject the service as a constructor parameter.
220
+
221
+
Constructor injection is recommended because it ensures that dependencies are provided at object creation, making the class easier to test and avoiding potential issues with uninitialized fields. Additionally, since the service is stored in a final variable, it cannot be reassigned accidentally, ensuring safer code.
222
+
223
+
224
+
== Calling a Service
225
+
226
+
Since Vaadin views are regular Java objects, calling a service is as simple as invoking a method.
227
+
228
+
In the following example, the view calls [classname]`CustomerOnboardingService` when the user clicks a button:
229
+
230
+
[source,java]
231
+
----
232
+
@Route
233
+
public class CustomerOnboardingView extends Main {
234
+
235
+
private final CustomerOnboardingService service;
236
+
private final Binder<CustomerOnboardingForm> binder;
237
+
238
+
public CustomerOnboardingView(CustomerOnboardingService service) {
239
+
this.service = service;
240
+
this.binder = new Binder<>(CustomerOnboardingForm.class);
<1> Retrieves a `CustomerOnboardingForm` record from the binder.
265
+
<2> Calls the service to onboard the customer.
266
+
<3> Navigates to the newly created customer's view.
267
+
268
+
For more information about forms and data binding, see the <<../forms-data/add-form#,Add a Form>> guide.
198
269
199
270
200
-
== Calling from Views
271
+
== Calling a Service on View Creation
201
272
202
-
You can call an application service both from Flow and Hilla. When calling an application service from a Hilla view, it must be *browser-callable*, which introduces certain design constraints. These constraints do not apply when calling the service from a Flow view.
273
+
Sometimes, you may need to call a service immediately upon view creation—for example, to populate a combo box or grid with data. While it may be tempting to do this in the constructor, *this is not recommended*.
203
274
204
-
The following guides teach you how to call application services in Flow and Hilla:
275
+
Vaadin may instantiate a view without actually displaying it. Because of this, you should *keep constructors free of side effects*.
276
+
277
+
.What is a side effect?
278
+
[NOTE]
279
+
A _side effect_ is any operation that modifies state outside the object's scope or interacts with external systems like databases, files, or network services during object construction.
280
+
281
+
282
+
=== After Navigation
283
+
284
+
To call a service only after the user has navigated to a view, implement the [interfacename]`AfterNavigationObserver` interface and call the service in the [methodname]`afterNavigation()` method:
285
+
286
+
[source,java]
287
+
----
288
+
@Route
289
+
// tag::snippet[]
290
+
public class MyView extends Main implements AfterNavigationObserver {
291
+
// end::snippet[]
292
+
293
+
private final CountryService countryService;
294
+
private final ComboBox<Country> countries;
295
+
296
+
public MyView(CountryService countryService) {
297
+
this.countryService = countryService;
298
+
countries = new ComboBox<>();
299
+
add(countries);
300
+
}
301
+
302
+
// tag::snippet[]
303
+
@Override
304
+
public void afterNavigation(AfterNavigationEvent afterNavigationEvent) {
var subscription = subscriptionService.myStream().subscribe(message -> { // <1>
342
+
// Do something with the message
343
+
});
344
+
addDetachListener(detachEvent -> {
345
+
detachEvent.unregisterListener(); // <2>
346
+
subscription.dispose(); // <3>
347
+
});
348
+
}
349
+
// end::snippet[]
350
+
}
351
+
----
352
+
<1> Calls the service to subscribe to the stream when attached.
353
+
<2> Removes the detach listener to prevent duplicate listeners.
354
+
<3> Cancels the subscription to avoid memory leaks.
205
355
206
-
* <<flow#,Calling Application Services in Flow>>
207
-
* <<hilla#,Calling Application Services in Hilla>>
356
+
.Components Can Be Attached and Detached Multiple Times
357
+
[IMPORTANT]
358
+
When adding a detach listener inside [methodname]`onAttach()`, always remove it when the component is detached. Otherwise, if the component is reattached later, multiple detach listeners will accumulate, leading to potential memory leaks.
0 commit comments