From a83f73dbd5c564e8cd823ca3b1f40b80f2bcbe7f Mon Sep 17 00:00:00 2001 From: russelljtdyer <6652767+russelljtdyer@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:14:47 +0200 Subject: [PATCH] Initial edits. --- .../application-services.adoc | 24 ++++++++++++++----- .../application-layer/index.adoc | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/articles/building-apps/application-layer/application-services.adoc b/articles/building-apps/application-layer/application-services.adoc index 5cbd665d1f..1424829d98 100644 --- a/articles/building-apps/application-layer/application-services.adoc +++ b/articles/building-apps/application-layer/application-services.adoc @@ -4,6 +4,7 @@ description: How to design application services. order: 10 --- + = Application Services The application layer API consists of _application services_. In a simple Vaadin application, the application services reside inside a single <<{articles}/building-apps/architecture/components#,system component>>, as illustrated on the following diagram: @@ -35,7 +36,8 @@ In a situation like this, you should create a new `PaymentSummaryService`, and i // TODO I want to write something about service methods being atomic and stand-alone, and that a service method should never call another, but I don't know how to formulate it. Maybe under its own section. -== Interfaces or Classes? + +== Interfaces or Classes In the early days of Spring, services consisted of both an interface, and an implementation class. The reason for this is that Spring uses proxies to handle various cross-cutting concerns. At that time, you could only create proxies of Java interfaces, not classes. Furthermore, when writing tests, you could only mock interfaces, not classes. @@ -63,10 +65,12 @@ public class PaymentService { The advantage with this approach is that you have one Java file less to maintain. Both approaches are suitable for Vaadin. -== Input and Output + +== Input & Output Application services often need to communicate with <<{articles}/building-apps/application-layer/persistence/repositories#,repositories>> to fetch and store data. They also need to pass this data to the presentation layer. For this, there are two options: pass the entities directly, or pass Data Transfer Objects (DTO:s). Both have their own pros and cons. + === Entities When the application service passes the entities directly to the presentation layer, the entities become a part of the application layer API. Many service methods delegate to the corresponding repository methods, for example like this: @@ -101,6 +105,7 @@ It is also a good idea when your entities are _anemic_, which means that they on In both of these cases, the user interface and the entities are likely to change at the same time, for the same reason. For example, if you need to add a field, you'll add it both to the user interface, and the entity. + === Data Transfer Objects Sometimes, it is not a good idea for the application services to return the entities themselves. @@ -115,7 +120,7 @@ In this case, the application services should accept DTO:s as input, and return This adds another responsibility to the application service: mapping between entities and DTO:s. -If you are using <<{articles}/building-apps/application-layer/persistence/repositories#query-objects,query objects>>, you can do the mapping in them by returning their DTO:s directly. In this case, the query object DTO:s become part of the application layer API. +If you're using <<{articles}/building-apps/application-layer/persistence/repositories#query-objects,query objects>>, you can do the mapping in them by returning their DTO:s directly. In this case, the query object DTO:s become part of the application layer API. For storing data, your services typically have to copy data from the DTO to the entity. For example, like this: @@ -150,10 +155,12 @@ public class CustomerCrudService { When using DTO:s, you have more code to maintain. Also, some changes, like adding a new field to the application, requires more work. However, your user interface and domain model are isolated from each other, and can evolve independently. + === Domain Payload Objects If you are using <<{articles}/building-apps/application-layer/domain-primitives#,domain primitives>>, you can, and should, use them in your DTO:s as well. In this case, the DTO:s are called _Domain Payload Objects_ (DPO). They are used in the exact same way as DTO:s. + == Cross-Cutting Concerns Application services act as the main entry point into the application from the user interface. Because of this, they have some extra responsibilities in addition to handling the business activities. The most important ones are security, transaction management, and observability. @@ -200,6 +207,7 @@ See the https://docs.spring.io/spring-framework/reference/core/aop.html[Spring D [NOTE] Each cross-cutting concern deserves a documentation page of its own. This section will be updated as new pages are written. + === Security All application services in your Vaadin application should be protected by Spring Security. You should do this regardless of how your user interface views are protected. Even methods that do not require authentication should be explicitly declared to permit anonymous users. @@ -210,6 +218,7 @@ See the https://docs.spring.io/spring-security/reference/servlet/authorization/m // TODO Add a link to a separate page about security once written. + === Transactions All application service methods that touch the database should always run inside their own transactions. You should use the `REQUIRES_NEW` transaction propagation. @@ -218,6 +227,7 @@ See the https://docs.spring.io/spring-framework/reference/data-access/transactio // TODO Replace this link with a link to our own documentation page. + === Observability If you want to observe what your application services are doing from the outside, you can use https://micrometer.io/[Micrometer Observation]. @@ -226,6 +236,7 @@ See the https://docs.spring.io/spring-boot/reference/actuator/observability.html // TODO Add a link to a separate page about observability + == Vaadin Integration For Flow user interfaces, you inject the application services directly into your views, like this: @@ -257,6 +268,7 @@ public class PaymentService { See the <<{articles}/hilla/guides/endpoints#,Hilla endpoints documentation page>> for more information. + == Scaling As the application grows, it makes sense to split the application services component into smaller parts. It is recommended to split the services according to which _bounded context_ they belong to. @@ -273,9 +285,9 @@ Bounded contexts are often associated with <<{articles}/building-apps/architectu In <<{articles}/building-apps/project-structure/single-module#,single-module projects>>, you should place the bounded contexts into their own packages. The three contexts from the example above would correspond to the following Java packages: -* `com.example.application.qm.services` (Quotation Management) -* `com.example.application.om.services` (Order Management) -* `com.example.application.crm.services` (Customer Relations Management) +- `com.example.application.qm.services` (Quotation Management) +- `com.example.application.om.services` (Order Management) +- `com.example.application.crm.services` (Customer Relations Management) In <<{articles}/building-apps/project-structure/multi-module#,multi-module projects>>, you should place the bounded contexts into their own Maven modules. diff --git a/articles/building-apps/application-layer/index.adoc b/articles/building-apps/application-layer/index.adoc index dad0af9a07..752139ecd8 100644 --- a/articles/building-apps/application-layer/index.adoc +++ b/articles/building-apps/application-layer/index.adoc @@ -4,6 +4,7 @@ description: How to build the application layer of Vaadin applications. order: 40 --- + = The Application Layer Vaadin applications consist of two <<{articles}/building-apps/architecture/layers#,conceptual layers>>: the presentation layer, and the application layer. The presentation layer contains the user interface, and the application layer everything else in the application.