FontysVenlo / prj2-airline-information-system

Example for the Airline Information System project
MIT License
9 stars 9 forks source link

:imagesdir: images/ ifdef::env-github[] :imagesdir: images/ endif::[]

== Setup a layered architecture for your project

== Introduction

This tutorial describes how to setup a layered architecture for your project. Before answering the question HOW to do that, we should ask ourselves the question WHY we would do that! An important principle in general, but definitely also in Software Engineering is the KISS-principle: Keep It Stupid and Simple. Things that are simple are less error prone, and can be easily understood by others as well. Making our architecture more complex is therefore only reasonable if it serves a purpose.

== Our goals

What do we want to achieve with a proper project architecture?

== Our design starting points

Design principles are mostly focussed on avoiding dependency. But isn't that very logical? In real life, we also want to avoid dependency. Dependency causes complexity, independency gives freedom! When you have a job, you're married, have children, you have dependencies and responsibilities that restrict freedom. Why did UK think that leaving the EU was a good idea? Too much dependency can even cause that rules are set FOR you! Independency is therefore persuable. But is avoiding dependency always a good idea? It comes with a price (its own complexities) as well. Moral of the story? We have to find a balance between making things flexible but still simple.

== Let's set it up

[TIP]

With the demo application in this repository, you have received a (hopefully) working starting point for your project. HOWEVER... more important is that you are able to setup a project yourself as well. Therefore, the tutorial below describes the way to do it. We highly recommend that each group member tries to setup a working project architecture themselves. Afterwards, you can start together from scratch again, having gained relevant knowledge. The paragraphs below don't contain all the details; these can be looked up in the example implementation however. Okay, here we go...

What's key? Our Business Logic of course! The persistence layer is only a service that serves the Business Logic Layer by storing object data at any time, and retrieving these on request. From a business point of view not important. The (G)UI only enables end users to interact with the business logic. From that perspective it's only a passthrough and a messenger; relevant from a software system point of view, not from a business point of view. Assume that we want to write an application that is able to create, store and retrieve customers. We'll explain the setup step-by-step afterwards.

.pom-file in order to have informaticspom as parent [source,xml]

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

4.0.0 io.github.fontysvenlo informaticspom 0.9 nl.fontys.ais airlineinformationsystem 1.0-SNAPSHOT pom AirlineInformationSystem UTF-8

-- +

image::AISClassDiagram1.svg[Class diagram after 1st step]

[TIP]

Whenever you get issues during this project with your environment, most likely it will be related to JavaFX. In order to avoid frustration, make sure that you can create, build and execute a HelloWorld JavaFX application outside of this project context. In a temporary directory, create a new NetBeans project, select 'Java with Maven' and 'FXML JavaFX Maven Archetype (Gluon)'. You'll get a HelloWorld application out of the box. Make sure that this application builds and runs without problems, before continuing the steps below. Also make sure that you've setup SceneBuilder properly and integrated into NetBeans (Set Scene Builder Home under Preferences/Java/JavaFX).

Create a new module in your AIS-project, this time choose 'FXML JavaFX Maven Archetype (Gluon)' as your project type. This module will act as Graphical User Interface (GUI) layer. A ready-to-use JavaFX-application is generated. Benefit of this type of JavaFX project is that the User Interface definition including all styling is in separate XML-files, specifically fxml-files in this case. The User Interface Logic (No Business Logic!!!) is in separate Controller classes. User Interface Logic reacts on events (like button clicks), communicates with the business logic and updates the GUI (e.g. shows results from the business logic, enables or disables GUI controls, is responsible for navigation to other windows etc). For those aware of the Model-View-Controller pattern, the fxml-files act as 'View', the controller classes as 'Controller' and the Business Logic layer as 'Model'.

+

image::AISClassDiagram2.svg[Class diagram after 2nd step]

So, the GUI is a component that uses the BusinessLogic as a service, a Dependent-On-Component. But it shouldn't create this service itself! If it would, the GUI would be tightly coupled. When we would do GUI testing, there is no way to test its interaction with the BusinessLogic without using the real implementation of that BusinessLogic. This real implementation might not be ready or stable (e.g. depending on actual database contents). The GUI should only talk to the BusinessLogic interface (let's call it the BusinessLogicAPI) and get an actual implementation injected.

Final question, 'Who should inject the BusinessLogic implementation?' The businessLogic itself? No, we just learned that the BusinessLogic should be unaware of the presentation layer! We need another module in our AIS-project: an Assembler project that acts as starting point of our application and sets up all layers and connects them properly.

So, what do we need to do? (the steps will be explained in detail below)

-- +

The demo-implementation uses a data records approach. Each entity class (Customer for example) encapsulates a data record field (of type CustomerData in our example) and business logic. Data records are java record types, that are immutable data carrier objects that are available in all layers of your application. To make them available, we encapsulate them in a separate new module of your AIS-project. So, within your AIS-project, create a new module 'DataRecords' that is of type 'Java Application' again. Let both the BusinessLogic-layer and the GUI-layer depend on this new module.

+

[TIP]

Our demo application provides you with an implementation that supports parameterized Controller construction. It delegates the work of loading and switching views to a SceneManager class. Compare this implementation with the default implementation (the main application class) in your generated HelloWorld JavaFX application!! You'll find out that the same things take place, however we're interrupting the automatic load process.

-- +

image::AISClassDiagram3.svg[Class diagram after 3rd step]

Be careful, two details we should take care of:

-- +

image::AISClassDiagram4.svg[Final class diagram]

== Some remarks...

.settings.xml with profile for local JavaFX version [source,xml]

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

javafxlocal 17.0.2
<activeProfiles>
    <activeProfile>javafxlocal</activeProfile>
</activeProfiles>

The tag name is a self defined property, it could have been named differently. You can de-activate this so-called profile by commenting out the line.

In the pom where you're using JavaFX as dependency, configure it as below:

.snippet from pom.xml with variable JavaFX version [source,xml]

.....

UTF-8 19
<dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>${local-javafx-version}</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>${local-javafx-version}</version>
    </dependency>

.....

Above, the version in the JavaFX dependencies refer to ${local-javafx-version}. This variable is defined as property in the section. In this case, the version is resolved to 19. This is the default for all developers in the team. If you're using another version, e.g. 17.0.2, having the settings.xml file as described above, will override the version setting. So, settings.xml overrules the settings in the project pom. This concept has been applied in the pom.xml file in the GUI-project.