The Bakery project: a Vaadin Flow full stack demo application

21 March

Taking inspiration from the Vaadin Bakery app, we decided to write a full stack demo that simulates the orders management of a real bakery online store.

The goal of the project is to show how to create a graphical interface built through the new Holon Vaadin Flow module and in general how easy it is to develop a complete application through the Holon platform. Inside the post we will analyze the main features of the application, then we will go deeper inside the project viewing the various application layers and the building blocks that we used to write the application code.

Introduction: features, application layers and building blocks

Here's the main features we provided:
  1. Backoffice function to insert and manage users
  2. Backoffice function to insert and manage bakery products
  3. Storefront function to manage the whole lifecycle of the orders
  4. Analysis function on sales data through dynamic graphs
The Bakery application's software is divided into three main common logical layers, that are:
  • Presentation layer
  • Business logic layer
  • Data model layer
The building blocks used to implement each of the layers are: The full code of the demo is available on GitHub at https://github.com/holon-platform/holon-vaadin-flow-demo.

Let's code: project setup and code

Project setup

The first thing we need to do is setup the Bakery application. We use Maven to manage it through the pom file. We need to import the Holon Platform BOM; this will manage all Platform dependencies ensuring to have the right version of each artifact.
<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>com.holon-platform</groupId>
			<artifactId>bom</artifactId>
			<version>${holon.platform.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
    </dependencies>
</dependencyManagement>
We need two specific modules of the platform so the <dependencies> section will be populated with the following modules:
<!-- Holon JDBC Datastore with HikariCP starter -->
<dependency>
    <groupId>com.holon-platform.jdbc</groupId>
    <artifactId>holon-starter-jdbc-datastore-hikaricp</artifactId>
</dependency>
This is the Holon JDBC Datastore Spring Boot starter, used to automatically configure the data access layer.
<!-- Holon: Vaadin starter -->
<dependency>
    <groupId>com.holon-platform.vaadin</groupId>
    <artifactId>holon-starter-vaadin-flow</artifactId>
</dependency>
The second is Holon Vaadin Flow starter that represents the platform support for the Vaadin Flow (10+). To complete the <dependecies> section we will define:
<!-- H2 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.197</version>
</dependency>
the H2 in memory database dependency;
<!-- Vaadin Board add-on -->
<dependency>
    <groupId>com.vaadin</groupId>
    <artifactId>vaadin-board-flow</artifactId>
    <version>2.2.0</version>
</dependency>

<!-- Vaadin Charts add-on -->
<dependency>
    <groupId>com.vaadin</groupId>
    <artifactId>vaadin-charts-flow</artifactId>
    <version>6.2.2</version>
</dependency>
a couple of Vaadin addons to use Charts and Board components. Project setup is completed through the application.yml file:
holon:
  datasource:
    url: "jdbc:h2:mem:test"
    username: "sa"
        
server:
  port: 8080
This will auto-configure a Holon JDBC Datastore, that will be available and accessible as a Spring Bean and will also configure an embedded Tomcat that will listen HTTP connections through port 8080. Database will be created and populated in memory at startup setting schema.sql and data.sql files.

Configuration

Now that the project setup is complete, we can give a brief overview of the main aspects from the Java development point of view. Let's start from the Spring entry-point, that is the Application class.
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

	@Bean
	public Realm realm(AccountProvider accountProvider) {
		return Realm.builder()
				// add an Authenticator using default Account interface and given AccountProvider
				.withAuthenticator(Account.authenticator(accountProvider))
				// use the default Authorizer
				.withDefaultAuthorizer().build();
	}

	// Account provider
	@Bean
	public AccountProvider accountProvider(Datastore datastore) {
		return userId -> datastore.query().target(User.TARGET).filter(User.EMAIL.eq(userId)).findOne(User.USER)
				// map the user PropertyBox to an Account
				.map(user -> Account.builder(userId)
						// details
						.withDetail(User.USER_DETAIL_NAME, user.getValue(User.NAME))
						.withDetail(User.USER_DETAIL_ID, user.getValue(User.ID))
						// build user stored credentials declaring SHA-512 as hash algorithm
						.credentials(Credentials.builder().secret(user.getValue(User.PASSWORD)) // password
								.hashAlgorithm(Credentials.Encoder.HASH_SHA_512) // hash algorithm
								.base64Encoded().build() // secret is Base64 encoded
						)
						// load permissions
						.permissionStrings(user.getValue(User.ROLE)).build());
	}

	@Bean
	@SessionScope
	public AuthContext authContext(Realm realm) {
		return AuthContext.create(realm);
	}

	@Bean
	@SessionScope
	public LocalizationContext localizationContext() {
		LocalizationContext lc = LocalizationContext.builder().withInitialLocale(Locale.US).build();
		lc.localize(Localization.builder(Locale.US).defaultDecimalPositions(2).build());
		return lc;
	}
This class is used to auto-configure the application and run the application itself, that will be deployed to Tomcat. This will auto-configure a Holon JDBC Datastore, that will be available and accessible as a Spring Bean and will also configure an embedded Tomcat that will listen HTTP connections through port 8080. For simplicity, we decided to concentrate, in the same class, the part relating to the configuration of the platform. It was enough to define the following Spring Beans to configure authentication and authorizations:
  • Realm: the main entry-point for authentication and authorization
  • AccountProvider: used to provide Account instances using account id
  • AuthContext: an optional Bean (Session scoped) used to facilitate the representation of the current authentication and authorization context
We also defined the LocalizationContext that is the main entry-point for the internationalization architecure of the platform, enabling localization of messages, numbers and date/time. So let's quickly recap: what we have so far is a pom, a Yaml file and a Java class. That's all folks: all the configuration we need is contained in these three objects. Now we can dedicate ourselves to the implementation of the user interface.

Application coding

We don't want to go into the implementation details: the most important thing is to underline how easy and fast it is to build the graphical components of our interface through the Holon platform. The idea is to start from the Product entity and from this entity build the insertion / modification form and the data visualization table.

The data model

The Product interface is the representation of our product database entity:
public interface Product {

	public static final NumericProperty<Integer> ID = NumericProperty.create("id", Integer.class);
	public static final StringProperty NAME = StringProperty.create("name").message("Name")
			.withValidator(Validator.notBlank(Localizable.builder().message("Product name is required").build()));
	public static final NumericProperty<Double> PRICE = NumericProperty.create("price", Double.class).message("Price")
			.withValidator(Validator.notNull(Localizable.builder().message("Product price is required").build()));

	public static final PropertySet<Property<?>> PRODUCT = PropertySet.builderOf(ID, NAME, PRICE).identifier(ID)
			.build();
	public static final DataTarget<?> TARGET = DataTarget.named("products");

}
Check the documentation to learn more about the main aspects of the Holon platform Property model.

UI: form and table

To manage the informations about products we need an input/edit form and a table to view the list of data. This is the snippet of code to create the input form to populate and manage products table:
PropertyInputForm form = Components.input.form(Product.PRODUCT).hidden(Product.ID).build();
Starting from the Components interface, that is the main provider of UI components of the platform, with this single line of code we get a form whose input prompts are automatically created based on the fields of the Product property model. In this case we also removed the Product ID from the form through the hidden(Product.ID) method. All the saved products are present in the table thus constructed:
Components.listing.properties(Product.PRODUCT).fullSize().selectionMode(SelectionMode.SINGLE)
		.visibleColumns(Product.NAME, Product.PRICE).resizable(true)
		.withThemeVariants(GridVariant.LUMO_ROW_STRIPES, GridVariant.LUMO_COLUMN_BORDERS)
		.dataSource(datastore, Product.TARGET).withQueryConfigurationProvider(this)
		.withDefaultQuerySort(Product.NAME.asc())
		.renderer(Product.PRICE, new TextRenderer<>(item -> "$" + lc.format(item.getValue(Product.PRICE), 2)))
		.withItemClickListener(evt -> {
			enableForm(true);
			form.setValue(evt.getItem());
			btnInsertUpdate.setText("Update");
		}).build();
This is the product table which is a single-selection table, with two visible and resizable columns. The target of the table dataset is the product entity and we set this with the datasource(datastore, Product.TARGET) method. We have set a default sort on the product name field and we also rendered the price column with a TextRenderer. The single click on a table row will trigger a listener with actions defined inside the lambda of the withItemClickListener(evt ->…) method.So a table with all these features will be the result of this single fluent instruction provided by the Holon platform builders. This is a screenshot of the result:

Summary

We've seen how to setup the Bakery demo project, the main configuration and some snippet of code taken from the presentation layer to underline how quickly and easily you can develop a full stack application through the Holon platform. Looking at the source code of the application at https://github.com/holon-platform/holon-vaadin-flow-demo you'll find the complete code to see the the newly created Holon Vaadin Flow module in action to build all the user interface.
  • Fabio Paroni

We use cookies to ensure that we give you the best experience on our website.