Skip to main content
Version: Next

Modules

Modules, or container modules in ExpressoTS, are collections of services—primarily Controllers and their dependencies—managed by the framework's container. These modules help organize your application into logical domains, register components, and resolve Controllers efficiently.

Lifecycle events

ExpressoTS encourages the creation of controllers following the SOLID principles. In this approach, each controller is responsible for a specific action, such as creating, updating, or deleting a product. For example, within a Product Module, you might have separate controllers like CreateController, UpdateController, and DeleteController. Each of these controllers would then call their unique service or use case, which contains the actual implementation logic. This structure helps maintain a clean and organized codebase, where each controller has a single responsibility.

info

ExpressoTS provides the flexibility to choose the approach that best fits your project’s needs, whether it's adhering to SOLID principles for maximum maintainability or following the MVC pattern for simplicity and clarity.

Creating a module

To create a module using the CLI tool, run the following command:

expressots g mo module-name

This command will generate a new module with the following structure:

import { CreateModule } from "@expressots/core";

export const ProductModule = CreateModule([]);

Registering controllers

To register controllers within a module, pass them as an array to the CreateModule function. For example:

import { CreateModule } from "@expressots/core";
import { ProductController } from "./product.controller";

export const ProductModule = CreateModule([ProductController]);

Module scope

Very similar to the Container, developers can set the scope of a module using the same BindingScopeEnum enum. All controllers registered within the module will inherit the module's scope.

Here are the possible scope values:

  • BindingScopeEnum.Singleton - The dependency will be created once and will be shared across all requests.
  • BindingScopeEnum.Request - The dependency will be created once per request.
  • BindingScopeEnum.Transient - The dependency will be created every time it is requested.

When creating a module, you can pass the scope as a second argument to the CreateModule function. It is optional and if not provided, the default scope is acquired from the AppContainer which is RequestScope.

export const ProductModule: ContainerModule = CreateModule([], BindingScopeEnum.Singleton);

This flexibility allows for tailored dependency management strategies, such as combining Singleton scope modules for shared services with Request scope modules for request-specific services.

In the example above, all controllers registered within the ProductModule will have a Singleton scope.

Scope precedence

The hierarchy of scope precedence is: AppContainer > Module > Controller, ensuring a versatile and hierarchical approach to dependency scope management. This layered structure permits precise control over service instantiation and lifecycle across the entire application.

  1. AppContainer
  • Scope: Global
  • Role: The AppContainer is at the top of the hierarchy. It manages dependencies and services that need to be available throughout the entire application. Services registered here have the broadest scope and are shared across all modules and controllers.
  1. Module
  • Scope: Modular
  • Role: Within the AppContainer, each Module represents a distinct domain or feature in your application. Dependencies registered in a module are available to all controllers and services within that specific module, allowing for modular separation of concerns.
  1. Controller
  • Scope: Specific
  • Role: The most specific layer is the Controller. Dependencies registered at this level are only available within the individual controller. This scope is ideal for services or logic that are only relevant to a particular controller’s operations.
tip

To override controller scope within a module, you can use @scope() decorator.

@scope( ) decorator example
@scope("Singleton")
@controller("/")
export class ProductController {
@Get("/")
execute() {
return "ProductController";
}
}

Support us ❤️

ExpressoTS is an MIT-licensed open source project. It's an independent project with ongoing development made possible thanks to your support. If you'd like to help, please read our support guide.