Collaborative app: event when database change

Hi,

I am making this demo app, where you can ‘draw’ online.
Demo here: https://runny.herokuapp.com/
Source here: https://github.com/Tyvain/myalert

I want to demonstrate the ‘power’ of a collaborative Vaadin app.

So the database (H2 for the moment) stores every pixel with position and color.
Each pixel is then display as a Vaadin button. When you click on a pixel it changes the button color (with the selected color on top) and save the pixel to the database.

Everything works, but now I want to make it ‘collaborative’.
→ if someone else change a pixel I want ‘my display’ to update.

Question: How can I do this ? I have now idea where to start… (the goal is to update only the pixels that are updated in base by someone else)

PS: If someone want to help me do this project, and make it a good demonstration to prove the power of Vaddin, feel free to make pull request on github.

Heh. Looks like Reddit Place.

You most likely do not want to do it on the database layer, but in a service layer.

A little bit about app architecture first.

This would be a quite traditional achitecture of the app.

Vaadin app -> PixelService -> PixelRepository -> Database

Now you have save(Pixel) in MainView, but it might make sense to separate UI and backend to make logic being able to run independently from UI. Backend does not depend on Vaadin and could in theory be run without the UI. Makes code easier to maintain and produces less side effects.

Okay back to the question.

You need a way to notify all users

When you get an event to save a new pixel, and you successfully save it through the Repository, you should send an message to all users. There are multiple ways of doing it, but an EventBus is one way. Think of it like the listeners/events you have in your app - a part of the code fires an event when something happens, and other parts of the code says that they want to be notified when the user takes an action. Event busses does the same but it is more about sending messages to disconnected parts of the application, or to multiple users.

As you have a Spring app there, using [Spring Reactor]
(http://https://www.baeldung.com/spring-reactor) as an event bus is an option, but there are other implementations as well. What you do is that you fire an event out after an successful save

class PixelService {

  @Autowired
  EventBus eventBus
  
  private void save(Pixel pixel) {
    pixelRepository.save(pixel);
    eventBus.notify("mainView", Event.wrap(pixel));  
  }

Now a message is fired out to the world each time an pixel is updated. Everybody that wants to listen to it, can. Now you want to make each MainView listen to that

public class MainView extends VerticalLayout implements Consumer<Event<Pixel>> {
  ...
  @Override
  public void accept(Event<Pixel> event) {
    Pixel pixel = notificationDataEvent.getData();
    // Here you have the updated pixel, you can directly
	// use that info to update the UI for every user or
	// load the data again from DB. 
	Button button = findButtonAtPoint(pixel.getPoint()).setClassName(pixel.getColor());		
  }
}

Now each users’ board should be updated. Sadly there is one problem. The other users won’t notice the change before they interact with the UI. Then their command, as well as the commands from all other users happen all at once. This comes to the next point…

You need to push data to the browsers

In Vaadin, all communication between browser and server is initialized by the Browser. Therefore if something changes on the server side, the browser will not be updated automatically for that. To get instant updates, you have to enable [push]
(https://vaadin.com/docs/v12/flow/advanced/tutorial-push-configuration.html). This tells the browser to open a port immediately and listen to things that happens on the server, and update immediately when change is noticed.

@Route
@StyleSheet("frontend://styles.css")
**@Push**
public class MainView ...

One last thing: With push, it can happen that two threads try to update the same thing at the same time, and then you get unexpected behaviour. Therefore you should instruct them to take turns with uiAccess. Wrap the ui changes you do into the method:

@Override
public void accept(Event<Pixel> event) {
  ...
  ui.access(() -> Button button = findButtonAtPoint(pixel.getPoint()).setClassName(pixel.getColor()));
}

That should be it. Hopefully it was not too confusing. As a summary there was two things you should check out: 1) Event bus or other messaging systems and 2) Push. I wrote the code directly to the forum without running it so there are certainly some errors and typos there.

Good luck and happy coding. :slight_smile: