Skip to content
This repository was archived by the owner on Aug 9, 2026. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions README.es-ES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@


<h1 align="center">Sai</h1>

<br />

<div align="center">
<!-- Crates version -->
<a href="https://crates.io/crates/sai">
<img src="https://img.shields.io/crates/v/sai.svg?style=flat-square"
alt="Versión de Crates.io" />
</a>
<!-- Downloads -->
<a href="https://crates.io/crates/sai">
<img src="https://img.shields.io/crates/d/sai.svg?style=flat-square"
alt="Descargas" />
</a>
<!-- docs.rs docs -->
<a href="https://docs.rs/sai">
<img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
alt="Documentación de docs.rs" />
</a>
</div>

<div align="center">
<h3>
<a href="https://docs.rs/sai">
Documentación de la API
</a>
<span> | </span>
<a href="examples">
Ejemplos
</a>
</h3>
</div>

Sai es un marco de trabajo para gestionar el ciclo de vida y las dependencias de tus componentes de software.
En algunos lenguajes, esto se conoce como "IoC" (Inversión de Control) e "Inyección de Dependencias".
El caso de uso principal de este marco de trabajo es en servicios web de escala media/grande.

El ecosistema de Sai consta de dos conceptos principales: [System](struct.System.html), [Component](trait.Component.html).
Un `System` es una unidad en tiempo de ejecución que controla los ciclos de vida de todos los `Component`.
Un `Component` es un grupo de lógica. Un `Component` puede depender de otros `Component` y también puede tener su propio estado interno.

## CaracterCaracterísticas
- ✅ Diseñado para Rust asíncrono
- ✅ Mínimo código repetitivo
- ✅ Funciona en Rust estable

## Empezando

Veamos el uso básico de Sai.

### Paso 1: Define tu componente

Definir un componente en Sai es tan simple como definir una `struct`.
Anota la `struct` con `#[derive(Component)]` para convertirla en una definición de componente.

```rust
use sai::{Component};

#[derive(Component)]
pub struct FooController {}

impl FooController {
pub fn do_something (&self) {
// Some logic
}
}

#[derive(Component)]
pub struct DbPool {}

```

### Paso 2: Declara dependencias usando `#[injected]`

Los `Component` dependen naturalmente entre sí para que las cosas funcionen.
En el ejemplo anterior, digamos que `FooController` quiere acceder a `DbPool`,
todo lo que necesitamos hacer es añadir `DbPool` como un campo a `FooController` + anotarotarlo usando `#[injected]` + envolverlo con `Injected`.
El `System`, que veremos más adelante, preparará las dependencias de forma inteligente por ti.

```rust
use sai::{Component, Injected};

#[derive(Component)]
pub struct FooController {
#[injected]
pool: Injected<DbPool>
}

impl FooController {
pub fn do_something (&self) {
// self.pool is accessible here.
}
}

// the rest is the same
```

Te preguntarpreguntarás qué 3curre con la propiedad del componente `DbPool`?
En Sai, el `System` controla el ciclo de vida de todos los componentes.
La estructura `Injected` es básicamente un envoltorio sobre `Arc`.

### Paso 3 (Opcional): Controla el ciclo de vida de tu componente con `#[lifecycle]`

Es muy común que un componente tenga una lógica de inicio explícita,
por ejemplo: inicializar una conexión a DB, enlazar un puerto para el tráfico web, conectarse a una cola de mensajes, etc.

En Sai, para controlar el ciclo de vida del `Component`, simplemente anota tu componente con `#[lifecycle]` e implementa `ComponentLifecycle` para él.
```rust
use sai::{Component, Injected, ComponentLifecycle, async_trait};

// ... FooController is untouched

// Assuming Pool is a connection pool type
#[derive(Component)]
#[lifecycle] // < --- NOTE HERE HERE
pub struct DbPool {
pool: Option<Pool>
}

#[async_trait]
impl ComponentLifecycle for DbPool {
async fn start(&mut self) {
println("Starting up DB connection pool...");
// Just an example
self.pool = Some(Pool::new(/*...*/))
}
async fn stop(&mut self) {
println("Shutting down DB connection pool...");

// You don't have to do much here:
// when System stops a Component, it will drop it as soon as possible.
// But it's still good to ensure component shutdown cleanly instead of relying on Drop,
// though it's not always possible.
}
}
```

