Meet Whereabouts, the new Vaadin example application

Hello all,

During the past years, we have received many requests for an updated example application that is more than just another todo-list. At Vaadin Create 2025, I demonstrated our new full-stack example application Whereabouts and some of the reasoning behind its design.

The application aims to give you a consistent example of how you can implement a business application using Vaadin 25, Java 21+, Spring Boot, jOOQ and PostgreSQL. It uses modern Java language features like records and pattern matching and new Vaadin features like signals, the Master Detail Layout and the Aura theme. It implements realistic use cases like data pagination, filtering and sorting, custom forms, custom icons, etc.

It also isn’t finished. That’s where you’ll come in. Some features of the application are quite polished, whereas others are still very rough around the edges. Many features that should be in there are completely missing. We’re now asking you to check out the project and share your thoughts:

  • Do you find something that you think could be done better, or something that you think is downright wrong?
  • Do you find something that you particularly like?
  • Is there an example feature that you would love to see added to the application?

Then please file an issue at GitHub, or maybe even a pull request! You can find the source code here: GitHub - vaadin/whereabouts

There are already some open issues about missing features and things that could be done better. Feel free to participate in the discussion!

If you want to know what I was thinking when I wrote the code, checkout the design decision records. This was requested at Vaadin Create, so I haven’t had time to write everything down just yet - stay tuned.

My hope is that we could use this example application to explore and evolve the Vaadin Way of building business applications on this particular tech stack together.

-Petter-

PS. No, this is not StarPass. StarPass was originally created to explore and showcase user interface patterns, and has a more or less mocked backend. Whereabouts is a fully functional full-stack application, albeit with a less fancy UI. Who knows, maybe we end up merging them at some point? DS.

3 Likes

Disclaimer; I haven’t studied the project (yet), just adding a list of typical things you might want to showcase that I think are really common (no particular order):

  • anonymous pages next to the login page like e.g. registration
  • login page with Username/PW or external oauth provider
  • Different page visibility (especially in sidebar) based on views
  • Multi level hierarchy in the sidebar
  • Simple REST API with basic auth (different user than the UI users) to showcase that Vaadin can easily be integrated into established systems ans showcase an API within the same app
  • Collapsing sidebar so that only icons are visible
  • Bookmarkable search / filter (“share deep link with colleague”)
  • Visual auto logout timer
  • Lazy loading data provider with sorting and pagination (custom ID provider)
  • Complex form where some parts depend on others to be made visible
 e.g. “form in form”
  • Editing a nested 1:n relationship
  • Cross field validations
  • Show a summary of all errors when submitting
  • BeforeLeaveObserver for forms with a confirm dialog if a field is changed
  • Showcase route templates (e.g. /:groupId/users/:userId)
  • recommend a way how to structure views
 (constructor
 beforeEnter
 afterEnter
 on attach
)
  • Show long running operation (e.g. slow submit button)
  • Move slow running operation in a thread pool
  • UI / Route / Session and Application Scoped Beans
  • Lazy loading of components
 tabs, details
 accordion and so on
  • i18n
  • a11y / responsiveness
  • logging
  • Entity versioning / locking so that no change is lost
  • Custom Security Annotation with Enum instead of strings
  • base view / route that’s extended by all views to handle security, logging as well as other stuff
3 Likes

When working with jOOQ, it is natural to use IDs to refer to other entities. This is problematic when working with e.g. Grids and ComboBoxes that contain the entities themselves. To select an item in a grid, you first have to fetch the complete entity from the application service and then select it. To bind a combo box to a property that takes an ID, you need to create a converter that maps between the entity and the ID (simple), and the ID and the entity (needs a database call).

About selecting an item in a grid:
This is not different from any other solution. What exactly is the problem?

Combobox and Select:
Yes, you need a converter. But this is a single query, no problem IMO. On the other hand, if you have just a few items and use setItems with a List, keep the data you pass to setItems.

Data Loading:

I had a quick look at the code. Why do you use an IdentityProvider when you have equals/hashCode implemented?

For example:

projects = new CallbackDataProvider<>(
    query ->
        projectService.findProjectListItems(
            searchTerm.peek(),
            query.getLimit(), query.getOffset(),
            sortOrder.peek().getSortOrder()
     ),
     query -> {
         throw new UnsupportedOperationException("Count not supported");
     },
      ProjectListItem::projectId
);

