# InnungsApp — Technische Architektur

> **Version:** 1.0 | **Stand:** Februar 2026

---

## 1. Überblick

InnungsApp besteht aus drei Hauptkomponenten:

```
┌─────────────────────────────────────────────────────────────┐
│                        CLIENTS                              │
│  ┌─────────────────┐  ┌─────────────────┐                  │
│  │  Mobile App     │  │  Admin Web App  │                  │
│  │  (React Native) │  │  (Next.js)      │                  │
│  │  iOS + Android  │  │  Browser        │                  │
│  └────────┬────────┘  └────────┬────────┘                  │
└───────────┼────────────────────┼──────────────────────────┘
            │                    │
            ▼                    ▼
┌─────────────────────────────────────────────────────────────┐
│                      SUPABASE                               │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────┐  │
│  │ Auth     │  │ Postgres │  │ Storage  │  │ Realtime  │  │
│  │ Magic    │  │ + RLS    │  │ PDFs     │  │ Push +    │  │
│  │ Link     │  │ Database │  │ Images   │  │ Events    │  │
│  └──────────┘  └──────────┘  └──────────┘  └───────────┘  │
└─────────────────────────────────────────────────────────────┘
            │
            ▼
┌─────────────────────────────────────────────────────────────┐
│               EXTERNE DIENSTE                               │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────┐  │
│  │ Expo     │  │ Resend   │  │ PostHog  │  │ Mux       │  │
│  │ Push     │  │ E-Mail   │  │ Analytics│  │ Video     │  │
│  │ (FCM/APNs│  │ Transact.│  │          │  │ (Q2)      │  │
│  └──────────┘  └──────────┘  └──────────┘  └───────────┘  │
└─────────────────────────────────────────────────────────────┘
```

---

## 2. Mobile App (React Native + Expo)

### Tech Stack

| Schicht | Technologie | Begründung |
|---|---|---|
| Framework | React Native 0.74 + Expo SDK 51 | Eine Codebasis iOS + Android |
| Navigation | Expo Router v3 (file-based) | Typ-sicher, einfach wartbar |
| State Management | Zustand | Leichtgewichtig, kein Redux-Overhead |
| Data Fetching | TanStack Query v5 | Caching, Background Refetch, Optimistic Updates |
| UI-Komponenten | Custom + NativeWind (Tailwind on Native) | Konsistentes Design, schnelle Entwicklung |
| Push Notifications | Expo Notifications + FCM/APNs | Out-of-the-box mit Expo |
| Auth | Supabase Auth Client | Magic Link Flow |
| Type Safety | TypeScript (strict mode) | Pflicht für Produktionscode |

### Ordnerstruktur

```
apps/mobile/
├── app/                        # Expo Router — File-based Navigation
│   ├── (auth)/
│   │   ├── login.tsx           # Magic Link Login Screen
│   │   └── verify.tsx          # Token Verification
│   ├── (tabs)/
│   │   ├── _layout.tsx         # Tab Bar Layout
│   │   ├── index.tsx           # Dashboard / Home Feed
│   │   ├── members.tsx         # Mitgliederverzeichnis
│   │   ├── news.tsx            # Mitteilungen Feed
│   │   ├── termine.tsx         # Terminkalender
│   │   └── stellen.tsx         # Lehrlingsbörse
│   ├── member/[id].tsx         # Mitglied Detailansicht
│   ├── news/[id].tsx           # Beitrag Detailansicht
│   ├── termin/[id].tsx         # Termin Detailansicht
│   └── _layout.tsx             # Root Layout (Auth Guard)
├── components/
│   ├── ui/                     # Atomare UI-Komponenten
│   │   ├── Button.tsx
│   │   ├── Card.tsx
│   │   ├── Badge.tsx
│   │   ├── Avatar.tsx
│   │   └── Input.tsx
│   ├── members/                # Feature-spezifische Komponenten
│   ├── news/
│   ├── termine/
│   └── stellen/
├── hooks/
│   ├── useAuth.ts
│   ├── useMembers.ts
│   ├── useNews.ts
│   └── usePushNotifications.ts
├── lib/
│   ├── supabase.ts             # Supabase Client Singleton
│   ├── queryClient.ts          # TanStack Query Client
│   └── notifications.ts        # Push Token Registration
├── store/
│   └── auth.ts                 # Zustand Auth Store
├── types/
│   └── database.ts             # Generierte Supabase Types
└── constants/
    ├── colors.ts               # Design Tokens
    └── config.ts               # Env-Variablen
```

---

## 3. Admin Web App (Next.js)

### Tech Stack

| Schicht | Technologie |
|---|---|
| Framework | Next.js 14 (App Router) |
| Styling | Tailwind CSS + shadcn/ui |
| Auth | Supabase Auth (SSR) |
| Data Fetching | Server Components + TanStack Query (Client) |
| Tables | TanStack Table v8 |
| Forms | React Hook Form + Zod |
| Charts | Recharts |
| Deployment | Vercel |

### Ordnerstruktur

```
apps/admin/
├── app/
│   ├── (auth)/
│   │   └── login/page.tsx
│   ├── (dashboard)/
│   │   ├── layout.tsx              # Sidebar Layout
│   │   ├── page.tsx                # Overview Dashboard
│   │   ├── members/
│   │   │   ├── page.tsx            # Mitgliederliste
│   │   │   ├── new/page.tsx        # Mitglied anlegen
│   │   │   └── [id]/page.tsx       # Mitglied bearbeiten
│   │   ├── news/
│   │   │   ├── page.tsx
│   │   │   └── new/page.tsx
│   │   ├── termine/
│   │   ├── stellen/
│   │   └── settings/
│   └── layout.tsx
├── components/
│   ├── ui/                         # shadcn/ui Komponenten
│   ├── data-table/
│   └── forms/
└── lib/
    ├── supabase-server.ts          # Supabase SSR Client
    └── actions.ts                  # Server Actions
```

