# Architektur
## Schichten
```mermaid
flowchart TD
IO["io (Konsole / GUI)
Interaktion mit Spieler"]
GAME["game (Engine/Loop)
Spielfluss, hält World + Player"]
CMD["command (Commands)
go, take, use, talk, ..."]
MODEL["model (Domain)
Room, Item, Player, Npc, World"]
LOADER["loader (YAML + DTO)
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
AppGui.java"]
root --> model
root --> loader
root --> command
root --> game
root --> io
model --> model_files["World, Room,
Player, Npc, Direction"]
model --> item
item --> item_files["Item (abstract)
ReadableItem
SwitchableItem
PlainItem"]
loader --> loader_files["WorldLoader
ReferenceResolver
WorldValidator"]
loader --> dto
dto --> dto_files["RoomDto, ItemDto,
NpcDto, GameDto"]
command --> cmd_core["Command (Interface)
CommandRegistry
CommandParser"]
command --> impl
impl --> impl_files["GoCommand, TakeCommand,
DropCommand, UseCommand,
InventoryCommand, LookCommand,
TalkCommand, GiveCommand,
HelpCommand, QuitCommand"]
game --> game_files["Game (Loop)
GameContext"]
io --> io_files["GameIO (Interface)
ConsoleIO
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` (typisicher, schnell) statt `Map` (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`, die vom JTextField-ActionListener gefüllt wird.