Domain Driven Design en Rust
Posté le 04/12/2022 dans Rust
Domain Driven Design, quésaco ?
Le Domain Driven Design, communément appelé DDD, est un paradigme de développement qui met l'accent sur la compréhension approfondie du domaine dans lequel ton application sera utilisée.
Tu peux donc imaginer des modèles de données et des algorithmes qui reflètent précisément les concepts et les relations qui existent dans ton domaine d'application.
Cela te permet, en théorie, de créer des logiciels plus clairs et plus faciles à comprendre pour les personnes du projet non techniques.
L'accent est alors mis sur la collaboration avec les experts du domaine pour obtenir leur avis et leur feedback, et permet donc de parler le même langage.
DDD en Rust
Tu dois d'abord comprendre toute la partie métier de ton appli pour pouvoir définir le domaine. Cette phase d'analyse et de compréhension est la plus importante et peut t'aider à résoudre les problèmes de manière plus efficace pour maintenir et faire évoluer ton application.
Ensuite, tu peux utiliser des structures en Rust pour représenter les concepts du domaine, ainsi que des fonctions pour manipuler ces structures.
Exemple d'application de gestion de compte bancaire.
Voici un exemple de code en Rust qui utilise le DDD pour gérer un compte bancaire :
use chrono::{DateTime, Utc};
struct Account {
balance: u64,
transactions: Vec<Transaction>,
}
struct Transaction {
amount: u64,
date: DateTime<Utc>,
}
struct Client {
name: String,
accounts: Vec<Account>,
}
impl Account {
fn deposit(&mut self, amount: u64) {
self.balance += amount;
self.transactions.push(Transaction {
amount,
date: Utc::now(),
});
}
fn withdraw(&mut self, amount: u64) -> Result<(), &'static str> {
if self.balance >= amount {
self.balance -= amount;
self.transactions.push(Transaction {
amount,
date: Utc::now(),
});
Ok(())
} else {
Err("Insufficient funds")
}
}
}
fn main() {
let mut client = Client {
name: "John Doe".to_string(),
accounts: vec![Account {
balance: 0,
transactions: vec![],
}],
};
client.accounts[0].deposit(100);
client.accounts[0].withdraw(50).unwrap();
client.accounts[0].withdraw(50).unwrap();
}
Ce code utilise des structures pour représenter les concepts du domaine (compte bancaire, transaction et client), ainsi que des fonctions pour manipuler ces structures (dépôt, retrait, etc.).
Exemple plus avancé : un système de gestion de stock.
Voici un exemple de code Rust plus avancé qui utilise le DDD pour gérer un système de gestion de stock :
use chrono::{DateTime, Utc};
use std::collections::HashMap;
// Représente un produit dans le stock
#[derive(Clone, Debug)]
struct Product {
// Nom du produit
name: String,
// Quantité en stock
quantity: u32,
// Prix de vente
price: f32,
}
// Représente un client dans le système
#[derive(Clone, Debug)]
struct Customer {
// Nom du client
name: String,
// Adresse email du client
email: String,
}
// Représente une commande dans le système
#[derive(Clone, Debug)]
struct Order {
// Identifiant unique de la commande
id: u32,
// Produits commandés
products: Vec<Product>,
// Client qui a passé la commande
customer: Customer,
// Date de la commande
date: DateTime<Utc>,
}
// Représente un système de gestion de stock
#[derive(Debug)]
struct StockSystem {
// Produits en stock
products: HashMap<String, Product>,
// Clients enregistrés dans le système
customers: HashMap<String, Customer>,
// Commandes enregistrées dans le système
orders: Vec<Order>,
}
impl StockSystem {
// Ajoute un produit au stock
fn add_product(&mut self, product: Product) {
self.products.insert(product.name.clone(), product);
}
// Ajoute un client au système
fn add_customer(&mut self, customer: Customer) {
self.customers.insert(customer.email.clone(), customer);
}
// Passe une commande pour un client donné
fn place_order(&mut self, products: Vec<Product>, customer_email: &str) -> Result<Order, &'static str> {
// Vérifie si les produits demandés sont en stock
for product in &products {
let stock_product = self.products.get(&product.name);
if stock_product.is_none() || stock_product.unwrap().quantity < product.quantity {
return Err("Product out of stock");
}
}
// Vérifie si le client existe dans le système
let customer = self.customers.get(customer_email);
if customer.is_none() {
return Err("Customer not found");
} else {
// Réduit la quantité en stock pour les produits commandés
for product in &products {
let mut stock_product = self.products.get_mut(&product.name).unwrap();
stock_product.quantity -= product.quantity;
}
// Crée la commande
let order = Order {
id: self.orders.len() as u32 + 1,
products,
customer: customer.unwrap().clone(),
date: Utc::now(),
};
// Ajoute la commande au système
self.orders.push(order.clone());
Ok(order)
}
}
}
Pour utiliser ce code, tu peux créer une instance de la structure StockSystem et ajouter des produits, des clients et passer des commandes :
fn main() {
// Crée une instance du système de stock
let mut stock_system = StockSystem {
products: HashMap::new(),
customers: HashMap::new(),
orders: Vec::new(),
};
// Ajoute des produits au stock
stock_system.add_product(Product {
name: "Table".to_string(),
quantity: 10,
price: 100.0,
});
stock_system.add_product(Product {
name: "Chair".to_string(),
quantity: 20,
price: 50.0,
});
// Ajoute des clients au système
stock_system.add_customer(Customer {
name: "John Doe".to_string(),
email: "john.doe@example.com".to_string(),
});
stock_system.add_customer(Customer {
name: "Jane Doe".to_string(),
email: "jane.doe@example.com".to_string(),
});
// Passe une commande pour un client
let order = stock_system.place_order(vec![
Product {
name: "Table".to_string(),
quantity: 1,
price: 100.0,
},
Product {
name: "Chair".to_string(),
quantity: 2,
price: 50.0,
},
], "jane.doe@example.com").unwrap();
println!("Order placed: {:?}", order);
// Affiche les informations du système
println!("Stock system: {:?}", stock_system);
}
Avantages et défis
En utilisant le DDD dans tes projets, tu peux bénéficier de nombreux avantages, tels que :
- Des logiciels plus clairs et plus faciles à comprendre, ce qui peut améliorer la collaboration avec les autres développeurs et les experts du domaine.
- Des modèles de données et des algorithmes qui reflètent les concepts et les relations du domaine d'application, ce qui peut améliorer la qualité de ton code et la précision de tes résultats.
- Un code plus facile à maintenir et à évoluer, ce qui peut réduire les coûts de développement et accélérer les délais de mise sur le marché.
Cependant, le DDD peut également présenter des défis, tels que :
- La nécessité de comprendre en profondeur le domaine d'application, ce qui peut être difficile et prendre du temps pour les développeurs qui ne sont pas des experts du domaine.
- L'intégration du DDD dans un projet existant peut être complexe et perturbante pour le fonctionnement de l'application.
- La mise en place d'un processus de développement orienté domaine peut nécessiter des changements importants dans la façon dont les équipes de développement travaillent, ce qui peut être difficile à gérer.