koprogo_api/application/ports/module_registry.rs
1//! Port (trait) du registre de modules — Story 5.1 (#585), ADR-0015.
2//!
3//! Hexagonal : le trait vit côté application, l'implémentation PostgreSQL
4//! dans `infrastructure/database/repositories/module_registry_impl.rs`.
5//!
6//! Toutes les méthodes retournent `Result<_, AppError>` (CRITICAL.md §4).
7
8use crate::application::error::AppError;
9use crate::domain::entities::Module;
10use async_trait::async_trait;
11use uuid::Uuid;
12
13#[async_trait]
14pub trait ModuleRegistry: Send + Sync {
15 /// Les modules actifs (`archived_at IS NULL`) d'une ACP.
16 ///
17 /// Une ACP inconnue rend une liste vide, pas une erreur : la garde de
18 /// portée s'exécute **avant** et c'est elle qui décide si l'appelant a
19 /// le droit de savoir que l'ACP n'existe pas.
20 async fn list_enabled(&self, acp_id: Uuid) -> Result<Vec<Module>, AppError>;
21
22 /// Allume un module. Idempotent : rallumer un module déjà actif remet
23 /// simplement `archived_at` à `NULL` et ne duplique pas la ligne — c'est
24 /// ce qui permet le cycle activer/désactiver/réactiver d'INV-27 sans
25 /// perdre les données.
26 async fn enable(&self, acp_id: Uuid, module: Module) -> Result<(), AppError>;
27
28 /// Éteint un module. Idempotent également. **Ne supprime rien** : pose
29 /// `archived_at`. Le refus d'éteindre `identity` est une règle métier et
30 /// vit dans le use-case, pas ici.
31 async fn disable(&self, acp_id: Uuid, module: Module) -> Result<(), AppError>;
32
33 /// Chemin chaud : appelé par `ModuleGuard` à chaque requête gardée.
34 async fn is_enabled(&self, acp_id: Uuid, module: Module) -> Result<bool, AppError>;
35}