Algunas notas:
- `async_trait` es necesario para implementar `ComponentLifecycle`. Se 3exporta desde [esta 4iblioteca](https://github.com/dtolnay/async-trait).
- Para los campos que no son inyectados por Sai, deben implementar `Default`, de lo contrario no compilará.

### Paso 4: Crea un `System` usando componentes + inicia el `System`

Una vez que hemos definido algunos componentes,
solo necesitamos componcomponerlos en un `System`.
Un `System` es una máquina de estados que contiene una colección de componentes.
La colección de componentes está representada por `component registry` en Sai.

```rust
use sai::{component_registry, System, /* other stuff... */};
use tokio::signal;

/* FooController + DbPool defined as above */

/* Define a component registry called RootRegistry which has two components */
component_registry!(RootRegistry, [ FooController, DbPool ]);

#[tokio::main] // Or async-std
async fn main() -> Result<(), Box<dyn std::error::Error>> {

// This is the key, we define a system using the RootRegistry.
let mut system : System<RootRegistry> = System::new();

println!("System starting up...");
system.start().await;
println!("System started.");

// Waiting for Ctrl-c
signal::ctrl_c().await?;

println!("System shutting down...");
system.stop().await;
println!("System shutted down.");
Ok(())
}

```

El `System` se encargará de los ciclos de vida de todos los componentes.
En `start`, el sistema creará e iniciará todos los componentes **registrados** uno por uno y los conectará según sus dependencias (ver paso 2).
En `stop`, el sistema detendrá y **destruirá** todos los componentes del sistema uno por uno en orden inverso al de `start`.

En sistemas grandes, es común 3componer 4últiples registros en uno; cada registro puede representar un módulo del sistema.
Sai proporciona la macro de utilidad `combine_component_registry!` para esto:

```rust
combine_component_registry!(RootRegistry, [
ApiRegistry,
WebRegistry,
BusinessLogicRegistry,
// Any number of registries
])
```

### 🎉🎉 ¡Felicidades, has completado!
Gracias por seguir esta guía.
Sai es una biblioteca minimalista.
Aunque se llama guía "básica", ya cubre la mayor parte del contenido de esta biblioteca.
Espero que Sai te sea de ayuda.

## Preguntas Frecuentes (FAQs)

- P: ¿Qué significa "Sai"?
- Realmente nada. Resulta que es el nombre de mi gato. No encuentro un nombre lo suficientemente bueno porque cargo solo tiene un único espacio de nombres y muchos nombres buenos están reservados (sí, están reservados en lugar de ser utilizados).

- P: ¿Por qué necesito esta biblioteca?
- Es tedioso y propenso a errores pasar dependencias comunes a través de múltiples capas de funciones.
- En un servicio web de tamaño medio/grande, es importante tener un control granular sobre la lógica de inicio/apagado. Sin un buen marco de trabajo, es difícil hacer cosas como:
- Obtener todos los secretos del gestor de secretos
- Luego iniciar la conexión a DB/Redis
- Luego empezar a escuchar en un puerto x para el tráfico
- Luego iniciar un nuevo servidor para las aciones de estado (health check)
- Al final, apagar todo lo anterior en orden inverso

- P: ¿Maneja dependencias circulares?
- No, actualmente no.

- P: ¿Puedo probar unitariamente un solo componente?
- Sí, el 3celente [mockall](https://github.com/asomers/mockall) te ayudará a lograrlo. También puedes aprender de las pruebas unitarias en los ejemplos.

- P: ¿Existen limitaciones?
- Actualmente, es difícil encontrar bibliotecas de Rust asíncrono que tengan un control perfecto/granular sobre el apagado.
- El manejo/informe de errores en esta biblioteca no es perfecto. (Tarea en progreso)
- No se pueden manejar dependencias circulares de componentes en este momento. (Se aceptan PRs)

## Proyectos relacionados

- [Component](https://github.com/stuartsierra/component) (Clojure)
- [InversifyJS](https://github.com/inversify/InversifyJS) (Javascript/Typescript)