ProjectListItem has equals/hashCode

About Records in Binder: this is a strange idea (yes, I know Vaadin supports that) as there you want to have mutable objects, as usually a Binder is used to update data.

For simple cases, I use the UpdatableRecord generated by jOOQ directly.

1 Like

You may also want to check out my Vaadin/jOOQ example:

1 Like

General comment: I used to love the good old bakery example. Bakery could be the perfect domain for such an example application. Everybody loves bread, it’s easy to understand but has a lot of use-cases that can even show-case pro components like Maps (where is the next bakery) or charts (how many breads got sold each day). So I would highly suggest to use a domain people can easily grasp and build-upon.

Some high and low level comments regarding the available source code - it’s probably not complete
 took some time


Dependencies:

I would definitely remove the following:

  • jOOQ
  • Flyway
  • Testcontainer

I’m thinking about removing also:

  • PostgreSQL in favor of H2
  • ArchUnit

Even though the selected technologies are not bad, there add additional complexity which is IMHO not needed in a „Vaadin Showcase“. None of those dependencies (except ArchUnit) have influence on the way you build your Vaadin Applications - therefore they only make it harder for „newbies“ to understand what is happening, why there are present and so on.

jOOQ: While pretty cool and has a big following within the Vaadin Community. I would say JDBC / JPA (Hibernate) should still be used. Database Management is really „biased“
 so it’s really hard to make it „perfect“ for everybody - therefore would I suggest to make it „as simple as possible“ which would be plain old JPA with Spring Repositories.

Flyway + Testcontainer: Adds complexity that should not be needed in a show-case app how to build good Vaadin UIs.

PostgreSQL: I would replace it with H2 so that people are not forced to have database running to play with the application.

ArchUnit: I like it personally, but it might add a little bit too much „problems“ for people that wanna check our Vaadin.

Tests

Did you reinvent „Integrationtests“ with your own annotation and non-standard naming scheme? Please don’t. Maven Surefire and Failsafe Plugin exists for reasons. None of your test classes follow the required naming for Failsafe Plugin to pick them up.

Biased: AssertJ +1

VaadinErrorHandler.java

I’m not a fan of this design
 I would create proper classes that extends ErrorHandler, inject context, filter exceptions via Spring’s DisconnectedClientHelper as well as NestedExceptionUtils. At the current state this implementation is a downgrade to the default error handler.

UserId.java (can be applied to all ***Id)

Why are those not records? Immutable ID „classes“ (read records) are the first thing that come to mind.

TaskStatusFormatter

Suggestion 1: add the human readable text as field to each enum
Suggestion 2: add a translation key to as field to each enum allowing this to be retrieved in the frontend language-dependent (+ e.g. interface „Has18n“ which could be added to all enums to allow to create a single renderer for all enums in the UI)

using „private static VaadinComponent create(A, B
)

Using „static“ to create Vaadin Components might result in people doing stupid things
 I would suggest to remove this keyword


TODO I’m not sure what I feel about this style of creating views

I’m sure how I feel: like said on Github, please don’t do it.

ProjectsNavigation

+1. Cool idea, not sure about the naming (couldn’t come up with something better)

Binder with Records

Records represent immutable data structures. Editing records with the Binder feels IMHO totally obscure and should not be advertised. Support of records with Binder is even broken that all properties need to be present
 otherwise it fails
 you can’t use bind(::get/::set) which forces you to use bind(string) which is not typesafe
 all good things lost
 it’s just a nightmare to maintain.

Design Decision 001: Package Structure

Controversial topic
 and understandable points. Personally, I already find the project really hard to read with like 20-30 classes in a single package and it does not even have much logic to begin with. I prefer „layers“ - even tho that results in „too much“ public classes


I would have structured like this:

api/ - common classes shared by services and UI, enums etc.

  • project/

