# Message Communication System

## Vue d'ensemble

Le système de communication inter-modules utilise des **classes de messages typées** au lieu de JSON brut pour garantir la fiabilité et la maintenabilité.

## Concept fondamental

**Une classe = un type de message**
**Une instance = un message individuel**

Exemple: La classe `TankMovedMessage` définit le type "mouvement de tank". Chaque fois qu'un tank bouge, on crée une nouvelle instance de cette classe.

## Pourquoi des classes?

### Problème avec JSON brut
- Erreurs découvertes uniquement au runtime
- Pas de validation automatique
- Typos dans les clés non détectées
- Difficile à maintenir et refactoriser

### Solution avec classes
- Validation au compile-time
- Contrat clair entre modules
- IDE autocomplétion et refactoring
- Hot-reload friendly (petites classes isolées)
- Évolutif (ajout de champs sans casser l'existant)

## Architecture

### IMessage - Interface de base

Interface pure définissant le contrat minimal pour tous les messages:

- **Type identification**: Chaque message déclare son type via enum
- **Serialization**: Conversion vers JSON pour transport via IIO
- **Deserialization**: Reconstruction depuis JSON reçu

### AMessage - Classe abstraite avec métadata

Classe abstraite obligatoire fournissant l'implémentation partielle commune à tous les messages.

**Métadata immutables (enforced):**
- `timestamp` - Horodatage de création (const)
- `sender` - Module émetteur (const)
- `messageId` - ID unique pour tracking et reassemblage fragments (const)
- `partId` - ID de fragment pour messages multi-parts (const)

**Caractéristiques:**
- Constructeur protégé force passage par classes enfants
- Métadata initialisées automatiquement à la construction
- Impossible de modifier métadata après création
- Tous messages garantis d'avoir timestamp/sender/messageId

### MessageType - Enum central

Enum listant tous les types de messages du système:
- `TANK_MOVED`, `TANK_FIRED`, `TANK_DESTROYED`
- `PRICE_UPDATED`, `TRADE_EXECUTED`
- `ITEM_PRODUCED`, `BELT_CONGESTION`
- etc.

**Pourquoi enum plutôt que strings?**
- Performance (comparaison d'entiers)
- Type safety (typos détectées au compile)
- Centralisation (tous les types visibles)

### Messages concrets

Chaque type de message est une classe dédiée héritant de `AMessage`:

**TankMovedMessage**: Position, vitesse, ID tank
**PriceUpdatedMessage**: Item, ancien prix, nouveau prix
**ItemProducedMessage**: Item type, quantité, factory ID

Chaque classe:
- Hérite obligatoirement de `AMessage`
- Appelle constructeur `AMessage(senderModule)`
- Stocke ses données spécifiques
- Valide à la construction
- Sérialise/désérialise son propre format

## Flow de communication

### Publication

**Exemple complet:**
```cpp
void TankModule::process(const json& input) {
    // 1. Calculer nouvelle position
    Vector2 newPos = calculatePosition();
    float currentSpeed = getSpeed();

    // 2. Créer message (validation automatique à la construction)
    TankMovedMessage msg(newPos, currentSpeed, tankId);

    // 3. Sérialiser en JSON
    json serialized = msg.serialize();

    // 4. Publier via IIO
    io->publish("tank:" + std::to_string(tankId), serialized);
}
```

**Étapes:**
1. Module crée instance de message (validation à la construction)
2. Message sérialisé en JSON via `serialize()`
3. Publié via `IIO::publish(topic, json)`
4. IIO route vers subscribers du topic

### Réception

**Méthode 1: Type-safe template helper (recommandé)**
```cpp
// Clean syntax avec type safety automatique
auto tankMsg = io->pullMessageAs<TankMovedMessage>();

if (tankMsg) {  // nullptr si type mismatch ou queue vide
    Vector2 pos = tankMsg->getPosition();
    float speed = tankMsg->getSpeed();
}
```

**Méthode 2: Manuelle (si besoin de flexibilité)**
```cpp
// Récupère message brut
Message rawMsg = io->pullMessage();

// Désérialise vers base
std::unique_ptr<IMessage> baseMsg = IMessage::deserialize(rawMsg.data);

// Cast vers type concret
if (TankMovedMessage* tankMsg = dynamic_cast<TankMovedMessage*>(baseMsg.get())) {
    // Accès type-safe
}
```

**Template helper implementation:**
```cpp
class IIO {
    // Template helper inline (zero overhead)
    template<typename T>
    std::unique_ptr<T> pullMessageAs() {
        if (!hasMessages()) return nullptr;

        Message raw = pullMessage();
        std::unique_ptr<IMessage> base = IMessage::deserialize(raw.data);

        T* casted = dynamic_cast<T*>(base.get());
        if (!casted) return nullptr;  // Type mismatch

        base.release();
        return std::unique_ptr<T>(casted);
    }
};
```

**Performance:**
- Template inlined = zero function call overhead
- `dynamic_cast` = ~5-10ns (négligeable vs JSON parsing)
- Bottleneck réel = JSON serialization, pas le cast

### Désérialisation centralisée

**Factory pattern avec routing par type:**
```cpp
// IMessage base class
std::unique_ptr<IMessage> IMessage::deserialize(const json& data) {
    // Extract type from JSON
    int typeInt = data.value("type", -1);
    MessageType type = static_cast<MessageType>(typeInt);

    // Route to concrete deserializer
    switch (type) {
        case MessageType::TANK_MOVED:
            return TankMovedMessage::deserialize(data);

        case MessageType::PRICE_UPDATED:
            return PriceUpdatedMessage::deserialize(data);

        case MessageType::ITEM_PRODUCED:
            return ItemProducedMessage::deserialize(data);

        // Add new messages here

        default:
            return nullptr;  // Unknown type
    }
}
```

**Chaque message implémente sa propre désérialisation:**
```cpp
class TankMovedMessage : public AMessage {
    static std::unique_ptr<TankMovedMessage> deserialize(const json& data) {
        try {
            Vector2 pos{data["position"]["x"], data["position"]["y"]};
            float speed = data["speed"];
            int tankId = data["tankId"];

            return std::make_unique<TankMovedMessage>(pos, speed, tankId);
        } catch (const json::exception& e) {
            return nullptr;  // Malformed JSON
        }
    }
};
```

**Gestion des erreurs:**
- JSON malformé → retourne `nullptr`
- Type inconnu → retourne `nullptr`
- Validation échoue → exception à la construction
- Modules doivent vérifier `if (msg != nullptr)`

## Organisation du code

### Location des messages
```
modules/shared/messages/
├── IMessage.h              # Interface pure
├── AMessage.h              # Classe abstraite avec métadata
├── MessageType.h           # Enum des types
├── TankMovedMessage.h
├── PriceUpdatedMessage.h
└── ...
```

**Rationale:**
- Single source of truth (pas de duplication)
- Contrat partagé entre émetteur et récepteur
- Facile à trouver et maintenir

### Validation

**À la construction**: Invariants validés immédiatement
- Vitesse négative → exception
- ID invalide → exception
- Fail fast pour détecter bugs tôt

**À la désérialisation**: Données réseau/externes validées
- JSON malformé → retourne nullptr
- Champs manquants → retourne nullptr
- Protège contre données corrompues

## Décisions de design finalisées

### Versioning - Breaking changes assumées

**Décision:** Types versionnés avec breaking changes strictes

Changement de format = nouveau type + nouvelle classe:
- `TANK_MOVED_V1` → `TANK_MOVED_V2` = types différents
- Pas de backward compatibility
- Pas de forward compatibility
- Migration forcée de tous les modules

**Rationale:**
- Zéro ambiguïté sur le format attendu
- Pas de logique conditionnelle complexe
- Hot-reload force mise à jour synchronisée
- Détection immédiate des incompatibilités
- Code simple et clair

**Conséquence:**
- Format change → tous modules doivent migrer
- Ancien code ne compile plus → migration manuelle obligatoire
- Breaking changes explicites et visibles

### Message inheritance - AMessage obligatoire

**Décision:** Classe abstraite `AMessage` avec métadata immutables enforced

Architecture:
```
IMessage (interface pure)
    ↓
AMessage (classe abstraite - métadata immutables)
    ↓
TankMovedMessage, PriceUpdatedMessage... (classes concrètes)
```

**Enforcement:**
- Constructeur `AMessage` protégé → impossible de créer message sans passer par enfant
- Métadata const → immutables après construction
- Tous messages garantis d'avoir timestamp/sender/messageId
- Format uniforme pour tous messages du système

**Rationale:**
- Pas de duplication (métadata dans AMessage)
- Impossible d'oublier métadata
- Contrat strict et enforced au compile-time
- Simplicité pour messages concrets (métadata automatique)

## Décisions de design finalisées (suite)

### Size limits - Fragmentation automatique par IO

**Décision:** Pas de limite au niveau message, fragmentation automatique par couche transport

**Architecture:**
- **Modules**: Publient/reçoivent messages complets (transparence totale)
- **IIO**: Gère fragmentation/défragmentation automatiquement
- **Transport adaptatif**: Fragmentation si nécessaire selon type IO

**Fragmentation par type:**
- **IntraIO**: Pas de fragmentation (copie mémoire directe)
- **LocalIO**: Fragmentation si > seuil (ex: 64KB chunks)
- **NetworkIO**: Fragmentation si > MTU (~1500 bytes)

**Mécanisme:**
1. Module publie message → sérialisé en JSON
2. IO détecte si taille > seuil transport
3. Si oui: découpe en fragments avec `messageId` + `partId`
4. Transport des fragments
5. IO destination reassemble via `messageId` (collect tous `partId`)
6. Module reçoit message complet via `pullMessage()`

**Robustesse:**
- **Packet loss**: Timeout si fragments incomplets (ex: 30s)
- **Ordering**: `partId` garantit ordre de reassemblage
- **Monitoring**: Log warning si message > 100KB (design smell probable)

**Conséquence:**
- Messages peuvent être arbitrairement gros
- Complexité cachée dans couche IO
- Performance optimisée par type de transport

### Async handling - Délégation au module

**Décision:** Pas de gestion async spéciale au niveau messages

**Rationale:**
- Messages = transport de données uniquement
- `ITaskScheduler` existe déjà pour opérations coûteuses
- Module décide si déléguer ou traiter directement
- Keep messages simple et stupid

**Responsabilité:**
- **Message**: Transport de données (aucune intelligence)
- **Module**: Décide de déléguer opérations coûteuses au scheduler
- **ITaskScheduler**: Gère threading et async

**Exemple:**
```cpp
// Module reçoit message déclenchant opération coûteuse
if (PathfindingRequestMessage* req = dynamic_cast<...>) {
    // Module délègue au TaskScheduler
    scheduler->scheduleTask(PATHFINDING_TASK, req->serialize());
    // Continue traitement autres messages
}

// Plus tard, récupère résultat
if (scheduler->hasCompletedTasks() > 0) {
    TaskResult result = scheduler->getCompletedTask();
    // Publie résultat via message
}
```

### Message ordering - Pas de garantie + Messages remplaçables

**Décision:** Pas de garantie d'ordre, modules gèrent via timestamp si critique

**Approche:**
- **Pas de FIFO enforced**: Messages peuvent arriver dans ordre arbitraire
- **Messages remplaçables par défaut**: `SubscriptionConfig.replaceable = true` pour majorité
- **Timestamp disponible**: `msg->getTimestamp()` permet tri manuel si nécessaire
- **Utilité questionnable**: Architecture modulaire rend ordre moins critique

**Rationale:**
- Performance maximale (pas de garanties coûteuses)
- Messages remplaçables optimisent bandwidth (garde juste dernier)
- Timestamp dans `AMessage` permet tri si vraiment nécessaire
- Architecture découple les dépendances temporelles

**Exemples d'usage:**
```cpp
// Prix - remplaçable (dernière valeur suffit)
io->subscribeLowFreq("economy:*", {.replaceable = true});

// Tank position - remplaçable (position actuelle suffit)
io->subscribe("tank:*", {.replaceable = true});

// Events critiques - non remplaçable + tri si nécessaire
io->subscribe("combat:*", {.replaceable = false});

// Si ordre vraiment critique (rare)
std::vector<Message> messages = collectAllMessages();
std::sort(messages.begin(), messages.end(),
    [](const Message& a, const Message& b) {
        return a.getTimestamp() < b.getTimestamp();
    });
```

**Conséquence:**
- Modules ne doivent pas assumer ordre de réception
- Messages remplaçables = dernière valeur seulement
- Tri par timestamp disponible mais rarement nécessaire

## Questions ouvertes

Aucune question ouverte restante. Toutes les décisions de design ont été finalisées.

## Statut d'implémentation

**Phase actuelle:** Design et documentation

**Prochaines étapes:**
1. Créer interface `IMessage.h`
2. Créer classe abstraite `AMessage.h` avec métadata immutables
3. Créer enum `MessageType.h`
4. Implémenter 5-10 messages d'exemple
5. Tester avec modules existants
6. Itérer selon usage réel

## Références

- `src/core/include/warfactory/IIO.h` - Couche pub/sub
- `docs/01-architecture/architecture-technique.md` - Patterns de communication
- `docs/03-implementation/CLAUDE-HOT-RELOAD-GUIDE.md` - Impact sur design messages
