Modules
A module is a class annotated with @Module(). The decorator provides
metadata that Austial uses to organize the application structure.
from austial import Module
from .cats_controller import CatsController
from .cats_service import CatsService
@Module(
controllers=[CatsController],
providers=[CatsService],
)
class CatsModule:
pass| Property | Meaning |
|---|---|
imports | Other modules whose exported providers this module needs. |
controllers | Controllers defined in this module, instantiated by Austial. |
providers | Providers registered by this module, resolvable within it (and, currently, application-wide — see the note below). |
exports | The subset of providers (or re-exported imported tokens) other modules can use once they import this module. |
The root module
Every Austial app has at least one root module, conventionally
AppModule, which AustialFactory.create() starts scanning from:
from austial import Module
from austial.common.middleware import LoggingMiddleware, MiddlewareConsumer
from austial.config import ConfigModule
from .app_controller import AppController
from .app_service import AppService
from .modules.health.health_module import HealthModule
@Module(
imports=[ConfigModule.for_root(), HealthModule],
controllers=[AppController],
providers=[AppService],
)
class AppModule:
def configure(self, consumer: MiddlewareConsumer) -> None:
consumer.apply(LoggingMiddleware).for_routes("*")AustialFactory.create(AppModule) walks this imports graph recursively
(austial.core.module_scanner.scan_module), registering every module’s
controllers/providers into one shared Container, and de-duplicating
modules that are imported more than once.
Feature modules
Group a feature’s controller + service + DTOs + entity behind its own
module, then import it from AppModule — one folder per feature, which
is exactly what austial generate module <name> /
austial generate resource <name> scaffold automatically (see
CLI):
src/modules/cats/
├── cats_module.py
├── cats_controller.py
├── cats_service.py
├── entities/cats_entity.py
└── dto/
├── create_cats_dto.py
└── update_cats_dto.py@Module(imports=[CatsModule], controllers=[AppController], providers=[AppService])
class AppModule:
passGlobal modules
from austial import Global, Module
@Global()
@Module(providers=[ConfigService], exports=[ConfigService])
class ConfigModule:
pass@Global() marks a module’s exports as available everywhere without every
consumer needing to list it in their own imports (used internally by
ConfigModule.for_root()).
A note on module visibility
Austial’s DI container is currently application-wide and flat: every
module’s providers register into the same Container instance
regardless of imports/exports. In practice this means anything
registered anywhere in the graph is resolvable anywhere else — imports
and exports are honored for scanning (so you must still import a
module for its controllers/providers to be registered at all) but are not
yet enforced as a visibility boundary. Write your imports/exports
correctly anyway: it documents intent and means nothing changes for your
code if/when strict visibility lands.
Dynamic modules
ConfigModule.for_root(...) and DatabaseModule.for_root_async(...) are
dynamic modules — static methods that build and return module metadata
at runtime instead of a fixed @Module(...) decorator. See
Dynamic Modules for how to write your own.