# Architektur

## Schichten

```mermaid
flowchart TD
    IO["io (Konsole / GUI)<br/>Interaktion mit Spieler"]
    GAME["game (Engine/Loop)<br/>Spielfluss, hält World + Player"]
    CMD["command (Commands)<br/>go, take, use, talk, ..."]
    MODEL["model (Domain)<br/>Room, Item, Player, Npc, World"]
    LOADER["loader (YAML + DTO)<br/>liest Ressourcen, validiert"]

    IO -- "liest/schreibt via GameIO" --> GAME
    GAME -- "dispatcht via CommandRegistry" --> CMD
    CMD -- "mutiert" --> MODEL
    LOADER -- "baut auf aus DTOs" --> MODEL
```

## Package-Struktur

```mermaid
flowchart LR
    root["thb.jeanluc.adventure"]

    root --> app["App.java<br/>AppGui.java"]
    root --> model
    root --> loader
    root --> command
    root --> game
    root --> io

    model --> model_files["World, Room,<br/>Player, Npc, Direction"]
    model --> item
    item --> item_files["Item (abstract)<br/>ReadableItem<br/>SwitchableItem<br/>PlainItem"]

    loader --> loader_files["WorldLoader<br/>ReferenceResolver<br/>WorldValidator"]
    loader --> dto
    dto --> dto_files["RoomDto, ItemDto,<br/>NpcDto, GameDto"]

    command --> cmd_core["Command (Interface)<br/>CommandRegistry<br/>CommandParser"]
    command --> impl
    impl --> impl_files["GoCommand, TakeCommand,<br/>DropCommand, UseCommand,<br/>InventoryCommand, LookCommand,<br/>TalkCommand, GiveCommand,<br/>HelpCommand, QuitCommand"]

    game --> game_files["Game (Loop)<br/>GameContext"]

    io --> io_files["GameIO (Interface)<br/>ConsoleIO<br/>SwingIO"]
```

## DTO vs. Domain-Trennung

**Kernprinzip:** YAML wird in Records deserialisiert (DTOs), erst danach werden String-IDs zu Objekt-Referenzen aufgelöst.

| | DTO | Domain |
|---|---|---|
| Typ | `record` | Lombok-Klasse |
| Mutabilität | immutable | mutable wo nötig |
| Felder | nur Daten + String-IDs | Objekt-Referenzen, EnumMap, etc. |
| Zweck | Jackson-Mapping | Spielablauf |
| Tests | Loader-Tests | Domain-Tests, brauchen kein YAML |

**Warum getrennt?**
- Domain-Klassen müssen nicht mit Jackson-Annotations verschmutzt werden
- `Room.exits` ist `EnumMap<Direction, Room>` (typisicher, schnell) statt `Map<String, String>` (was zum YAML passt)
- Validierung passiert beim Übergang DTO→Domain (siehe [loading-flow.md](loading-flow.md))
- Domain-Tests können Objekte direkt im Code bauen, ohne YAML-Fixtures

## Game-Loop (vereinfacht)

```java
while (!game.isOver()) {
    String input = io.read();
    ParsedCommand parsed = parser.parse(input);
    Command cmd = registry.get(parsed.verb());
    if (cmd == null) {
        io.write("Unbekannter Befehl.");
    } else {
        cmd.execute(context, parsed.args());
    }
}
```

## IO-Abstraktion

Konsole und GUI teilen sich `GameIO`:

```java
public interface GameIO {
    String read();             // blockierende Leseoperation
    void write(String text);
}
```

Damit ist der Game-Loop **identisch** für beide Modi. `SwingIO` blockiert intern mit einer `BlockingQueue<String>`, die vom JTextField-ActionListener gefüllt wird.
