Restructurer le code pour que chaque entité ait son propre dossier contenant tout son code (model, store, handlers). C’est une best practice pour les microservices en production.
microservice/
├── main.rs # Bootstrap
├── entities.rs # ❌ TOUTES les entités (56 lignes)
├── store.rs # Store centralisé
├── handlers.rs # ❌ TOUS les handlers (137 lignes)
└── module.rs # Configuration
Problèmes :
handlers.rs)microservice/
├── main.rs
├── module.rs
├── store.rs
└── entities/
├── mod.rs # Re-exports
├── order/ # ✅ Tout Order dans un dossier
│ ├── mod.rs # Exports Order
│ ├── model.rs # Structure Order (20 lignes)
│ ├── store.rs # OrderStore (35 lignes)
│ └── handlers.rs # Order handlers (55 lignes)
├── invoice/ # ✅ Tout Invoice dans un dossier
│ ├── mod.rs
│ ├── model.rs # Structure Invoice (18 lignes)
│ ├── store.rs # InvoiceStore (35 lignes)
│ └── handlers.rs # Invoice handlers (55 lignes)
└── payment/ # ✅ Tout Payment dans un dossier
├── mod.rs
├── model.rs # Structure Payment (19 lignes)
├── store.rs # PaymentStore (35 lignes)
└── handlers.rs # Payment handlers (57 lignes)
Avantages :
entities/order/order/
├── mod.rs # Module exports
│ pub mod handlers;
│ pub mod model;
│ pub mod store;
│ pub use model::Order;
│ pub use handlers::*;
│ pub use store::OrderStore;
│
├── model.rs # Structure de données PURE
│ #[derive(Debug, Clone, Serialize, Deserialize)]
│ pub struct Order {
│ pub id: Uuid,
│ pub tenant_id: Uuid,
│ pub number: String,
│ pub amount: f64,
│ pub status: String,
│ pub customer_name: Option<String>,
│ pub notes: Option<String>,
│ }
│
├── store.rs # Persistance
│ pub struct OrderStore {
│ data: Arc<RwLock<HashMap<Uuid, Order>>>,
│ }
│ impl OrderStore {
│ pub fn new() -> Self { ... }
│ pub fn add(&self, order: Order) { ... }
│ pub fn get(&self, id: &Uuid) -> Option<Order> { ... }
│ pub fn list(&self) -> Vec<Order> { ... }
│ }
│
└── handlers.rs # HTTP handlers
pub struct OrderAppState { ... }
pub async fn list_orders(...) -> Json<Value> { ... }
pub async fn get_order(...) -> Result<Json<Order>, StatusCode> { ... }
pub async fn create_order(...) -> Result<Json<Order>, StatusCode> { ... }
Pattern reproduit pour invoice/ et payment/ !
mkdir -p examples/microservice/entities/{order,invoice,payment}
| Fichier | Lignes | Rôle |
|---|---|---|
| entities/mod.rs | 8 | Re-exports globaux |
| order/mod.rs | 8 | Exports Order |
| order/model.rs | 20 | Structure Order |
| order/store.rs | 35 | OrderStore |
| order/handlers.rs | 55 | HTTP handlers Order |
| invoice/mod.rs | 8 | Exports Invoice |
| invoice/model.rs | 18 | Structure Invoice |
| invoice/store.rs | 35 | InvoiceStore |
| invoice/handlers.rs | 55 | HTTP handlers Invoice |
| payment/mod.rs | 8 | Exports Payment |
| payment/model.rs | 19 | Structure Payment |
| payment/store.rs | 35 | PaymentStore |
| payment/handlers.rs | 57 | HTTP handlers Payment |
Total : 13 fichiers, ~360 lignes (vs 2 fichiers, ~193 lignes avant)
examples/microservice/entities.rs (56 lignes monolithique)examples/microservice/handlers.rs (137 lignes monolithique)main.rs :
// Avant
mod entities;
mod handlers;
use entities::{Invoice, Order, Payment};
use handlers::*;
// Après
mod entities;
use entities::{
invoice::{create_invoice, get_invoice, list_invoices, InvoiceAppState},
order::{create_order, get_order, list_orders, OrderAppState},
payment::{create_payment, get_payment, list_payments, PaymentAppState},
Invoice, Order, Payment,
};
❓ Où est le code Order ?
✅ Tout dans entities/order/
❓ Où est le handler create_order ?
✅ entities/order/handlers.rs
❓ Où est la structure Order ?
✅ entities/order/model.rs
Ajouter une nouvelle entité = copier le dossier :
cp -r entities/order entities/product
# Renommer Order → Product
# C'est tout !
👨💻 Dev A travaille sur entities/order/
👩💻 Dev B travaille sur entities/invoice/
👨💻 Dev C travaille sur entities/payment/
→ Zéro conflit de merge !
// tests/order_tests.rs
use crate::entities::order::*;
// Tests uniquement pour Order
// tests/invoice_tests.rs
use crate::entities::invoice::*;
// Tests uniquement pour Invoice
Team Orders possède entities/order/
Team Billing possède entities/invoice/ et entities/payment/
→ Responsabilités claires !
Pour chaque nouvelle entité, créez cette structure :
entities/<entity_name>/
├── mod.rs # Exports
├── model.rs # Structure + derives
├── store.rs # Persistance
└── handlers.rs # HTTP handlers
//! <Entity> entity module
pub mod handlers;
pub mod model;
pub mod store;
pub use handlers::*;
pub use model::<Entity>;
pub use store::<Entity>Store;
//! <Entity> entity model
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct <Entity> {
// === Common fields ===
pub id: Uuid,
pub tenant_id: Uuid,
// === Standard fields ===
pub number: String,
pub amount: f64,
pub status: String,
// === Specific fields ===
pub custom_field: String,
}
//! <Entity> store implementation
use super::model::<Entity>;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use uuid::Uuid;
#[derive(Clone)]
pub struct <Entity>Store {
data: Arc<RwLock<HashMap<Uuid, <Entity>>>>,
}
impl <Entity>Store {
pub fn new() -> Self { ... }
pub fn add(&self, item: <Entity>) { ... }
pub fn get(&self, id: &Uuid) -> Option<<Entity>> { ... }
pub fn list(&self) -> Vec<<Entity>> { ... }
}
impl Default for <Entity>Store {
fn default() -> Self { Self::new() }
}
//! <Entity> HTTP handlers
use super::model::<Entity>;
use crate::store::EntityStore;
use axum::{extract::{Path, State}, http::StatusCode, response::Json};
use serde_json::{json, Value};
use uuid::Uuid;
#[derive(Clone)]
pub struct <Entity>AppState {
pub entity_store: EntityStore,
}
pub async fn list_<entity_plural>(State(state): State<<Entity>AppState>) -> Json<Value> {
let items = state.entity_store.list_<entity_plural>();
Json(json!({
"<entity_plural>": items,
"count": items.len()
}))
}
pub async fn get_<entity>(
State(state): State<<Entity>AppState>,
Path(id): Path<String>,
) -> Result<Json<<Entity>>, StatusCode> {
let id = Uuid::parse_str(&id).map_err(|_| StatusCode::BAD_REQUEST)?;
state.entity_store.get_<entity>(&id).map(Json).ok_or(StatusCode::NOT_FOUND)
}
pub async fn create_<entity>(
State(state): State<<Entity>AppState>,
Json(payload): Json<Value>,
) -> Result<Json<<Entity>>, StatusCode> {
// Implementation
}
mkdir -p entities/{order,invoice,payment}
Pour chaque entité, créer mod.rs, model.rs, store.rs, handlers.rs
# Extraire Order de entities.rs → order/model.rs
# Extraire OrderStore de store.rs → order/store.rs
# Extraire handlers Order de handlers.rs → order/handlers.rs
// main.rs
use entities::{
order::{list_orders, get_order, create_order, OrderAppState},
Order,
};
rm entities.rs handlers.rs
cargo build --example microservice
cargo run --example microservice
| Critère | Avant (Centralisé) | Après (Par Dossier) |
|---|---|---|
| Fichiers par entité | 0 (code mélangé) | 4 (isolés) |
| Lignes max/fichier | 137 lignes | ~60 lignes |
| Navigation | ❌ Difficile | ✅ Intuitive |
| Scalabilité | ❌ Limitée | ✅ Infinie |
| Conflits merge | ❌ Fréquents | ✅ Rares |
| Ownership | ❌ Flou | ✅ Clair |
| Tests isolés | ❌ Difficile | ✅ Facile |
| Production-ready | ❌ Non | ✅ Oui |
Code éparpillé dans 3 fichiers centraux
Difficile à naviguer
Pas scalable
Code isolé par entité
Navigation intuitive
Scalable à l'infini
Production-ready
# ✅ Structure claire
tree entities/
# → 3 dossiers, 13 fichiers
# ✅ Compilation
cargo build --example microservice
# → Success
# ✅ Code isolé
cat entities/order/model.rs
# → Uniquement Order, rien d'autre
Cette structure enseigne :
entities/
├── user/
├── company/
├── product/
├── order/
└── invoice/
entities/
├── customer/
├── order/
├── invoice/
├── payment/
├── shipment/
├── product/
├── inventory/
├── warehouse/
├── supplier/
├── ... (40+ autres)
La structure tient à l’échelle !
La restructuration par dossiers d’entités apporte :
✅ Organisation : Code clair et isolé
✅ Scalabilité : Pattern reproductible
✅ Maintenabilité : Facile à naviguer
✅ Collaboration : Pas de conflits
✅ Production : Architecture professionnelle
C’est LA best practice pour les microservices en production ! 🚀🦀✨
Date : 2025-10-22
Impact : Architecture production-ready
Status : ✅ Complété et testé