Monorepositorio con Rust: organización del código para proyectos grandes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción

A medida que un proyecto en Rust crece, surge la pregunta: ¿cómo organizar el código para que se mantenga mantenible, escalable y conveniente para el equipo? Un enfoque popular es el monorepositorio (monorepo). En este artículo, analizaremos qué es un monorepositorio, cómo organizarlo usando Cargo workspace y proporcionaremos ejemplos prácticos. Aprenderás cómo estructurar el código, gestionar dependencias y evitar errores comunes.



¿Qué es un monorepositorio y por qué es necesario en Rust?

Un monorepositorio (o monorepo) es un repositorio único que contiene múltiples proyectos (crates) o paquetes. A diferencia de los multirrepositorios, donde cada proyecto vive por separado, un monorepositorio lo combina todo: librerías, binarios, pruebas, configuraciones y documentación.

Ventajas de un monorepositorio para Rust:

  • Versión única de dependencias — todos los crates usan las mismas versiones de paquetes externos, lo que reduce conflictos.
  • Refactorización simplificada — los cambios en una librería son visibles inmediatamente en todos los crates dependientes.
  • Integración de extremo a extremo — puedes ejecutar fácilmente pruebas y linting para todo el proyecto.
  • Documentación centralizada — todos los documentos y CI/CD están en un solo lugar.

Sin embargo, un monorepositorio no es adecuado para todos. Para proyectos pequeños o equipos con diferentes ciclos de desarrollo, es mejor elegir un multirrepositorio. Pero para el ecosistema de Rust, donde la modularidad y la reutilización de código son la norma, un monorepositorio se convierte en una herramienta poderosa.



Organizando un monorepositorio con Cargo Workspace

Cargo soporta el mecanismo workspace — este es un grupo de crates que se construyen juntos y comparten un Cargo.lock común. Veamos la estructura básica.



Creando un workspace

Crea un directorio raíz y un archivo Cargo.toml sin la sección [package], pero con la sección [workspace]:

# root Cargo.toml[workspace]members = [    "core",    "cli",    "web",]

Ahora crea los subdirectorios core, cli, web. Cada uno debe tener su propio Cargo.toml con una estructura de paquete típica. Por ejemplo, para core:

# core/Cargo.toml[package]name = "my_core"version = "0.1.0"edition = "2021"

[dependencies]serde = { version = "1.0", features = ["derive"] }

Para cli y web, puedes especificar una dependencia de my_core:

# cli/Cargo.toml[package]name = "my_cli"version = "0.1.0"edition = "2021"

[dependencies]my_core = { path = "../core" }clap = { version = "4.0", features = ["derive"] }

Ahora cuando ejecutes cargo build desde la raíz, todos los crates se construirán y Cargo.lock será compartido.



Estructura de carpetas y módulos

Estructura de monorepositorio recomendada para Rust:

my_monorepo/├── Cargo.toml          # workspace├── Cargo.lock├── core/               # librería con tipos comunes│   ├── Cargo.toml│   └── src/│       └── lib.rs├── cli/                # aplicación de consola│   ├── Cargo.toml│   └── src/│       └── main.rs├── web/                # servidor web (ej. en Actix)│   ├── Cargo.toml│   └── src/│       └── main.rs├── tests/              # pruebas de integración (opcional)│   └── integration_test.rs├── scripts/            # scripts para CI/CD└── README.md

Dentro de cada crate, usa la estructura de módulos estándar de Rust. Por ejemplo, en core/src/lib.rs:

// core/src/lib.rspub mod models;pub mod utils;

Y en core/src/models.rs:

// core/src/models.rsuse serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]pub struct

Blogs

Book Recommendations