Bakery’s guiding principle is "Create what is needed, do not create what you might think possibly will be needed sometime in the future".
It is responsible for providing the data displayed in the UI, and for handling updates to the data.
It hides the data layer implementation details from the UI, e.g. Spring Data, so as the UI can use descriptive high-level methods. In addition to making the UI code easy to read, this provides a logical place to handle transaction boundaries, like data level access control (not implemented in this app) and other database related concepts.
The service layer could quite easily be extracted to a separate Maven module, though, in Bakery it is in the same module because of simplicity. The advantage of a separated module, is that you can verify that you are not accidentally using the UI layer from the service layer.
The service layer in this app starter is built upon Spring Data. Validation in the service layer is performed using bean validation and annotations on the entity classes.
An H2 in-memory database is used by default for easy setup and getting started. This should be swapped out for a real database before going into production, see changing the database.
The UI layer is responsible for creating the visual interface, reacting to user events, calling the service layer to fetch and update data, and updating the UI based on changes.
As the UI layer is built using Vaadin 10+, it is based on composition. Everything you see on the screen is a component, possibly containing other components.
There are two types of blocks involved when creating the UI: Basic UI Components and Application Components (a.k.a. Templates).
Basic UI Components are the components shipped with Vaadin 10+, or included in the project as add-ons. These include components such as
Button where you mainly listen for user events, layout components like
FormLayout, and user input components such as
TextField. The basic UI components are general purpose and can be used in any application or replaced by other components, as they are not tied in any way to the application logic.
Application Components are components created specifically for the application. They differ from a Basic UI Component in the way that it knows the application particularities. Although you might not be aware that you are creating components, any view in your application is a component, its parts can be extracted to smaller application components, and so on.
The Pattern followed for coding Bakery is the Model-View-Presenter (MVP) or a variant thereof. In this pattern, the Model represents the Data to be displayed or updated, the View represents the UI, and the Presenter is the glue code between both. In Bakery, the Model is part of the Service Layer described above, and the UI Layer might contain View and Presenter.
When a component is simple enough, you can implement it by using a small amount of HTML disposed in a template, and a few lines of Java code that fits into one class. This is the case of
ProductForm. It can be easily created by using Vaadin Designer, that splits out the layout configuration into the HTML template
product-form.js, and its companion
ProductForm.java file, making possible that components in the layout can be accessed from Java.
In the other side, some application components with much functionality can be very complex. To deal with them, the implementation should be split into multiple parts, each with a clear responsibility. In this app the first split is Layout and Logic, using Vaadin Designer for the layouts. The logic (event listeners, service-layer calls, etc.) is then added to a separated Java file which is the Presenter.
Whenever there are views with similar behavior, you might extract their common code into reusable classes, this is the case of
ProductView views. They share code in their views and presenters by extending
A View in Vaadin terminology does not quite match the "V" in MVP, where it is used to denote the UI part of the component. In Vaadin, a View is simply something that can be navigated to by using a specific URL, as configured with the
In Bakery, when MVP pattern is utilized, the view decides which components to use and how to lay them out (the template file is considered part of the view), it wires event listeners, configures how fields are bound, and what validators to use in the UI. Validators are just UI level helpers, the real data validation takes place in the service layer.
Presenter otherwise, is the one aware of the service layer, while view is not, it deals with what should happen on view when events happen, and it also handles navigation to/from views.
|To follow the guiding principle, Bakery MVP implementation does not try to accomplish goals like swapping the View class with an implementation not based on Vaadin 10+ etc. The presenter class is also aware of Vaadin 10+ concepts, but does not care about specific components used in the view.
Application components are built to be standalone and reusable in multiple places. To avoid coupling them to other application components, they provide an API which can be used to configure them and/or they fire events to indicate that something has happened, they might be of interest to other components.
References to components are acquired by utilizing dependency injection, e.g. by using the
@Id annotation. After injection, the component API can be used to e.g. add an event listener.
Components can be added dynamically to the view. For example, a number of
OrderItemEditor instances are created and added to a
OrderItemsEditor based on the number of items in the Order.
|Keep it simple, do what you need now and refactor when you need more.
|Events should tell what happened and not what action the receiver should take. This makes code much more clear and also ensures the responsibilities stay clear.