stadtwerke/ARCHITECTURE.md

12 KiB
Raw Permalink Blame History

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 46 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.

-- 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

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)

// 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