Updated Project Architecture (markdown)

Aurora Lahtela 2022-05-15 19:10:27 +03:00
parent 115cb83d82
commit 83f7281bf7
1 changed files with 78 additions and 67 deletions

@ -6,78 +6,52 @@ This page attempts to help new developers start contributing to Plan.
It does not go over the build tool & commands for building as those are detailed in [[Project Setup]].
Page version: **5.1 build 904** [Commit](https://github.com/plan-player-analytics/Plan/tree/80cf3fcdf6d360c04147b524a4d8793cc5082639)
Page version: **5.4 build 1651** [Commit](https://github.com/plan-player-analytics/Plan/tree/8bbbde4556a1604771db0af68ff9fefa92e0fc93)
### Table of contents
- Java
- Modules & Dependencies between modules
- Dagger & Dependency injection
- Structure outline of `common`-module
- Abstraction layer for platforms
- Level 1: System overview
- Level 2: Module level overview
- Level 3: Package level overview
- Web dev
- Technology stack
- Template-like html
- Static Resources
- Level 1: Technology stack overview
- Level 2: Directory level overview
# Modules & Dependencies between modules
# Java
## Level 1: System overview
Plan runs as a plugin on top of various Minecraft server platforms. A lot of the platform specific code required to do this is abstracted away for easier development. Below is a system overview diagram.
[![System overview diagram](https://raw.githubusercontent.com/plan-player-analytics/drawio-diagrams-storage/master/C1-system-overview.drawio.svg)](https://app.diagrams.net/#Hplan-player-analytics%2Fdrawio-diagrams-storage%2Fmaster%2FC1-system-overview.drawio.svg)
# Level 2: Module level overview
Module | Role
-- | --
`api` | Contains code for [public API](https://github.com/plan-player-analytics/Plan/wiki/APIv5) interface classes
`extensions` | Contains built-in extensions that use DataExtension API (in public API)
`common` | Main package for everything
`bukkit`, `bungee`, `sponge`, `velocity`, `nukkit` | Platform specific modules
`bukkit`, `bungee`, `sponge`, `velocity`, `nukkit`, `fabric` | Platform specific modules
`plugin` | Creates a single jar out of the modules
Most of the time work is done in `common` module as platforms are abstracted away.
## Build order
`api` -> `extensions` -> `common` -> `bukkit`, `bungee`, `sponge`, `velocity`, `nukkit` -> `plugin`
- `api` -> `extensions` -> `common` -> `bukkit`, `bungee`, `sponge`, `velocity`, `nukkit` -> `plugin`
- `api` -> `extensions` -> `common` -> `fabric`
From the build order, the dependencies between the modules should be apparent.
# Dagger & Dependency Injection
## Level 3: Package level overview
Dagger (https://dagger.dev/) is used extensively around the project.
It was used as a remedy to reduce static usage. (*See decisions here [Refactor plugin to use static getters](https://github.com/plan-player-analytics/Plan/wiki/Architectural-Decision-Records#411---417-3rd-dec-2017---2nd-march-2018) and [Dependency Injection: Dagger](https://github.com/plan-player-analytics/Plan/wiki/Architectural-Decision-Records#450-11-august-2018---27-october-2018)*)
`@Singleton` annotations are used to tell Dagger that only a single instance should exist per initialized Component (So that each `PlanSystem` only has one `DatabaseSystem` instead of returning a new one every time the database is needed.)
`@Inject` annotations are used in Constructors to tell Dagger to resolve the dependencies for constructing a new instance of this class.
Most of the plugin uses Inject annotations, but some dagger related things can be found from `modules` package
Note that the Inject constructors allow Injecting an instance of the class the constructor is in to another object:
```
public class Foo {
@Inject
public Foo() {}
}
public class Bar {
@Inject
public Bar(Foo foo) {}
}
```
This is the main mechanism used for instantiating different system level objects, more about those below.
# Structure outline of `common`-module
## Lifetime
The process begins in a platform specific module by creating a new Dagger `Component` that resolves all dependencies for `PlanSystem`.
- When the plugin is enabled by the platform it is running on `PlanSystem#enable` is called, which will lead to all subsystems getting started in a specific order. This order is determined by dependencies between the subsystems. When parts of these subsystems are injected in any constructor, I've tried to keep the same order as they are enabled.
- When the platform is shutting down and the plugin is disabled `PlanSystem#disable` is called, which stops all extra threads and closes resources the plugin is using. Platform specific shutdown such as cancelling runnables happens in the plugin class of the platform.
- When the plugin is reloaded with `plan reload` command by user, first the plugin is disabled and then enabled. A new Dagger component is created in the process, so `PlanSystem` instance is new each time the process enables.
## Overview of structure
### Structure outline of `common`-module
![Overview](https://raw.githubusercontent.com/plan-player-analytics/Plan/master/Plan_architecture_overview.png)
## Package structure
#### Package structure
Below are *packages* that are in package `common/src/java/com.djrapitops.plan`
@ -122,7 +96,41 @@ Package | Role
`utilities.logging` | Error logging code
`version` | System that checks for new version
# Abstraction layer for platforms
### Dagger & Dependency Injection
Dagger (https://dagger.dev/) is used extensively around the project.
It was used as a remedy to reduce static usage. (*See decisions here [Refactor plugin to use static getters](https://github.com/plan-player-analytics/Plan/wiki/Architectural-Decision-Records#411---417-3rd-dec-2017---2nd-march-2018) and [Dependency Injection: Dagger](https://github.com/plan-player-analytics/Plan/wiki/Architectural-Decision-Records#450-11-august-2018---27-october-2018)*)
`@Singleton` annotations are used to tell Dagger that only a single instance should exist per initialized Component (So that each `PlanSystem` only has one `DatabaseSystem` instead of returning a new one every time the database is needed.)
`@Inject` annotations are used in Constructors to tell Dagger to resolve the dependencies for constructing a new instance of this class.
Most of the plugin uses Inject annotations, but some dagger related things can be found from `modules` package
Note that the Inject constructors allow Injecting an instance of the class the constructor is in to another object:
```
public class Foo {
@Inject
public Foo() {}
}
public class Bar {
@Inject
public Bar(Foo foo) {}
}
```
This is the main mechanism used for instantiating different system level objects, more about those below.
#### Lifetime
The process begins in a platform specific module by creating a new Dagger `Component` that resolves all dependencies for `PlanSystem`.
- When the plugin is enabled by the platform it is running on `PlanSystem#enable` is called, which will lead to all subsystems getting started in a specific order. This order is determined by dependencies between the subsystems. When parts of these subsystems are injected in any constructor, I've tried to keep the same order as they are enabled.
- When the platform is shutting down and the plugin is disabled `PlanSystem#disable` is called, which stops all extra threads and closes resources the plugin is using. Platform specific shutdown such as cancelling runnables happens in the plugin class of the platform.
- When the plugin is reloaded with `plan reload` command by user, first the plugin is disabled and then enabled. A new Dagger component is created in the process, so `PlanSystem` instance is new each time the process enables.
### Abstraction layer for platforms
Different server platforms offer more or less the same sort of functionality, but with a different API. This is why in Plan some of it is abstracted away with [Abstract Plugin Framework](https://github.com/AuroraLS3/Abstract-Plugin-Framework).
@ -133,42 +141,45 @@ This is mostly limited to Task scheduling and logging.
Other abstraction is achieved by using `PlanSystem` and Dagger to avoid the platform specific plugins doing too much work.
## Platform Modules
### Platform Modules
Platform modules include only the implementation for the specific platform so code from Bukkit is not available in `sponge` module and so on.
In the platform modules you can find implementations for Listeners and some tasks that require platform specific function calls.
Note that proxy implementations (Bungee, Velocity) have different gathering behaviors and deliver different pages - this is done with the listeners and the different execution paths are done in the `common` module by using `ServerInfo#isProxy` method.
# Web Technology Stack
# Web Dev
All web files can be found from `/src/main/resources/assets/plan/web`.
## Level 1: Technology stack overview
Plan uses the following stack to deliver the web content:
The Plan frontend is currently being rewritten in React, so the two web stacks exist in parallel.
- .html files inside the jar, that have `${placeholder}` replacement support
- Java Sun HTTPServer webserver and custom HTTP 1.1 request resolution
- Google GSON for serving data as JSON.
- XMLHTTPRequest for loading the JSON
- jQuery for placing the data on the pages
- Note: jQuery is slowly being removed from the plugin by replacing jQuery calls with modern javascript.
[![Web System overview diagram](https://raw.githubusercontent.com/plan-player-analytics/drawio-diagrams-storage/master/C1-web-tech-overview.drawio.svg)](https://app.diagrams.net/#Hplan-player-analytics%2Fdrawio-diagrams-storage%2Fmaster%2FC1-web-tech-overview.drawio.svg)
Web in general is moving away from using jQuery as the Javascript ecosystem has matured to a point where its functionality can be found in every browser. The decision to use it was made based on it being used by Bootstrap and jQuery data-tables.
## Level 2: Directory level overview
## Template like html
- Web files served by the webserver in the plugin live in `/Plan/common/src/main/resources/assets/plan/web`.
- The files are the old frontend stack.
- Gradle task copies `/Plan/react/dashboard/build` to `/Plan/common/build/resources/assets/plan/web` during build so the built bundle is available to the webserver in the same way.
- The files in `/Plan/react/dashboard` are the new frontend stack.
The html has some `${placeholder}` placed inside it, which is how 4.x.x versions delivered the data to the users.
New uses are discouraged if there is a different way to deliver the same information to the client.
### Export consideration
The placeholder replacement should be done in the `delivery.rendering.pages` package.
- Web files are written for static webserver deployments in `/Plan/common/src/main/java/com/djrapitops/plan/delivery/export` and new files added to old frontend stack should be reflected there.
The html [can be customized](https://github.com/plan-player-analytics/Plan/wiki/Html-Customization) by the users, which is made possible by specific methods in `PlanFiles` that find the files.
## Development
## Static resources
### Developing old frontend stack from IDE
Most of the javascript libraries used in the plugin are in `/src/main/resources/assets/plan/web/vendor`. Exceptions include jQuery and Font Awesome.
- Start Game/Proxy server with Plan webserver on port 8804
- Enable [web developer mode](https://github.com/plan-player-analytics/Plan/wiki/Html-Customization#web-developer-mode)
When introducing new resources into the .html files the resource paths should be added to the appropriate `Exporter` in `delivery.export` so that the files are available for all users.
### Developing new frontend stack from IDE
- Start Game/Proxy server with Plan webserver on port 8804
- Change proxy address to `http://localhost:8804` in `/Plan/react/dashboard/package.json`
- Go to `/Plan/react/dashboard/`
- run `yarn start`
----