From 17943bffc2ab85192d93df535ae8c02e6533297c Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 31 Oct 2021 15:01:10 +0100 Subject: [PATCH] added readme.md --- readme.md | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 readme.md diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..90c8f61 --- /dev/null +++ b/readme.md @@ -0,0 +1,145 @@ +# Peridot + +## Terminology + +| Term | Description | +|--------------------|-------------------------------------------------------------------| +| Module (Container) | Bundle of providers, consumers, and imports to provide injections | +| Provider | Injectable element to provide functionality | +| Consumer | Element that can only get injections but cannot be injected | + +## Functionality + +This library is completely built around modules/containers. A module is responsible for managing instances of providers, +consumers and for injecting dependencies into these. Modules can also import other modules to fulfill the injection +requests of their consumers. Consumers and providers are isolated from other modules, but a module can export providers +to make them available for other modules. Modules only get loaded when required by others. + +There's no big difference between consumers and providers, only that consumers cannot be exported or injected. There's +only one single instance of each consumer and provider during the whole runtime of the application. Future plans include +scopes so that this behavior could be customized. + +The following steps will be done while booting the application: + +1. Decorators will be resolved and metadata fills up. +2. The root module will boot all linked modules. +3. Each module will create a single instance of each consumer and provider. +4. Each module then will inject all dependencies into the consumer and provider. +5. The module will call `onModuleInit()` to signal that injected elements are ready to use. + +Now you may ask "so I cannot use injected elements in the constructor?" - and you are right. You can't. First of all, +because I was too lazy to implement this, and handling circular dependencies this way is less pain. To execute code +after the module is ready you can use the `onModuleInit()` function from the `OnModuleInit` interface. This will ensure +that all required injections are done and safe to use. + +## Simple examples + +A complete example can be found in the example folder of this repo. + +We will create a simple app with one module, one provider and one consumer: + +```ts +import { Module, createApplication, OnModuleInit, Inject } from "./mod.ts" + +// Creates a simple provider with an add function +class SimpleProvider { + public add(a: number, b: number): number { + return a + b; + } +} + +// Creates a simple consumer that utilizes the add function from our simple provider +class SimpleConsumer implements OnModuleInit { + // SimpleProvider should be injected here + @Inject(SimpleProvider) + simpleProvider!: SimpleProvider; + + onModuleInit(): void { + // After module was initialized, use the provider to add two numbers + console.log("Service calculate 2 + 3 = " + this.simpleProvider.add(2, 3)); + } +} + +@Module({ + // Tell the module that it has one provider and one consumer that it should satisfy + providers: [SimpleProvider], + consumers: [SimpleConsumer], +}) +class SimpleModule { +} + +// Boot application +createApplication(SimpleModule); +``` + +In the next example, we will import service from another module + +```ts +import { Module, createApplication, OnModuleInit, Inject } from "./mod.ts" + +// Creates a simple math provider +class MathProvider { + public add(a: number, b: number): number { + return a + b; + } + + public multiply(a: number, b: number): number { + return a * b; + } +} + +@Module({ + providers: [MathProvider], + // This line is important + // It tells the module that it can serve the MathProvider for other modules to use + exports: [MathProvider], +}) +class MathModule { +} + +class SimpleConsumer implements OnModuleInit { + // Utilizes MathProvider from MathModule + @Inject(MathProvider) + mathProvider!: MathProvider; + + onModuleInit(): void { + console.log("Service calculate 2 + 3 = " + this.mathProvider.add(2, 3)); + console.log("Service calculate 2 * 3 = " + this.mathProvider.multiply(2, 3)); + } +} + +@Module({ + consumers: [SimpleConsumer], + // Imports MathModule so that this module can inject MathProvider to its consumers + imports: [MathModule], +}) +class SimpleModule { +} + +createApplication(SimpleModule); +``` + +> More examples will be added in the future + +## Circular dependencies + +To resolve circular dependencies we have to trick the typescript compiler a little bit by +passing `forwardRef(() => ProviderClass)` as parameter for `Inject()`. `forwardRef()` can also be used to resolve +circular module dependencies by passing `forwardRef(() => ModuleClass)` as parameter into the imports. Note, you need to +use `forwardRef()` on both sides. + +> Example will be added in the future + +## Logging + +You can change the log level by setting the "LOG_LEVEL" environment variable. Valid values are: + +- OFF +- CRITICAL +- ERROR +- WARNING +- INFO +- DEBUG + +The logger's default is INFO. Logging can slow down your application. When deploying your app set it to CRITICAL or +ERROR.