12 KiB
12 KiB
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.
-- 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_idwird beim Login aususer_rolesgeladen 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 |