Monorepositório com Rust: organização de código na prática
\n\nAo desenvolver projetos grandes em Rust, mais cedo ou mais tarde surge a pergunta: como organizar o código para que ele permaneça sustentável, reutilizável e facilmente escalável? Uma das abordagens populares é o monorepositório. Neste artigo, vamos explorar o que é um monorepositório no contexto do Rust, como configurá-lo usando Cargo Workspaces e quais técnicas práticas ajudam a evitar o caos na base de código.
O material será útil tanto para iniciantes que estão começando a trabalhar com projetos grandes, quanto para desenvolvedores experientes em busca de maneiras eficientes de organizar o código.
\n\nO que é um monorepositório e por que ele é necessário no Rust?
\n\nMonorepositório (monorepo) é uma abordagem onde todo o código de um projeto (ou de vários projetos relacionados) é armazenado em um único repositório. Ao contrário dos multirrepositórios, onde cada serviço ou biblioteca vive em um repositório Git separado.
\n\nPor que os desenvolvedores Rust frequentemente escolhem o monorepositório?
\n- \n
- Versão única de dependências. Você evita conflitos quando diferentes crates exigem versões diferentes da mesma biblioteca. \n
- Refatoração simplificada. A alteração de uma API em um crate é imediatamente visível em todos os crates dependentes — o compilador Rust apontará todos os lugares que precisam ser corrigidos. \n
- Compilação conjunta. O Cargo pode otimizar a construção, reutilizando artefatos já compilados. \n
- Histórico único de alterações. Você não precisa sincronizar commits entre diferentes repositórios. \n
A principal ferramenta para organizar um monorepositório em Rust é o Cargo Workspaces. Vamos ver como usá-lo na prática.
\n\nConfiguração do Cargo Workspaces: exemplo passo a passo
\n\nSuponha que estamos criando uma aplicação composta por três partes:
\n- \n
- biblioteca
core_lib— lógica comum; \n - biblioteca
utils— utilitários; \n - arquivo executável
app— binário principal. \n
Vamos criar a estrutura do monorepositório:
\n\nmy_monorepo/\n├── Cargo.toml # manifesto raiz do workspace\n├── core_lib/\n│ ├── Cargo.toml\n│ └── src/\n│ └── lib.rs\n├── utils/\n│ ├── Cargo.toml\n│ └── src/\n│ └── lib.rs\n└── app/\n ├── Cargo.toml\n └── src/\n └── main.rs\n\n\nPasso 1. Cargo.toml raiz
[workspace]\nmembers = [\n "core_lib",\n "utils",\n "app",\n]\n\n\nAgora todos os três crates fazem parte de um único workspace. O Cargo os gerenciará como um todo.
\n\nPasso 2. Dependências internas
\nNo arquivo app/Cargo.toml, indicaremos que ele depende de nossas bibliotecas:
[package]\nname = "app"\nversion = "0.1.0"\nedition = "2021"\n\n[dependencies]\ncore_lib = { path = "../core_lib" }\nutils = { path = "../utils" }\n\n\nPasso 3. Escrevendo o código
\nExemplo de código em core_lib/src/lib.rs:
pub fn greet(name: &str) -> String {\n format!("Hello, {}!", name)\n}\n\n\nEm app/src/main.rs, usamos esta função:
use core_lib::greet;\n\nfn main() {\n println!("{}", greet("Rust Developer"));\n}\n\n\nPasso 4. Compilação e execução
\nOs comandos cargo build, cargo test e cargo run funcionam para todo o workspace. Também é possível executar um crate específico:
cargo run -p app\ncargo test -p core_lib\n\n\nEsta é a configuração básica. Agora vamos para práticas mais avançadas.
\n\nOrganização avançada: módulos, testes e versionamento
\n\nQuando o número de crates aumenta, é importante manter a disciplina. Aqui estão algumas dicas