---

## 4. Backend: Supabase

### Warum Supabase?

- **Kein eigener API-Server** nötig für MVP → spart 4–6 Wochen Entwicklung
- **PostgreSQL** mit vollem SQL-Zugriff → keine NoSQL-Kompromisse
- **Row Level Security** → Multi-Tenancy ohne eigene Middleware
- **Realtime** → Live-Updates ohne WebSocket-Implementierung
- **Storage** → S3-kompatibel, CDN included
- **Auth** → Magic Link, Sessions, JWT out-of-the-box
- **EU-Region Frankfurt** → DSGVO-konform

### Supabase Services genutzt

| Service | Verwendung |
|---|---|
| Auth | Magic Link Login, Session-Management, JWT |
| Database | PostgreSQL, alle Tabellen |
| Row Level Security | Multi-Tenancy, Datenisolation |
| Storage | PDF-Anhänge, Profilbilder, Logos |
| Realtime | Live-Updates (News Feed, Teilnehmerlisten) |
| Edge Functions | Komplexe Businesslogik (Einladungs-E-Mails) |

---

## 5. Multi-Tenancy Konzept

### Datenisolation via Row Level Security (RLS)

Jede Innung ist eine `organization`. Alle Tabellen haben eine `org_id` Spalte.

```sql
-- Beispiel RLS Policy für die members-Tabelle
CREATE POLICY "members_isolation" ON members
  FOR ALL
  USING (org_id = (
    SELECT org_id FROM user_roles
    WHERE user_id = auth.uid()
  ));
```

**Prinzip:**
- Kein Nutzer sieht Daten außerhalb seiner `org_id`
- Policy wird für jede Operation (SELECT, INSERT, UPDATE, DELETE) durchgesetzt
- Supabase prüft dies auf Datenbankebene — kein Bypass möglich

### Tenancy Identifikation

- **MVP:** `org_id` wird beim Login aus `user_roles` geladen und in allen Queries mitgegeben
- **Post-MVP:** Subdomain-Routing (`innung-elektro-stuttgart.innungsapp.de`) mit Middleware-Lookup

---

## 6. Authentifizierung

### Login Flow

```
Nutzer gibt E-Mail ein
        │
        ▼
Supabase sendet Magic Link
        │
        ▼
Nutzer klickt Link im E-Mail
        │
        ▼
App/Browser öffnet sich, Token wird verarbeitet
        │
        ▼
Supabase Auth gibt Session zurück (JWT)
        │
        ▼
App lädt user_roles → bestimmt org_id und Rolle
        │
        ▼
Redirect zu korrekter Startseite
```

### Rollen-System

```sql
CREATE TYPE user_role AS ENUM ('admin', 'member', 'public');

CREATE TABLE user_roles (
  id         uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id    uuid REFERENCES auth.users NOT NULL,
  org_id     uuid REFERENCES organizations NOT NULL,
  role       user_role NOT NULL DEFAULT 'member',
  created_at timestamptz DEFAULT now(),
  UNIQUE(user_id, org_id)
);
```

---

## 7. Push Notifications

### Architektur

```
Admin erstellt Beitrag
        │
        ▼
Supabase Edge Function wird getriggert (via Database Webhook)
        │
        ▼
Edge Function fetcht alle Push Tokens der org_id
        │
        ▼
Expo Push Notification Service (EPNS)
        │
     ┌──┴──┐
     ▼      ▼
   APNs    FCM
   (iOS)  (Android)
```

### Push Token Registrierung (Mobile)

```typescript
// hooks/usePushNotifications.ts
async function registerPushToken() {
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== 'granted') return;

  const token = await Notifications.getExpoPushTokenAsync({
    projectId: Constants.expoConfig.extra.eas.projectId,
  });

  await supabase
    .from('push_tokens')
    .upsert({ user_id: user.id, token: token.data });
}
```

---

## 8. Infrastruktur & Kosten

### Monatliche Kosten (MVP, bis 100 Innungen)

| Service | Plan | Kosten/Monat |
|---|---|---|
| Supabase | Pro | 25 € |
| Vercel | Pro | 20 € |
| Resend (E-Mail) | Starter | 0 € (bis 3.000 Mails) |
| Expo EAS Build | Production | 29 € |
| PostHog | Cloud | 0 € (bis 1 Mio. Events) |
| Apple Developer | (jährlich) | 8 € |
| **Gesamt** | | **~82 €/Monat** |

### Skalierung (ab 500 Innungen)

| Service | Plan | Kosten/Monat |
|---|---|---|
| Supabase | Team | 599 € |
| Vercel | Enterprise | ~400 € |
| Resend | Business | 89 € |
| **Gesamt** | | **~1.100 €/Monat** |

Break-even bei 6 zahlenden Innungen à 200 €.

---

## 9. Deployment & CI/CD

### Pipeline

```
git push → GitHub/Gitea
        │
        ├── Mobile: Expo EAS Build (iOS + Android)
        │           └── App Store / Play Store (manual submit)
        │
        └── Web Admin: Vercel Deploy (automatisch)
                    └── Preview URL für jeden Branch
```

### Environments

| Environment | Supabase | Vercel | Verwendung |
|---|---|---|---|
| `development` | Lokal (Docker) | localhost:3000 | Entwicklung |
| `staging` | Staging-Projekt | staging.innungsapp.de | Pilot-Tests |
| `production` | Pro-Projekt | app.innungsapp.de | Live |