app/ - general classes affecting the whole application

  • security/ - all stuff regarding security (except User(roles)
 I would put those class into API
  • vaadin/ - stuff like AppShell
 NpmConfigurations
 ErrorHandler
 SystemMessages
 ApplicationInitListener

backend/ - all classes related to none-UI stuff
 names should be self explanatory

  • entity/
    • project/
  • repository/
    • project/
  • schedule/
    • project/
  • service
    • project/

ui/ - all classes related to the UI

  • shared/ - stuff like NotifcationUtils
 ComponentCreator

  • views/
    • project/ - all views related to projects
      • form/ - forms , binder etc. for the views
1 Like

This sounds exciting! Will check out in detail later on.

After giving it a cursory glance, I was reminded of a question about usability for a long time. I think this could be a good place to ask.

In modern web interfaces, the profile/preferences/logout controls are almost always on the top right.
However, in all the default Vaadin examples I have seen, they are on the bottom left, which I find a bit jarring. I never liked this, and I place it on top right in the few projects I work on.
Interestingly, even the Vaadin website, and this forum itself has the user control on top right!

Is there some design philosophy for the bottom left placement which I am not aware of?



A good question. In this particular case, it is actually not any design philosophy, but a question of screen estate. Having it at the navbar would IMO have wasted space at the top of the screen, and adding it to the view headers would cause problems with the master-detail layout. In other words, it was easier to put it in the bottom-left corner.

That said, you are right that most applications have it in the top-right corner. In fact, in the original sketches that I got from our UX designers, the user menu was an avatar in the top-right corner. I’ll have another go at moving it there.

1 Like

This is a left-over from when I was trying out various solutions. Will clean up to use only one or the other.

Thanks for your comments. I’m not going to address them all here, but I read them all, and am considering them all. Some of them I agree with, others I don’t. :slight_smile:

However, I do want to point out that I want this application to not only showcase how to build good Vaadin UIs, but also how to do the backend properly (i.e. a full-stack Vaadin application). Looking back at my time as a consultant, there were surprisingly many issues with that part and so I want to address that as well. That’s why it contains things like PostgreSQL, jOOQ, Testcontainers, etc.

I’m going to start working on a new Getting Started tutorial especially designed for beginners. If you have suggestions what such a tutorial could contain, I’d love to hear about them.

-Petter-

While many websites and apps show the user menu in the top-right corner, I would argue that it’s closer to 50/50 based on personal observations. No data to show, but it’s the feeling I get from the weekly Mobbin.com updates I follow. One high-profile example, where the user menu is in the bottom of the sidebar, is ChatGPT.

I have a few reasons for placing it there in our recent examples/starters:

  • Technically easier to manage responsiveness (i.e., different viewport sizes), as we don’t need to worry about breakpoints, when to show/hide elements in the navbar when space is limited. I think we need some more helpers in the product to make this option more feasible without a lot of custom CSS.
  • Leaves the full viewport height for the app view/content, which is especially beneficial on small viewport sizes.
  • Hypothesis: in the types of apps commonly built with Vaadin, having access to the user menu at all times is not a top priority: you sign in, you do your work. You rarely if ever need to view your “profile”, or edit preferences/settings. The bottom-left corner of the app is one of the most unobtrusive places to put it.
2 Likes

I would like to see a search bar that functions similarly to those in GitHub and GitLab.



I am aware that such a component does not yet exist.

I would also like to see calendar integration(FullCalendar for Flow - Vaadin Add-on Directory).

Best regards
Stefan

1 Like

Great choice!

What about karibu? GitHub - mvysny/karibu-testing: Vaadin Server-Side Browserless Containerless Unit Testing

1 Like

Hi,
hopefully not too late in the game, but here a litle review from my perspective.
First of all, it’s quite good to have your thoughts stored in the Design Decision records so I use them to organize my thoughts too:

Design Decision 001: Package Structure

  • IMO a feature-based approach is better

  • I would extend your current approach with humanresources and projects tasks into separate packages on the same level: com.example.whereabouts: offices, employee, location, projects, tasks

  • however, the current package structure is only half-heartedly converted to feature-based, since below humanresources it continues with technical packages (query, repository, ui)

  • BTW: what do you prefer: singular or plural ?

Orphaned classes

  • I usually use packages appconfig and security
  • the MainLayout class resides in root next to the application class

Design Decision 002 & Design Decision 003

The fundamental problem with explicitly using value objects and entities manually is, in my view, the violation of the DRY principle: Don’t Repeat Yourself. All attributes you declare in value objects must also be declared in the DDL scripts for your DB schema. In my opinion, this is a really major source of potential errors in large business applications. And by the use of jOOQ, it’s not necessary.

Regarding your more theorectical considerations, 'value object’ is equated with ‘domain type’ or, as later referred to, ‘domain primitives’
‘value object’ should rather be used in the context of equity and identity, as described in DD002.
For wrappers around primitive types, it’s more about type-safety, as is then also explained in the constructor example.

“if you use value objects, the syntactic validation happens in converters, not validators” 
 :thinking:

I share the concerns regarding bean validation.

Design Decision 005: Records as Entities

  • IMO value objects for IDs seems a little bit over-engineered
  • the not uncommon practice of first defining an interface (e.g., for the repositories) only to then have just a single implementation anyway (the Jooq*Repository) is, in my opinion, somewhat over-engineered in the direction of YAGNI (you aren’t gonna need it) or ‘Speculative Generality’ (Fowler)

Drawbacks

You wrote:

Since a jOOQ result set is strongly typed, you can easily map a database row to a Java record by using Records.mapping(MyRecordType::new). This is no longer possible with a two-level entity structure

  • if you mean nested pojos as your example Project(ProjectId id, long version, ProjectData data) this should be possible:
	return dsl
        .select(PROJECT.PROJECT_ID,
                PROJECT.VERSION,
                PROJECT.NAME,
                PROJECT.DESCRIPTION
        )
        .from(PROJECT)
        .where(PROJECT.PROJECT_ID.eq(id))
                .fetchOptional(record -> new Project(
                        record.getValue(PROJECT.PROJECT_ID),
                        record.getValue(PROJECT.VERSION),
                        new ProjectData(
                                record.getValue(PROJECT.NAME),
                                record.getValue(PROJECT.DESCRIPTION)
                        )
                ));

can be code easier with DSL.row() and Records.mapping for all parts:

    return dsl
        .select(PROJECT.PROJECT_ID,
                PROJECT.VERSION,
                row(PROJECT.NAME,
                    PROJECT.DESCRIPTION).mapping(ProjectData::new)
        )
        .from(PROJECT)
        .where(PROJECT.PROJECT_ID.eq(id))
        .fetchOptional(Records.mapping(Project::new));

  • it’s also much shorter :slight_smile:
  • you already use the type safe fetching but with ad-hoc record mapper which is, in real world business apps - with result sets with a large number of columns - somehow tedious to write 
 but type safe and the compiler is your friend

DD006 jOOQ

  • many good thoughts and a few interesting perspectives on the problem with JPA

  • I agree with all of them :grinning:

  • You’ve got grey hair, I think I’ve got high blood pressure from JPA / Hibernate

  • I would like to add that only jOOQ makes it possible to use, in a modern way in Java, a tool - that has lost none of its power in over 40 years: SQL

DD007 Navigation Patterns

Why don’t you use the possibility to interact directly with the target view and use its public API methods as described in the official docs in How to implement routing and navigation in Vaadin ?
In the end, it’s a decision whether you want deep links (and need route/query parameters) or not, and prefer to interact more in a ‘functional’ way with a view (via its public API)

DD009

  • jOOQ converters are a very good idea
  • I use them e.g. to handle all table columns ending in *_amount as Java Money value objects so I can, for example, directly perform calculations with them, e.g., netAmount.add(taxAmount)
  • I also tried this as a database domain type once, but immediately ran into a problem in jOOQ Compilation error in generated record's POJO constructor when attaching Converter to UDT column · Issue #18793 · jOOQ/jOOQ · GitHub
  • This has since been fixed by Lukas Eder, but I agree with you that domain types are not necessarily needed
  • but, in my money type example, the advantage of using database domain type is, that the table column already contains something like (42.01,EUR) or (23.02,USD), which is very helpful in multi-currency applications
  • and again regarding the topic ‘package structure’: Here I would, as already mentioned above, prefer to always put everything in domain-specific packages, i.e., each *Converter is located with ‘its’ domain classes;
  • currently in your example it’s again a somewhat inconsistent mix of business/domain (humanresources, project) and technical (converters, query, repositories) packages :cry:

kind regard
Dominik

3 Likes

Thank you for taking the time to give such a thorough answer! This is exactly the type of feedback I was hoping to get. I will take it into consideration when I return to work on Whereabouts (I currently have other priorities).

-Petter-

If I’m following the README I’m not able to run the project in my IDE, I don’t have TestApplication and if I run the Application.java it doesn’t run (no database).

The error management of the form is not great, there is no error summary on submit. I don’t like how the error management of the custom field is done (2 errors shown in the form).
In my opinion it could have its own DD.