# Dependency Injection Container

Letzte Aktualisierung: 2026-03-06

## Was ist das und wozu brauchen wir es?

Statt dass jede Action-Datei ihre Services selbst zusammenbaut
(`new UserAccountService(new UserRepo(), new AuditRepo(), ...)`),
gibt es einen zentralen "Service-Speicher": den **AppContainer**.

Der Container weiß, wie er jeden Service bauen muss —
aber er baut ihn erst, wenn er zum ersten Mal gebraucht wird (lazy).
Wird der gleiche Service in derselben Anfrage nochmal angefragt,
bekommt man dieselbe Instanz zurück (Singleton pro Request).

## Services abrufen — der `app()` Helper

In jeder Action-Datei (`pages/**/*.php`) steht der Container über
die globale Hilfsfunktion `app()` zur Verfügung:

```php
$userService = app(\MintyPHP\Service\User\UserAccountService::class);
$authService = app(\MintyPHP\Service\Access\AuthorizationService::class);
```

Den vollständigen Klassennamen (FQN) als Schlüssel verwenden —
so gibt es keine Namenskollisionen und IDEs können Autocomplete nutzen.

`app()` wirft eine `RuntimeException` wenn der Service nicht registriert ist.
Das ist absichtlich laut, damit fehlende Registrierungen sofort auffallen.

## Aufbau: Registrars

Beim Start der Anwendung läuft `core/App/registerContainer.php`.
Es erstellt den Container und ruft 8 **Registrars** nacheinander auf:

| Registrar | Was er registriert |
|---|---|
| `RepositoryFactoryRegistrar` | Alle Repository-Factories (SQL-Schicht) |
| `ServiceFactoryRegistrar` | Alle Service-Factories (Business-Logik, mit Abhängigkeiten) |
| `AccessRegistrar` | RBAC-spezifische Services (Roles, Permissions, Gateways) |
| `AuthRegistrar` | Auth-spezifische Services (Login, SSO, API-Tokens) |
| `DirectoryRegistrar` | LDAP/Entra-Directory-Services |
| `UserRegistrar` | User-spezifische Services und Gateways |
| `SettingsRegistrar` | Settings-Services und Gateways |
| `AppServicesRegistrar` | Konkrete Top-Level-Services, die direkt in Actions genutzt werden |

Jeder Registrar implementiert das Interface `ContainerRegistrar::register(AppContainer $container)`.

## Wie Abhängigkeiten aufgelöst werden

```php
// In ServiceFactoryRegistrar:
$container->set(UserServicesFactory::class, static fn (AppContainer $c) =>
    new UserServicesFactory(
        $c->get(AuthServicesFactory::class),      // wird lazy aufgelöst
        $c->get(UserRepositoryFactory::class),
        $c->get(UserGatewayFactory::class),
        $c->get(DatabaseSessionRepository::class)
    )
);
```

Der Container übergibt sich selbst als `$c` an die Factory-Funktion —
so werden alle Abhängigkeiten automatisch und lazy aus dem Container gezogen.
Zirkuläre Abhängigkeiten werden über `static fn (): Foo => $c->get(Foo::class)` aufgelöst
(der Aufruf wird verzögert, bis er wirklich gebraucht wird).

## Neuen Service registrieren

**1. Service und ggf. Factory erstellen** (wie bisher in `core/Service/`)

**2. Im passenden Registrar eintragen:**

```php
// In AppServicesRegistrar (für Services, die direkt in Actions genutzt werden):
$container->set(MeinNeuerService::class, static fn (AppContainer $c) =>
    new MeinNeuerService(
        $c->get(UserServicesFactory::class),
        $c->get(SettingGateway::class)
    )
);
```

**3. In der Action nutzen:**

```php
$meinService = app(\MintyPHP\Service\MeinNeuerService::class);
```

**Faustregel: welcher Registrar?**

- Neue *Repository-Factory* → `RepositoryFactoryRegistrar`
- Neue *Service-Factory* (mit Abhängigkeiten) → `ServiceFactoryRegistrar`
- Konkreter *Service*, der direkt in Actions per `app()` abgerufen wird → `AppServicesRegistrar`
- Klar einer Domain zugeordnet (Auth, Access, Directory, User, Settings) → der passende Domain-Registrar

## Wichtige Regeln

- **Nie** `new SomeService(...)` direkt in Action-Dateien schreiben —
  immer `app(SomeService::class)` nutzen.
- **Nie** Factory-Ketten in Actions (`app(SomeFactory::class)->createService()`) —
  den konkreten Service direkt registrieren und per `app()` holen.
- Der Container lebt **pro HTTP-Anfrage** — er wird in `web/index.php` initialisiert
  und über `setAppContainer()` im globalen State abgelegt.
- In CLI-Commands wird der Container separat initialisiert (über die Runtime-Bootstrap-Klassen unter `core/Console/Support/`).
- In `core/Service/**` außerhalb von `*Factory.php` niemals `new ...Factory()`,
  `new ...Service()` oder `new ...Repository()` direkt instanziieren.
