Módulos en Sword
En Sword, un módulo agrupa piezas relacionadas de una misma capacidad de la aplicación, como controladores, componentes, proveedores, DTOs e incluso otros módulos. Cada módulo implementa el trait Module y registra sus piezas en el contenedor de dependencias.
Trait Module
El contrato base es:
pub trait Module {
fn register_controllers(controllers: &ControllerRegistry) {}
fn register_components(components: &ComponentRegistry) {}
async fn register_providers(config: &Config, providers: &ProviderRegistry) {}
}Todos los métodos tienen implementación por defecto vacía.
¿Qué registra cada método?
register_controllers(...)
Registra puntos de entrada externos: HTTP, Socket.IO y otros tipos de estructuras que implementan ControllerSpec.
fn register_controllers(controllers: &ControllerRegistry) {
controllers.register::<UsersController>();
}register_components(...)
Registra estructuras #[injectable] que deben construirse desde el contenedor de dependencias.
fn register_components(components: &ComponentRegistry) {
components.register::<UserRepository>();
components.register::<UsersService>();
}register_providers(...)
Registra estructuras #[injectable(provider)], normalmente conexiones o clientes externos: base de datos, cache o servicios remotos. Este método es asíncrono por defecto, ya que la inicialización de recursos externos puede requerir operaciones async.
async fn register_providers(config: &Config, providers: &ProviderRegistry) {
let db_config = config.expect::<DatabaseConfig>();
providers.register(
Database::new(db_config)
.await
.expect("Failed to create Database provider"),
);
}Ejemplo de módulo
use sword::prelude::*;
pub struct UsersModule;
impl Module for UsersModule {
fn register_components(components: &ComponentRegistry) {
components.register::<UserRepository>();
components.register::<UsersService>();
}
fn register_controllers(controllers: &ControllerRegistry) {
controllers.register::<UsersController>();
}
}Registro en la aplicación
Los módulos se registran con with_module::<M>() en ApplicationBuilder.
#[sword::main]
async fn main() {
let app = Application::builder()
.with_module::<SharedModule>()
.with_module::<UsersModule>()
.build();
app.run().await;
}Separación de responsabilidades
Expone una interfaz externa.
Ejemplos: endpoint HTTP, namespace Socket.IO.Lógica interna autoconstruida por DI.
Ejemplos: servicio de dominio, repositorio, hasher.Recurso externo o inicialización async.
Ejemplos: base de datos, cliente Redis, SDK externo.Estructura habitual
Una estructura habitual es:
users/
controller.rs
service.rs
repository.rs
mod.rsY en mod.rs:
pub struct UsersModule;
impl Module for UsersModule {
// registro de los elementos del módulo
}
