Monorepositório com Rust: organização de código para grandes projetos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução

À medida que um projeto Rust cresce, surge a pergunta: como organizar o código para que ele permaneça sustentável, escalável e conveniente para a equipe? Uma abordagem popular é o monorrepositório (monorepo). Neste artigo, vamos detalhar o que é um monorrepositório, como organizá-lo usando Cargo workspace e fornecer exemplos práticos. Você aprenderá como estruturar código, gerenciar dependências e evitar erros comuns.



O que é um monorrepositório e por que ele é necessário no Rust?

Um monorrepositório (ou monorepo) é um único repositório que contém vários projetos (crates) ou pacotes. Ao contrário dos multirrepositórios, onde cada projeto vive separadamente, um monorrepositório combina tudo: bibliotecas, binários, testes, configurações e documentação.

Vantagens de um monorrepositório para Rust:

  • Versão única de dependências — todos os crates usam as mesmas versões de pacotes externos, o que reduz conflitos.
  • Refatoração simplificada — alterações em uma biblioteca são imediatamente visíveis em todos os crates dependentes.
  • Integração de ponta a ponta — você pode executar facilmente testes e linting para todo o projeto.
  • Documentação centralizada — todos os documentos e CI/CD estão em um só lugar.

No entanto, um monorrepositório não é adequado para todos. Para projetos pequenos ou equipes com ciclos de desenvolvimento diferentes, é melhor escolher um multirrepositório. Mas para o ecossistema Rust, onde modularidade e reutilização de código são a norma, um monorrepositório se torna uma ferramenta poderosa.



Organizando um monorrepositório com Cargo Workspace

O Cargo suporta o mecanismo workspace — este é um grupo de crates que são compilados juntos e compartilham um Cargo.lock comum. Vamos analisar a estrutura básica.



Criando um workspace

Crie um diretório raiz e um arquivo Cargo.toml sem a seção [package], mas com a seção [workspace]:

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

Agora crie os subdiretórios core, cli, web. Cada um deles deve ter seu próprio Cargo.toml com uma estrutura de pacote típica. Por exemplo, para core:

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

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

Para cli e web, você pode especificar uma dependência em 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"] }

Agora, ao executar cargo build a partir da raiz, todos os crates serão compilados e o Cargo.lock será compartilhado.



Estrutura de pastas e módulos

Estrutura de monorrepositório recomendada para Rust:

my_monorepo/├── Cargo.toml          # workspace├── Cargo.lock├── core/               # biblioteca com tipos comuns│   ├── Cargo.toml│   └── src/│       └── lib.rs├── cli/                # aplicação de console│   ├── Cargo.toml│   └── src/│       └── main.rs├── web/                # servidor web (ex.: no Actix)│   ├── Cargo.toml│   └── src/│       └── main.rs├── tests/              # testes de integração (opcional)│   └── integration_test.rs├── scripts/            # scripts para CI/CD└── README.md

Dentro de cada crate, use a estrutura de módulos padrão do Rust. Por exemplo, em core/src/lib.rs:

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

E em core/src/models.rs:

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

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

Blogs

Book Recommendations