[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-22-injection-dependances-rust-service-locator":3},{"article":4,"author":51},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":16,"meta_description":17,"focus_keyword":18,"og_image":19,"canonical_url":19,"robots_meta":20,"created_at":15,"updated_at":15,"tags":21,"category_name":31,"related_articles":32},"d6000000-0000-0000-0000-000000000122","a0000000-0000-0000-0000-000000000066","Deep EVM #22 : Injection de dépendances en Rust — ServiceLocator, Arc et objets trait","deep-evm-22-injection-dependances-rust-service-locator","Implémentez l'injection de dépendances en Rust sans framework. Couvre le pattern composition root, Arc\u003Cdyn Trait> vs génériques, implémentations mock pour les tests et le pattern ServiceLocator.","## Le principe d'injection de dépendances\n\nL'injection de dépendances est un principe de conception fondamental : un composant doit recevoir ses dépendances de l'extérieur plutôt que de les créer en interne. Rust n'a pas de framework DI standard, et son modèle de propriété rend les patterns DI naïfs maladroits.\n\n## Traits comme interfaces\n\nEn Rust, les traits sont l'équivalent des interfaces :\n\n```rust\n#[async_trait]\ntrait Database: Send + Sync {\n    async fn get_user(&self, id: UserId) -> Result\u003CUser>;\n    async fn save_user(&self, user: &User) -> Result\u003C()>;\n}\n\n#[async_trait]\ntrait Cache: Send + Sync {\n    async fn get(&self, key: &str) -> Option\u003CVec\u003Cu8>>;\n    async fn set(&self, key: &str, value: &[u8], ttl: Duration) -> Result\u003C()>;\n}\n```\n\n## Arc\u003Cdyn Trait> vs génériques\n\n### Dispatch dynamique avec Arc\u003Cdyn Trait>\n\n```rust\nstruct UserService {\n    db: Arc\u003Cdyn Database>,\n    cache: Arc\u003Cdyn Cache>,\n}\n\nimpl UserService {\n    fn new(db: Arc\u003Cdyn Database>, cache: Arc\u003Cdyn Cache>) -> Self {\n        Self { db, cache }\n    }\n}\n```\n\nAvantages : flexibilité, échange facile des implémentations, pas de monomorphisation.\nInconvénients : indirection de pointeur, pas d'inlining.\n\n### Dispatch statique avec génériques\n\n```rust\nstruct UserService\u003CD: Database, C: Cache> {\n    db: D,\n    cache: C,\n}\n```\n\nAvantages : inlining, pas d'allocation heap, performances optimales.\nInconvénients : code plus verbeux, monomorphisation (plus gros binaire).\n\nRègle pratique : utilisez `Arc\u003Cdyn Trait>` pour les frontières de service et les génériques pour le code critique en performance.\n\n## Le pattern Composition Root\n\nConstruisez le graphe de dépendances au point d'entrée de l'application :\n\n```rust\n#[tokio::main]\nasync fn main() -> Result\u003C()> {\n    \u002F\u002F Composition root — toutes les dépendances sont créées ici\n    let pool = PgPool::connect(&database_url).await?;\n    let redis = RedisClient::connect(&redis_url).await?;\n\n    let db: Arc\u003Cdyn Database> = Arc::new(PostgresDb::new(pool));\n    let cache: Arc\u003Cdyn Cache> = Arc::new(RedisCache::new(redis));\n\n    let user_service = Arc::new(UserService::new(db.clone(), cache.clone()));\n    let auth_service = Arc::new(AuthService::new(db.clone()));\n\n    let app = Router::new()\n        .route(\"\u002Fusers\", get(list_users))\n        .with_state(AppState { user_service, auth_service });\n\n    axum::serve(listener, app).await?;\n    Ok(())\n}\n```\n\n## Mocks pour les tests\n\n```rust\nstruct MockDatabase {\n    users: Mutex\u003CHashMap\u003CUserId, User>>,\n}\n\n#[async_trait]\nimpl Database for MockDatabase {\n    async fn get_user(&self, id: UserId) -> Result\u003CUser> {\n        self.users.lock().await\n            .get(&id)\n            .cloned()\n            .ok_or(Error::NotFound)\n    }\n\n    async fn save_user(&self, user: &User) -> Result\u003C()> {\n        self.users.lock().await.insert(user.id, user.clone());\n        Ok(())\n    }\n}\n\n#[tokio::test]\nasync fn test_user_creation() {\n    let db: Arc\u003Cdyn Database> = Arc::new(MockDatabase::default());\n    let cache: Arc\u003Cdyn Cache> = Arc::new(MockCache::default());\n    let service = UserService::new(db, cache);\n\n    let user = service.create_user(\"Alice\").await.unwrap();\n    assert_eq!(user.name, \"Alice\");\n}\n```\n\n## Le pattern ServiceLocator\n\nPour les systèmes complexes avec de nombreuses dépendances :\n\n```rust\nstruct ServiceLocator {\n    services: HashMap\u003CTypeId, Box\u003Cdyn Any + Send + Sync>>,\n}\n\nimpl ServiceLocator {\n    fn register\u003CT: Send + Sync + 'static>(&mut self, service: T) {\n        self.services.insert(TypeId::of::\u003CT>(), Box::new(service));\n    }\n\n    fn resolve\u003CT: Send + Sync + 'static>(&self) -> Option\u003C&T> {\n        self.services.get(&TypeId::of::\u003CT>())\n            .and_then(|s| s.downcast_ref())\n    }\n}\n```\n\n## Conclusion\n\nL'injection de dépendances en Rust repose sur les traits, Arc et le pattern composition root. Pas besoin de framework — le système de types de Rust et son modèle de propriété guident naturellement vers une bonne architecture.","\u003Ch2 id=\"le-principe-d-injection-de-d-pendances\">Le principe d’injection de dépendances\u003C\u002Fh2>\n\u003Cp>L’injection de dépendances est un principe de conception fondamental : un composant doit recevoir ses dépendances de l’extérieur plutôt que de les créer en interne. Rust n’a pas de framework DI standard, et son modèle de propriété rend les patterns DI naïfs maladroits.\u003C\u002Fp>\n\u003Ch2 id=\"traits-comme-interfaces\">Traits comme interfaces\u003C\u002Fh2>\n\u003Cp>En Rust, les traits sont l’équivalent des interfaces :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">#[async_trait]\ntrait Database: Send + Sync {\n    async fn get_user(&amp;self, id: UserId) -&gt; Result&lt;User&gt;;\n    async fn save_user(&amp;self, user: &amp;User) -&gt; Result&lt;()&gt;;\n}\n\n#[async_trait]\ntrait Cache: Send + Sync {\n    async fn get(&amp;self, key: &amp;str) -&gt; Option&lt;Vec&lt;u8&gt;&gt;;\n    async fn set(&amp;self, key: &amp;str, value: &amp;[u8], ttl: Duration) -&gt; Result&lt;()&gt;;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"arc-vs-g-n-riques\">Arc\u003Cdyn Trait> vs génériques\u003C\u002Fh2>\n\u003Ch3>Dispatch dynamique avec Arc\u003Cdyn Trait>\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-rust\">struct UserService {\n    db: Arc&lt;dyn Database&gt;,\n    cache: Arc&lt;dyn Cache&gt;,\n}\n\nimpl UserService {\n    fn new(db: Arc&lt;dyn Database&gt;, cache: Arc&lt;dyn Cache&gt;) -&gt; Self {\n        Self { db, cache }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Avantages : flexibilité, échange facile des implémentations, pas de monomorphisation.\nInconvénients : indirection de pointeur, pas d’inlining.\u003C\u002Fp>\n\u003Ch3>Dispatch statique avec génériques\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-rust\">struct UserService&lt;D: Database, C: Cache&gt; {\n    db: D,\n    cache: C,\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Avantages : inlining, pas d’allocation heap, performances optimales.\nInconvénients : code plus verbeux, monomorphisation (plus gros binaire).\u003C\u002Fp>\n\u003Cp>Règle pratique : utilisez \u003Ccode>Arc&lt;dyn Trait&gt;\u003C\u002Fcode> pour les frontières de service et les génériques pour le code critique en performance.\u003C\u002Fp>\n\u003Ch2 id=\"le-pattern-composition-root\">Le pattern Composition Root\u003C\u002Fh2>\n\u003Cp>Construisez le graphe de dépendances au point d’entrée de l’application :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">#[tokio::main]\nasync fn main() -&gt; Result&lt;()&gt; {\n    \u002F\u002F Composition root — toutes les dépendances sont créées ici\n    let pool = PgPool::connect(&amp;database_url).await?;\n    let redis = RedisClient::connect(&amp;redis_url).await?;\n\n    let db: Arc&lt;dyn Database&gt; = Arc::new(PostgresDb::new(pool));\n    let cache: Arc&lt;dyn Cache&gt; = Arc::new(RedisCache::new(redis));\n\n    let user_service = Arc::new(UserService::new(db.clone(), cache.clone()));\n    let auth_service = Arc::new(AuthService::new(db.clone()));\n\n    let app = Router::new()\n        .route(\"\u002Fusers\", get(list_users))\n        .with_state(AppState { user_service, auth_service });\n\n    axum::serve(listener, app).await?;\n    Ok(())\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"mocks-pour-les-tests\">Mocks pour les tests\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">struct MockDatabase {\n    users: Mutex&lt;HashMap&lt;UserId, User&gt;&gt;,\n}\n\n#[async_trait]\nimpl Database for MockDatabase {\n    async fn get_user(&amp;self, id: UserId) -&gt; Result&lt;User&gt; {\n        self.users.lock().await\n            .get(&amp;id)\n            .cloned()\n            .ok_or(Error::NotFound)\n    }\n\n    async fn save_user(&amp;self, user: &amp;User) -&gt; Result&lt;()&gt; {\n        self.users.lock().await.insert(user.id, user.clone());\n        Ok(())\n    }\n}\n\n#[tokio::test]\nasync fn test_user_creation() {\n    let db: Arc&lt;dyn Database&gt; = Arc::new(MockDatabase::default());\n    let cache: Arc&lt;dyn Cache&gt; = Arc::new(MockCache::default());\n    let service = UserService::new(db, cache);\n\n    let user = service.create_user(\"Alice\").await.unwrap();\n    assert_eq!(user.name, \"Alice\");\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"le-pattern-servicelocator\">Le pattern ServiceLocator\u003C\u002Fh2>\n\u003Cp>Pour les systèmes complexes avec de nombreuses dépendances :\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">struct ServiceLocator {\n    services: HashMap&lt;TypeId, Box&lt;dyn Any + Send + Sync&gt;&gt;,\n}\n\nimpl ServiceLocator {\n    fn register&lt;T: Send + Sync + 'static&gt;(&amp;mut self, service: T) {\n        self.services.insert(TypeId::of::&lt;T&gt;(), Box::new(service));\n    }\n\n    fn resolve&lt;T: Send + Sync + 'static&gt;(&amp;self) -&gt; Option&lt;&amp;T&gt; {\n        self.services.get(&amp;TypeId::of::&lt;T&gt;())\n            .and_then(|s| s.downcast_ref())\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"conclusion\">Conclusion\u003C\u002Fh2>\n\u003Cp>L’injection de dépendances en Rust repose sur les traits, Arc et le pattern composition root. Pas besoin de framework — le système de types de Rust et son modèle de propriété guident naturellement vers une bonne architecture.\u003C\u002Fp>\n","fr","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:29.325797Z","Injection de dépendances en Rust — ServiceLocator, Arc et objets trait","Injection de dépendances en Rust sans framework : Arc, objets trait, composition root et ServiceLocator pour systèmes complexes.","injection dépendances rust",null,"index, follow",[22,27],{"id":23,"name":24,"slug":25,"created_at":26},"c0000000-0000-0000-0000-000000000022","Performance","performance","2026-03-28T10:44:21.513630Z",{"id":28,"name":29,"slug":30,"created_at":26},"c0000000-0000-0000-0000-000000000001","Rust","rust","Ingénierie",[33,39,45],{"id":34,"title":35,"slug":36,"excerpt":37,"locale":12,"category_name":31,"published_at":38},"d0000000-0000-0000-0000-000000000677","Pourquoi Bali devient le hub impact-tech d'Asie du Sud-Est en 2026","pourquoi-bali-devient-hub-impact-tech-asie-sud-est-2026","Bali se classe 16e parmi les écosystèmes startups d'Asie du Sud-Est. Avec une concentration croissante de bâtisseurs Web3, de startups IA durables et d'entreprises eco-travel tech, l'île se forge une identité de capitale impact-tech de la région.","2026-03-28T10:44:49.517126Z",{"id":40,"title":41,"slug":42,"excerpt":43,"locale":12,"category_name":31,"published_at":44},"d0000000-0000-0000-0000-000000000676","Le patchwork de la protection des données ASEAN : checklist de conformité pour les développeurs","patchwork-protection-donnees-asean-checklist-conformite-developpeurs","Sept pays de l'ASEAN disposent désormais de lois complètes sur la protection des données, chacune avec des modèles de consentement, des exigences de localisation et des structures de sanctions différents. Voici une checklist pratique de conformité pour les développeurs.","2026-03-28T10:44:49.504560Z",{"id":46,"title":47,"slug":48,"excerpt":49,"locale":12,"category_name":31,"published_at":50},"d0000000-0000-0000-0000-000000000675","La transformation numérique de 29 milliards de dollars d'Indonesia : opportunités pour les éditeurs de logiciels","transformation-numerique-29-milliards-dollars-indonesia-opportunites-editeurs-logiciels","Le marché des services informatiques d'Indonesia devrait atteindre 29,03 milliards de dollars en 2026, contre 24,37 milliards en 2025. L'infrastructure cloud, l'IA, le e-commerce et les centres de données tirent la croissance la plus rapide d'Asie du Sud-Est.","2026-03-28T10:44:49.469231Z",{"id":13,"name":52,"slug":53,"bio":54,"photo_url":19,"linkedin":19,"role":55,"created_at":56,"updated_at":56},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]