# Essay 2: From Vision to Architecture
Architecture plays a key role in software systems around the world, it defines the way a system is structured. The architecture of libGDX is an interesting topic to study. In order to get to know more about the system behind this framework we discuss the important architectural aspects of the libGDX framework in this blog post.
First of all, we discuss the general architectural style. Next, is a description of the different architectural views. Furthermore, one can read about how libGDX functions as an API. Finally, we present a short review of the libGDX architecture.
## Architectural style
To reason about the architectural style of libGDX, it is important to first think about how this framework is used. As libGDX is only a development framework, it is imported by the developer that would like to create an application with the help of it. This means that libGDX does not offer a graphical user interface to the developer, but rather it is used as an abstract tool to develop applications. This means that for example a model-view-controller design pattern[^modelviewcontroller] would not be applicable to the framework.
The major design methodology of the libGDX project is domain-driven design. With the main focus of libGDX being to serve as a framework for developing video games, different components within the system are named after this. For example, there are distinct components associated with the sound or graphics of the game.
A second point that becomes clear when inspecting the codebase of the libGDX framework, is that files are categorized. The project defines multiple packages, each containing files that are created for one specific part of the framework. There are also packages that contain sub-packages. This packaging makes it very clear what each part of the codebase is for and makes the project look well-structured.
## Views
libGDX is packaged and deployed as a regular library. This fits well with its developer-centric approach, as developers are familiar with utilizing software in this manner. There are separate libraries for each supported platform. This way, a project does not have to include implementations for platforms it does not support itself[^starterclasses].
Since the Java ecosystem heavily features "jars", archives of compiled code and resources, the final product will likely be delivered as one as well[^jarfiles]. It is possible to create a "fat jar", a jar of the product that has been merged with the jars of its dependencies, such that shipping the product with all of its dependencies can be easily done using a single file. The Java runtime knows how to execute jars and therefore this one fat jar is sufficient to ship and run the final product.
<!-- Distribution is primarily done via Sonatype's public open-source software repositories, a de facto standard for publishing Java libraries. Many development environments have out-of-the-box support for this distribution platform. This also reduces the resources necessary to host the project and mitigates risks such as downtime and security breaches where someone could upload a malicious package. -->
<!-- Components view -->
<!-- Structural decomposition into components with explicit interfaces, and their inter-dependencies -->
In broad strokes, the framework is very loose, as it embraces the "only pay for what you use" concept and only delivers the bare necessities in the core project. Even backends, as mentioned earlier, are pick-and-choose. However, for most components there still is some form of interop. The backends are an obvious case where there is a standard interface, which we'll investigate first.
The main class of every libGDX project is an implementor of an `ApplicationListener`[^lifecycle]. For ease-of-use there also is an `ApplicationAdapter` that is simply an abstract class that provides defaults for every method of `ApplicationListener`[^listenersvsadapters]. Instances of this interface are then passed to the chosen backend's `Application` instance, which handles building the right environment for GDX applications and translates the platform's events into something the `ApplicationListener` can process.
{{<image file="application-uml.png">}}
<!--
@startuml
skinparam componentStyle uml2
skinparam componentBackgroundColor PeachPuff/PaleGoldenrod
skinparam databaseBackgroundColor PeachPuff/PaleGoldenrod
skinparam interfaceBackgroundColor PeachPuff/PaleGoldenrod
skinparam packageBackgroundColor Honeydew/FloralWhite
() ApplicationListener as applistener
package "Application" as app {
}
app -- applistener
package "Backends" as backends {
[Android] .u.> applistener : drives
[LWJGL3] .u.> applistener : drives
[RoboVM] .u.> applistener : drives
[etc...] .u.> applistener : drives
}
@enduml
-->
`ApplicationListener`s are one-way: signals from the backend travel to the application and there is no direct return path except by raising exceptions, at which point the application will halt. Instead, the application communicates with the backend via a set of global interfaces that are populated by the backend during startup[^modules]:
```java
public class Gdx {
public static Application app;
public static Graphics graphics;
public static Audio audio;
public static Input input;
public static Files files;
public static Net net;
public static GL20 gl;
public static GL20 gl20;
public static GL30 gl30;
}
```
If the application is running using the JWJGL3 backend and uses the graphics, audio and input services, the situation is as follows:
{{<image file="jwjgl-backend-uml.png">}}
<!--
@startuml
skinparam componentStyle uml2
skinparam componentBackgroundColor PeachPuff/PaleGoldenrod
skinparam databaseBackgroundColor PeachPuff/PaleGoldenrod
skinparam interfaceBackgroundColor PeachPuff/PaleGoldenrod
skinparam packageBackgroundColor Honeydew/FloralWhite
() ApplicationListener as applistener
() Graphics as graphics
() Audio as audio
() Input as input
() Files as files
() Net as net
package "Application" as app {
}
app -- applistener
app ..> graphics : uses
app ..> audio : uses
app ..> input : uses
package "LWJGL3 Backend" as backend {
[LwjglApplication] .u.> applistener : drives
[DefaultLwjglInput] -u- input
[OpenALLwlglAudio] -u- audio
[LwjglGraphics] -u- graphics
[LwjglNet] -u- net
[LwjglFiles] -u- files
}
@enduml
-->
Individual components of a backend are largely independent. Some components, such as audio, can even be disabled, and allow the developer to remove even more dependencies that are not always necessary.
Aside from backends, libGDX also provides utilities for dealing with typical game development concepts such as maps and scenes. These components will interact with the backend on behalf of the application to deal with common use cases. However, different from how other frameworks are designed, in libGDX it is not expected for the end user to use these tools. libGDX is designed around the user having full freedom and implement such features in their own way.
<!-- ### Connectors view ->
<!-- Main types of connectors used between components / containers. -->
<!-- *very* loose interpretation of "connector": -->
Because libGDX is integrated in the application, the main communication channel is via simple function calls across objects. This is important especially in code that handles graphics and audio, because most audiovisual operations require high speed and low latency messaging. In the case of graphics, operations are also limited to the main "graphics" thread and calls from other threads are likely to be rejected or cause errors. While the main thread could set up a message queue or a similar cross-thread messaging system, the overhead of this can easily dominate the time spent rendering.
As stated before, there is typically no communication between the different components of a backend. While it is not expressly forbidden, it is unlikely that a backend will require significant communication channels between its components since their domains are very dissimilar. Components outside of the backend will use the same interfaces as the application can.
### The application developer's perspective
<!-- System decomposition and dependencies, as enbodied in the source code -->
When creating an application using the libGDX platform, it is possible to create the application with the libGDX Project Generator[^projectgeneration]. This generator will create the file structure of a basic project that the application developer can build upon.
{{<image file="libgdx-project-setup.png">}}
It is possible to (de)select certain sub projects to fit your project's needs. Extensions that are maintained by libGDX can be selected as well as some third party extensions. By pressing "generate", a project will be created that can launch on all selected platforms.
The structure of the project will be as follows with each sub project as its own separate project:
{{<image file="example-project-structure.png">}}
The majority of the codebase should be inside the `core` folder; only platform-specific code should be placed inside the subproject folder for the given platform.
The `MyGdxGame` class is the core of the entire project. It extends the aforementioned `ApplicationListener` and houses basic application functionality. This class drives the render loop and listens for actions that listen to changes to the application state, such as resizing the window and pausing and resuming the game. There is also a `dispose` entrypoint that is activated when the application should exit, in order for it to clean up any resources that are not automatically managed by the Java platform, such as network connections.
<!--
- TODO: Add more about the interaction between components
-->
<!-- ### Run time view -->
When running the application, the render loop of the application will be called and should be used to activate other features that rely on time. Environments such as the display of the stage (Scene2d[^scene2d]) and the 2D physics environment (Box2d[^box2d]) should have their update event called inside the render loop.
During runtime an extensive number of additional listeners can be added to listen to specific inputs[^input]. These listeners/adapters can be implemented/overriden to apply essential code based on the calls made by the listeners/adapters. The adapters are essentially an implemented version of the listener. This allows the user to only override certain methods instead of requiring all the methods to be implemented.
Developers are free to create their own interaction between the components of libGDX. As explained before, libGDX's own components require little to no interaction between them. Your application can, however, allow for this interaction by implementing it yourself. There is a lot of freedom for the developers to design their run-time behavior; this is one of the strengths of libGDX.
<!-- - How do components interact at runtime to realize key scenarios?
- TODO: Write, but bit difficult as it depends on the created application, most parts are standalone and the interaction is designed by the developer itself... -->
## libGDX as an API
In our previous post we made extensive mention that libGDX is by itself a broad API[^previouspost]. Almost all of libGDX is a developer's interface with various components that it offers to aid the developer in shaping their application. Developers are not required to interface with the whole 'API' when creating their application. This is good, since libGDX is very broad in the features it provides. As discussed previously, not every developer will need every feature that libGDX provides. As an example, a developer might not need audio in their application and can choose not to interface with this system. They can even outright exclude it from their final (packaged) application.
<!--
Since the scale of libGDX is so broad we will not go too in-depth in this post, however ...
- TODO add something to close off this section OR shift things around.
-->
## Review
The architecture of libGDX is very much like that of a library. It is one broad system of components packaged as one API. It allows developers to 'pick and choose' what they need and is thus very flexible. This architecture is chosen to not limit developers when creating their application, while attaining a feature-rich library that can be interfaced with.
<!-- - How architecture realizes key quality attributes in the system
- How are potential trade-offs between these attributes resolved?
-->
<!-- ## Conclusion
- Most important aspects of the architectural style?
- Most important findings for the different views
- Bit about the key quality attributes and trade-offs
- API design principles in short. -->
## References
[^previouspost]: B. Dorland, M. P. H. Lips, L. Everse, C. Geukes. (2021) libGDX - Product Vision. Retrieved March 12, 2021, from https://2021.desosa.nl/projects/libgdx/posts/product-vision/
[^scene2d]: libGDX. (n. d.) Scene2d. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/Scene2d
[^box2d]: libGDX. (n. d.) Box2d. Retrieved March 15, 2021, from
https://github.com/libgdx/libgdx/wiki/Box2d
[^input]: libGDX. (n. d.) Input. Retrieved March 15, 2021, from
https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/Input.java
[^projectgeneration]: libGDX. (n. d.) Project Generation. Retrieved March 15, 2021, from
https://libgdx.com/dev/project-generation/
[^lifecycle]: libGDX. (n. d.) The life cycle. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/The-life-cycle
[^jarfiles]: oracle. (n. d.) JAR File Overview. Retrieved March 15, 2021, from
https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jarGuide.html
[^modules]: libGDX. (n. d.) Modules overview. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/Modules-overview
[^starterclasses]: libGDX. (n. d.) Starter classes and configuration. Retrieved March 15, 2021, from https://github.com/libgdx/libgdx/wiki/Starter-classes-and-configuration
[^listenersvsadapters]: J. Zukowski. (2007) Listeners vs Adapters. Retrieved March 15, 2021, from https://blogs.oracle.com/corejavatechtips/listeners-vs-adapters
[^modelviewcontroller]: Model View Controller. (n.d.). Wiki C2. Retrieved March 15, 2021, from https://wiki.c2.com/?ModelViewController