2.1 Project structure - Baumgaer/Game GitHub Wiki

This Project includes a fullstack framework which means that this framework acts like popular frameworks like Angular, React or Vue but not only for frontend. It includes the backend and to achieve this behavior, a special folder structure is needed or at least recommended to use.

Furthermore this Framework and the rest of the project is designed to be as type save as possible, so try to avoid the any type if possible. It is not a shame to use this type but we are planning to use AssemblyScript which does not allow the any type.

2.1.1 Folder structure

The project has several areas where to put in certain types of files. There is the out folder which contains all compiled files or files that are ready to use.

The source folder contains all files that has to be compiled to execute on server or client or those files which must be optimized.

The var folder contains variable content like uploaded stuff from users, logs, database files or similar things.

There are also several shortcuts for imports in typescript:

  • ~project

    points to the projects root / where you find node_modules, var, out and source.

  • ~root

    points to the source folder source.

  • ~bdo

    points to the source/app folder. This area contains source code which is used in server and client. You also should avoid to import something from client or server inside a file of this area because then the code can't be used in both, server and client.

  • ~server

    points to the source/app/server folder. This area contains source code which can only be executed on server side. Avoid to import things from client because client can have a functionality that is not executable on server like the window or document object.

  • ~static

    points to the source/app/client folder which contains essentially static files for the client. It is allowed to import this stuff into a file of server because there is no logic inside except the config.

  • ~client points to source/app/client/ts and contains the logic for the client. Avoid to import things from server!

See the following illustration (Figure 1) for more explanation.

Folder structure

Figure 1: The folder structure of the project with source, bdo, static, client, server and project folder and marker for file system junctions.

2.1.2 Shared code inheritance

Some objects in this project are using shared code (BDO) which is the reason why we need a special way to extend new classes. Figure 2 shows a general approach how to extend mixed areas of the project, which is also explained by Example 1. Every layer of extension or inheritance in a BDO section should be a Factory function which mixes an abstract class with a given class and returns this composition. The base of every object of this type should be an abstract bdo class. The initialization layer of the inheritance should contain the name of the area where it is inside for example "ClientRoute" or "ServerModel" to avoid confusion (only for files in folder "lib").

Structure of inheritance

Figure 2: Structure of inheritance layers for merging of BDO section and client / server section.

// in folder <type here> on client side
// This is the initialization layer

import {BDOMyClassFactory} from "~bdo/aFolder/BDOMyClass"
import {MyParentClass} from "~client/aFolder/MyParentClass"

export class MyClass extends BDOMyClassFactory(MyParentClass) {
    // Some stuff here
}

// in folder <type here> on BDO side
// NOTE the BDO in the name of the class and factory
// This is the initialization layer
export function BDOMyClassFactory<TBase extends ReturnType<typeof BDOMyParentClassFactory>>(ctor: TBase) {
    abstract class BDOTest1 extends ctor {
        // Some stuff here
    }
    return BDOTest1;
}

// in folder <type here> on client side
// This is the first layer of inheritance
import {BDOMyParentClassFactory} from "~bdo/aFolder/BDOMyParent"
import {ClientClass} from "~client/lib/ClientClass"

export class MyParentClass extends BDOMyParentClassFactory(ClientClass) {
    // Some stuff here
}

// in folder <type here> on BDO side
// NOTE the BDO in the name of the class and factory
// This is the first layer of inheritance
export function BDOMyParentClassFactory<TBase extends Constructor<BDOModel>>(ctor: TBase) {
    abstract class BDOMyParentClass extends ctor {
        // Some stuff here
    }
    return BDOTest1;
}

// in folder lib on client side
// NOTE the client in the name of the class
// This is the base layer
import {BDOClass} from "~bdo/lib/BDOClass"

export class ClientClass extends BDOClass {
    // Some stuff here
}

// in folder lib on BDO side
// NOTE the BDO in the name of the class
// This is the base layer
export abstract class BDOClass {
    // Some stuff here
}

// Somewhere on client side
const myClass = new MyClass();

Example 1: Explanation of a general class, which is contained in aFolder in all sections, that is build as a BDO and Client class.

A BDO should always be an abstract class to avoid unwanted initialization with a few exceptions. If the BDO HAS TO be used in another BDO, than it shouldn't be an abstract class. For example the BDO lib object "Attribute", "Property" and "Watched" are used in this way. They are only used in another BDO file.

2.1.3 Class initialization

The most classes will be a baseConstructor (will be discussed in Chapter 2.2) which allows the assignment of an object to the class fields. That means that public class fields will be the arguments which takes a class instead of giving the constructor an ton of parameters.

Example 2 shows the different possibilities of class fields. You will see that there are 3 possibilities for public class fields which are used as parameters in the constructor params: ConstParams<MyClass>.

If you want a parameter which MUST be assigned to a class, use the ! and do not assign a value to it.

If you want a parameter which is not required and doesn't have a default value, use the ? and don't assign a default value.

Class fields which have a default value are automatically optional.

protected and private class fields are not assignable from external.

import {baseConstructor} from "~bdo/utils/decorators";

@baseConstructor()
export myClass {

  public requiredField!: string;

  public optionalFieldWithNoDefaultValue?: number;

  public optionalFieldWithDefaultValue: number[] = [1, 2, 3];

  protected noneExternalAssignableClassField: string = "hello";

  private noneExternalAssignableClassField2: boolean = true;

  constructor(params: ConstParams<myClass>) {
    // ...
  }
}

Example 2: possibilities of class fields which are assignable.

This has the benefit that it is not necessary to write or assign type twice. You just define your class fields and use them as parameters. This also has the benefit of better readability.

⚠️ **GitHub.com Fallback** ⚠️