Engineering

Asynchronous MVP Design with JavaFX

So for the last couple of months I have been reading about architecture and design of software products, mostly about n-tier clean architecture and MVP as a presentation layer.

After some trials and errors I have managed to implement my own simple solution for Android, and eventually have decided to port it to a different technology.

I thought I might give JavaFX, which is intended to replace Swing as the standard GUI library for Java SE, a try.

JavaFX’s approach to user interface construction is similar to Android, because of the declarative nature of the user interface layout. This means that the user interface is created in a “special” file, called FXML (basically an XML file), dedicated for this purpose. Of course you can also create the user interface in code, both for JavaFX and Android. However I recommend using the declarative approach because it is cleaner and more decoupled, which are some of the characteristics of good software design.

Having read briefly some tutorials about JavaFX and having more experience with Android, I have attempted to emulate the solution developed in Android for JavaFX. For most parts I have had no issues in porting the solution. The only part that is different in the JavaFX solution is the navigation between views, or scenes how they are called in JavaFX, but this might also be because of my lack of deep knowledge of the JavaFX toolkit.

The main idea was to implement a simple asynchronous MVP solution, packaged as a feature, that is also testable. Navigation was not meant to happen, it exists only out of curiosity. The JavaFX toolkit might have a better approach, so if you are interested in this topic I encourage you to do your own research.

However I do consider the solution a good starting point for MVP in JavaFX, and appropriate for small projects, any even medium.

The application from a user perspective

Let’s start by seeing the application in action:

JavaFX MVP - FeatureX
JavaFX MVP – FeatureX

 

JavaFX MVP - FeatureY
JavaFX MVP – FeatureY

 

As you can see the application has two views, FeatureX and FeatureY.

Each view has a couple of buttons that start some requests and post some responses, random strings, to the user interface. Each request is asynchronous, and you can even switch between the views while the requests are in progress. Each request also has progress indicator to make the example a bit more spicy.

To better understand the topic, I encourage you to get the code, and have a look see yourself: JavaFXAsynchronousMVPBoilerpart.

The code can be imported in your favorite IDE as a Maven project.

 

The application from a design perspective

This is a post about architecture and design. Architecture is about the “what”, while design is about the “how”. The architecture requires a decoupled and testable asynchronous solution, and the design implements the architecture by using an MVP pattern.

From the perspective of an n-tier architecture, MVP is the implementation of the presentation layer.

Generic 3-tier clean architecture
Generic 3-tier clean architecture

The example application does not have a domain layer, data layer, or any other layers. It is just an implementation of the presentation layer. Usually the domain layer and data layer are part of a RESTful web service implementation, which resides on a server somewhere, while the presentation layer can be a desktop application, Android application, Web application, console application, etc. In this particular case it is a desktop application built in JavaFX.

The first design step was to identify the main features of the application, in order to create the appropriate class package structure.

I have considered each view implementation to be a separate feature, and have packaged them accordingly:

Design of features
Design of features

Each feature is implemented as an asynchronous MVP design:

Model View Presenter
Model View Presenter

Having the following UML class diagrams:

FeatureX UML diagram
FeatureX UML diagram

 

FeatureY UML diagram
FeatureY UML diagram

The design is according to the OO principle “program to an interface not an implementation”. The methods that I expect to use are declared in appropriate interfaces, to which the implementation must adhere. Basically this establishes a contract between application components, and with the help of polymorphism, you always get from an object what you expect to get and nothing more . There is also the advantage of replacing application components with other, condition being they both implement the same interface, adhere to the same contract so to speak.

You can get a better overview of the OO principle “program to an interface not an implementation”, by seeing more detailed ULM diagrams for the features:

FeatureX contract UML
FeatureX contract UML

 

FeatureY contract UML
FeatureY contract UML

As you can see, the view of both features implement a “setNavigationController” method. This navigation controller is the class that implements the navigation between features.

To better understand the workflow of the application, including feature navigation, I believe it is appropriate to also see the UML diagram of the application class:

Desktop application UML
Desktop application UML

Here the application class is also the navigation controller. This is obviously a violation of the OOP “single responsibility principle”, meaning the application class has more than one responsibility: main application entry point, navigation controller, and even user interface creation. Nevertheless the desired result was MVP in JavaFX, and as I have already mentioned, navigation was not meant to happen, and exists only out of curiosity. The JavaFX toolkit might have a better approach, so if you are interested in this topic I encourage you to do your own research.

 

Component testing

Testing is a crucial part of successful software design.

As you can see, the testing packages have the same structure as the “production” code:

Features tests
Features tests

Bellow you can see the tests for both features, represented in UML:

FeatureX component testing UML
FeatureX component testing UML

 

FeatureY component testing UML
FeatureY component testing UML

Not much to say about tests here, just remember that you should always have them!

Asynchronous behavior

What is it and why do we need it? The application runs in a single continues thread. It loops forever until the application is terminated. So if any long running operations run on this thread, the whole application will appear to be frozen. Imagine this happening in GUI application, basically you could only do one operation at a time. This synchronous behavior is bad, especially bad in GUI application.

The desired behavior is asynchronous. This means that each operation, “request” in the context of this example, is executed on a separate thread from the application thread. As soon as the operation is finished by the executor thread, the result is posted back to the user interface in the application thread. During a running operation you can start any other operation, in whichever order you want, while the user interface remains responsive.

The asynchronous behavior is implemented in the presenters. Bear in mind that this is just a suggestions, there are a lot of schools of thought in implementing asynchronous operations in MVP.

Here is s snippet of code from XPresenterImpl showing the implementation of an asynchronous operation called requestAction1:

import static javafx.application.Platform.runLater;
...
private ExecutorService service;

public XPresenterImpl(...) {
...
   this.service = Executors.newCachedThreadPool();
...
}

@Override
public void requestAction1() {
    service.execute(() -> {
        Util.simulateNetworkLatency(5000);
        XResponse result = model.requestAction1();
        runLater(() -> view.postResult1(result.getResult()));
    });
}

Some might say that this code is ugly. More ugly if you do not use lambda expressions. However I find it simple to understand and follow, and appropriate for a lot of solutions where simple asynchronous operations are required. In all depends on a lot of other factors too, and working with threads in Java is always a bit messy.

The workflow is as follows: a request comes from the view and goes to the presenter, the presenter starts the executions of the request in a new thread, as soon as the operation is finished, the result is posted back to the view in the main application thread in the runLater method. That is it.

 

Lessons learned

Always have a plan before you build software. Even if what you want to build is small, plan first. Take enough time to understand what you want to build, take your time to define the requirements, take your time to create the architecture, take your time to understand the technologies you have to work with, take your time to consider the design abstractions that you should use (e.g. in our case MVP) in the context of the technologies you have to use, and doing so the implementation becomes a simple chore of simply “translating” the requirements into code.

Design concepts, like MVP, are universal. They are technology agnostic. So it is imperative to know about their existence and purpose. Learn about them and start using them in your own applications asap, it will make a huge difference. Remember, reinventing the wheel is not a best practice.

You should always have tests! Their existence from a human perspective gives you peace of mind, because they make sure that nothing was broken during a new development phase. To get the sweet peace of mind, run the tests often.

Spread the knowledge