Files
AFC-Demo/docs/architecture-overview.md
T

1011 lines
37 KiB
Markdown
Raw Normal View History

2025-12-03 12:59:30 +01:00
# System-Architektur: Risk Intelligence Platform
## 📋 Inhaltsverzeichnis
1. [Übersicht](#übersicht)
2. [System-Diagramm](#system-diagramm)
3. [Technologie-Stack](#technologie-stack)
4. [Datenbank-Architektur](#datenbank-architektur)
5. [Datenfluss & ETL-Pipeline](#datenfluss--etl-pipeline)
6. [Applikations-Schichten](#applikations-schichten)
7. [Frontend-Architektur](#frontend-architektur)
8. [Backend-Integration](#backend-integration)
9. [Scheduling & Automation](#scheduling--automation)
10. [Sicherheit & Authentication](#sicherheit--authentication)
11. [Deployment-Architektur](#deployment-architektur)
---
## Übersicht
Die **Risk Intelligence Platform** ist eine Laravel-basierte Web-Applikation zur Analyse und Bewertung von Unternehmenstransaktionen hinsichtlich KYC (Know Your Customer) und Compliance-Risiken.
### Kernfunktionen
-**Unternehmenssuche & -bewertung** mit KYC Risk Levels
-**Transaktionsanalyse** mit 139 Datenfeldern (37 Core + 102 JSONB)
-**Automatische Backend-Synchronisation** (ETL-Pipeline)
-**Risk Score Berechnung** basierend auf Multi-Faktoren-Analyse
-**Compliance-Checks** (EU/OFAC/UK Sanctions, PEP, AML)
-**Benutzer-Authentication** mit 2FA-Unterstützung
### Zielgruppe
- Compliance-Teams
- Risk Analysts
- KYC-Spezialisten
---
## System-Diagramm
```
┌─────────────────────────────────────────────────────────────────────────┐
│ FRONTEND (Browser) │
│ Livewire Volt Components + Flux UI │
└─────────────────────────┬───────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ LARAVEL APPLICATION │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │
│ │ Controllers │ │ Livewire Volt │ │ API Endpoints │ │
│ │ (Minimal) │ │ Components │ │ (Future) │ │
│ └────────┬────────┘ └────────┬────────┘ └─────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Eloquent Models & Repositories │ │
│ │ Company │ Transaction │ User │ │
│ └──────────────────┬──────────────────────────┘ │
│ │ │
│ ┌─────────┴─────────┐ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Jobs/Queue │ │ Services │ │
│ │ - SyncDataPool │ │ - KycRiskCalc │ │
│ │ - Transform │ │ - DataExtract │ │
│ └──────────────────┘ └──────────────────┘ │
└────────────┬────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ POSTGRESQL DATABASE (Port 5433) │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Schema: PUBLIC (Default Connection) │ │
│ │ ┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ │
│ │ │ companies │ │ transactions │ │ backend_data_ │ │ │
│ │ │ │ │ (139 cols) │ │ pool │ │ │
│ │ │ - name │◄─┤ - company_id │ │ │ │ │
│ │ │ - kyc_risk │ │ - risk_score │ │ - transaction_ │ │ │
│ │ │ - sector │ │ - 102 JSONB cols│ │ id │ │ │
│ │ └─────────────┘ └──────────────────┘ │ - output_key │ │ │
│ │ │ - content │ │ │
│ │ └─────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Schema: BACKEND (Backend Connection) │ │
│ │ ┌──────────────────┐ ┌──────────────────────────────────┐ │ │
│ │ │ transactions │ │ transaction_outputs │ │ │
│ │ │ │ │ │ │ │
│ │ │ - id │◄─┤ - transaction_id │ │ │
│ │ │ - status │ │ - prompt_id │ │ │
│ │ │ - tx_amount │ │ - output_key │ │ │
│ │ │ - risk_score │ │ - content (JSONB) │ │ │
│ │ └──────────────────┘ └──────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│ (ETL Pipeline - Scheduled Jobs)
┌────────────┴────────────────────────────────────────────────────────────┐
│ LARAVEL SCHEDULER (Cron) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Every 6h (0:00, 6:00, 12:00, 18:00): │ │
│ │ → SyncBackendDataPool (Incremental) │ │
│ │ │ │
│ │ Every 6h (0:30, 6:30, 12:30, 18:30): │ │
│ │ → TransformDataPoolToProduction │ │
│ │ │ │
│ │ Sunday 3:00: │ │
│ │ → SyncBackendDataPool (Full Rebuild) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Technologie-Stack
### Backend
| Komponente | Version | Zweck |
|-----------|---------|-------|
| **PHP** | 8.4.14 | Runtime |
| **Laravel Framework** | 12.x | Application Framework |
| **PostgreSQL** | Latest | Primäre Datenbank |
| **Laravel Fortify** | 1.x | Authentication |
| **Livewire** | 3.x | Reactive Components |
| **Livewire Volt** | 1.x | Single-File Components |
### Frontend
| Komponente | Version | Zweck |
|-----------|---------|-------|
| **Flux UI** | 2.x (Free) | Component Library |
| **Tailwind CSS** | 4.x | Styling Framework |
| **Alpine.js** | Latest (via Livewire) | JavaScript Interactivity |
| **Vite** | Latest | Asset Bundling |
### Development & Testing
| Komponente | Version | Zweck |
|-----------|---------|-------|
| **Pest** | 4.x | Testing Framework |
| **PHPUnit** | 12.x | Unit Testing |
| **Laravel Pint** | 1.x | Code Formatting |
| **Laravel Sail** | 1.x | Docker Development |
| **Laravel Boost** | 1.4+ | MCP Server (Development) |
### Infrastructure
- **Docker** - Containerization
- **Kubernetes** - Orchestration (k8s-cronjob.yaml)
- **Cron** - Scheduling (Laravel Scheduler)
- **Queue System** - Asynchronous Jobs
---
## Datenbank-Architektur
### Verbindungen
Die Applikation nutzt **eine PostgreSQL-Datenbank** mit **zwei Schemas**:
```php
// config/database.php
'default' => 'pgsql_second', // Default: public schema
'connections' => [
// Frontend Schema (Production Tables)
'pgsql_second' => [
'search_path' => 'public',
'port' => env('DB_PORT2', 5432),
'database' => env('DB_DATABASE2'),
],
// Backend Schema (Source Data)
'backend' => [
'search_path' => 'backend',
'port' => env('DB_PORT2', 5432),
'database' => env('DB_DATABASE2'), // Same DB, different schema
],
]
```
### Schema: PUBLIC (Production)
#### Companies Tabelle
```sql
CREATE TABLE public.companies (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
legal_name VARCHAR(255),
ticker VARCHAR(50),
sector VARCHAR(100),
country VARCHAR(2), -- ISO 3166-1 alpha-2
headquarters TEXT,
kyc_risk_level VARCHAR(20), -- low | high | critical
summary TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
```
#### Transactions Tabelle (139 Spalten)
**Core Felder (37):**
```sql
CREATE TABLE public.transactions (
-- Identity & Relationships
id SERIAL PRIMARY KEY,
company_id INTEGER REFERENCES companies(id),
reference VARCHAR(255) UNIQUE NOT NULL,
-- Transaction Details
amount DECIMAL(15,2),
currency VARCHAR(3),
counterparty VARCHAR(255),
counterparty_country VARCHAR(2),
channel VARCHAR(50),
executed_at TIMESTAMP,
-- Risk Assessment
risk_score INTEGER, -- 0-255 normalized
status VARCHAR(50), -- true_positive | false_positive | cleared
requires_review BOOLEAN,
flagged_by VARCHAR(255),
flagged_reason TEXT,
signals JSONB,
-- Additional Entity Data
entity VARCHAR(255), -- Corporate entity name
counterparty_kyc_risk_level VARCHAR(20),
-- Risk Breakdown
transaction_risk_score INTEGER,
sanctions_risk_score INTEGER,
country_risk_score INTEGER,
pep_adverse_risk_score INTEGER,
corruption_risk_score INTEGER,
-- Timestamps
created_at TIMESTAMP,
updated_at TIMESTAMP,
-- ... 102 JSONB Columns (siehe unten)
);
```
**JSONB Output Columns (102):**
Alle `output_key` Werte aus `backend.transaction_outputs` werden als JSONB-Spalten gespeichert:
<details>
<summary>Vollständige Liste der 102 JSONB Spalten (klicken zum Ausklappen)</summary>
**Corporate Information:**
- `corporate_summary`
- `corporate_history`
- `corporate_legalname`
- `corporate_sector`
- `corporate_HQ`
- `corporate_keyexecs`
- `corporate_shareholding`
- `corporate_parentcompany`
- `corporate_subsidiaries`
- `corporate_countryofincorporation`
- `corporate_ticker`
- `corporate_counterparty`
- `corporate_counterpartysector`
- `corporate_counterpartycountry`
- `corporate_counterpartysummary`
- `corporate_counterpartyhistory`
**Sanctions & Compliance:**
- `corporate_eusanctions`
- `corporate_ofacsanctions`
- `corporate_uksanctions`
- `corporate_pepexposure`
- `corporate_AMLexposure`
- `corporate_adverse`
- `sanctions_circumvention`
**Country Risk:**
- `country_risk`
- `country_name`
- `country_iso2`
- `country_iso3`
- `country_cpi`
- `country_euprivacy`
- `country_dataprivacy`
- `country_gdpr`
- `country_sanctions`
- `country_wgi`
- `country_pep`
**Corruption:**
- `corruption_sector`
- `corruption_country`
- `corruption_relationship`
**Financial & Transaction:**
- `tranx_score`
- `tranx_reasoning`
- `tranx_amount`
- `tranx_currency`
- `tranx_date`
- `tranx_counterparty`
- `tranx_channel`
- `financial_assessment`
... und weitere 60+ Spalten für detaillierte Analysen
</details>
#### Backend Data Pool Tabelle
```sql
CREATE TABLE public.backend_data_pool (
id SERIAL PRIMARY KEY,
-- Foreign Keys (Backend Schema)
transaction_id INTEGER NOT NULL,
prompt_id INTEGER NOT NULL,
-- Transaction Core Data
corporate_entity VARCHAR(255),
tx_amount DECIMAL(15,2),
tx_currency VARCHAR(3),
tx_date DATE,
status VARCHAR(50),
-- Risk Scores
risk_score INTEGER, -- Backend risk_score (-100 to 100)
-- Output Data
output_key VARCHAR(255),
content JSONB,
-- Metadata
synced_at TIMESTAMP DEFAULT NOW(),
UNIQUE(transaction_id, prompt_id)
);
CREATE INDEX idx_backend_data_pool_transaction_id ON backend_data_pool(transaction_id);
CREATE INDEX idx_backend_data_pool_output_key ON backend_data_pool(output_key);
```
### Schema: BACKEND (Source Data)
#### Backend Transactions
```sql
CREATE TABLE backend.transactions (
id SERIAL PRIMARY KEY,
corporate_entity VARCHAR(255),
tx_amount DECIMAL(15,2),
tx_currency VARCHAR(3),
tx_date DATE,
status VARCHAR(50), -- pending | processing | done | failed
risk_score INTEGER, -- -100 to 100 (Backend Scale)
last_modified_at TIMESTAMP,
created_at TIMESTAMP
);
```
#### Backend Transaction Outputs
```sql
CREATE TABLE backend.transaction_outputs (
id SERIAL PRIMARY KEY,
transaction_id INTEGER REFERENCES transactions(id),
prompt_id INTEGER,
output_key VARCHAR(255), -- z.B. "corporate_summary", "tranx_score"
content JSONB, -- LLM-Generated Output
created_at TIMESTAMP,
UNIQUE(transaction_id, prompt_id)
);
```
---
## Datenfluss & ETL-Pipeline
### Dreistufiger Datenprozess
```
┌─────────────────────────────────────────────────────────────┐
│ STUFE 0: Backend Processing (Extern) │
│ │
│ LLM Analysis → backend.transactions │
│ → backend.transaction_outputs │
└─────────────┬───────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STUFE 1: Data Pool Sync │
│ │
│ Job: SyncBackendDataPool │
│ Schedule: Every 6h (Incremental) | Sunday 3am (Full) │
│ │
│ Source: backend.transactions │
│ + backend.transaction_outputs │
│ │
│ Target: public.backend_data_pool │
│ │
│ Process: │
│ 1. Query backend schema (WHERE status='done') │
│ 2. Join transactions + outputs │
│ 3. Batch insert into data pool (1000 records/batch) │
│ 4. Track sync timestamp │
│ │
│ Performance: │
│ - Full Sync: ~30-60s (7,471 records) │
│ - Incremental: ~5-10s (new records only) │
└─────────────┬───────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STUFE 2: Production Transformation │
│ │
│ Job: TransformDataPoolToProduction │
│ Schedule: Every 6h at :30 (30min after Sync) │
│ │
│ Source: public.backend_data_pool │
│ │
│ Targets: public.companies │
│ + public.transactions │
│ │
│ Process: │
│ 1. Group by transaction_id │
│ 2. For each transaction: │
│ a) Extract/Update Company (by corporate_entity) │
│ - Calculate KYC Risk Level │
│ - Enrich with sector, country, HQ │
│ b) Create/Update Transaction │
│ - Map all 139 columns │
│ - Link to company_id │
│ - Transform risk_score (-100..100 → 0..255) │
│ 3. Batch process (100 records/batch) │
│ │
│ Features: │
│ - Idempotent (updateOrCreate) │
│ - Deduplication by reference │
│ - Risk Score Normalization │
│ │
│ Performance: │
│ - Initial: ~30-60s (74 transactions → ~70 companies) │
│ - Updates: ~5-10s │
└─────────────┬───────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STUFE 3: Frontend Display (Real-time) │
│ │
│ Livewire Volt Components query: │
│ - public.companies │
│ - public.transactions │
│ │
│ No additional processing required │
└─────────────────────────────────────────────────────────────┘
```
### Risk Score Transformation
Backend nutzt eine Skala von **-100 bis 100**, Frontend **0 bis 255**:
```php
// Backend → Frontend Normalization
$normalizedScore = (($backendScore + 100) / 200) * 255;
// Beispiele:
// Backend: -100 → Frontend: 0 (Sehr gut)
// Backend: 0 → Frontend: 127 (Neutral)
// Backend: 100 → Frontend: 255 (Sehr schlecht)
```
**Risk Level Mapping:**
| Backend Score | Frontend Score | Risk Level | Transaction Status |
|--------------|----------------|------------|-------------------|
| -100 to 20 | 0 to 153 | `low` | `cleared` |
| 20 to 80 | 153 to 229 | `high` | `false_positive` |
| 80 to 100 | 229 to 255 | `critical` | `true_positive` |
---
## Applikations-Schichten
### Layer Architecture
```
┌──────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ - Livewire Volt Components (resources/views/ │
│ livewire/) │
│ - Blade Templates │
│ - Flux UI Components │
└──────────────────┬───────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ APPLICATION LAYER │
│ - Controllers (minimal, mostly Volt) │
│ - Livewire Actions (app/Livewire/Actions/) │
│ - Form Requests (validation) │
└──────────────────┬───────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ DOMAIN LAYER │
│ - Eloquent Models (app/Models/) │
│ · Company │
│ · Transaction │
│ · User │
│ · Backend\Transaction │
│ · Backend\TransactionOutput │
│ - Repositories (app/Repositories/) │
│ · TransactionRepository │
│ - Services (app/Services/) │
│ · KycRiskCalculator │
└──────────────────┬───────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ INFRASTRUCTURE LAYER │
│ - Jobs (app/Jobs/) │
│ · SyncBackendDataPool │
│ · TransformDataPoolToProduction │
│ - Commands (app/Console/Commands/) │
│ · SyncBackendDataPoolCommand │
│ · TransformDataPoolCommand │
│ · RebuildFromDataPoolCommand │
│ - Database Migrations │
│ - Schedulers (routes/console.php) │
└──────────────────────────────────────────────────────┘
```
---
## Frontend-Architektur
### Livewire Volt Components
Die Applikation nutzt **Single-File Components** mit Livewire Volt:
```php
// resources/views/livewire/company-search.blade.php
<?php
use Livewire\Volt\Component;
use App\Models\Company;
new class extends Component {
public string $search = '';
public function with(): array
{
return [
'companies' => Company::query()
->when($this->search, fn($q) =>
$q->where('name', 'ilike', "%{$this->search}%")
)
->limit(20)
->get(),
];
}
}
?>
<div>
<flux:input wire:model.live.debounce.300ms="search" />
<!-- UI -->
</div>
```
### Routen-Struktur
```php
// routes/web.php
// Public
Route::get('/', fn() => view('welcome'))->name('home');
// Authenticated Routes
Route::middleware(['auth', 'verified'])->group(function () {
Route::view('dashboard', 'dashboard')->name('dashboard');
// Volt Routes
Volt::route('company-search', 'company-search')->name('company-search');
Volt::route('transaction-review', 'transaction-review')->name('transaction-review');
Volt::route('companies/{company}/transactions', 'companies.transactions')
->name('company.transactions');
Volt::route('upload', 'upload.index')->name('upload');
// Settings
Volt::route('settings/profile', 'settings.profile')->name('profile.edit');
Volt::route('settings/password', 'settings.password')->name('password.edit');
Volt::route('settings/two-factor', 'settings.two-factor')
->middleware(['password.confirm'])
->name('two-factor.show');
});
```
### Hauptseiten
| Route | Component | Beschreibung |
|-------|-----------|--------------|
| `/` | welcome.blade.php | Landing Page |
| `/dashboard` | dashboard.blade.php | Dashboard-Übersicht |
| `/company-search` | company-search.blade.php | Unternehmenssuche |
| `/transaction-review` | transaction-review.blade.php | Transaktionsübersicht |
| `/companies/{id}/transactions` | companies.transactions.blade.php | Company-Detail mit Transactions |
| `/upload` | upload.index.blade.php | Upload-Funktionalität |
---
## Backend-Integration
### Models & Relationships
```php
// app/Models/Company.php
class Company extends Model
{
protected $fillable = [
'name', 'legal_name', 'ticker', 'sector',
'country', 'headquarters', 'kyc_risk_level', 'summary',
];
public function transactions(): HasMany
{
return $this->hasMany(Transaction::class);
}
}
// app/Models/Transaction.php
class Transaction extends Model
{
protected $guarded = []; // Allow all (for 102 JSONB columns)
// Dynamic JSONB casting
protected function casts(): array
{
$allColumns = Schema::getColumnListing('transactions');
$jsonbColumns = array_diff($allColumns, $standardColumns);
foreach ($jsonbColumns as $column) {
$casts[$column] = 'array';
}
return $casts;
}
public function company(): BelongsTo
{
return $this->belongsTo(Company::class);
}
}
```
### Jobs
#### SyncBackendDataPool
```php
// app/Jobs/SyncBackendDataPool.php
class SyncBackendDataPool implements ShouldQueue
{
public function __construct(
public bool $fullSync = false,
public int $batchSize = 1000,
) {}
public function handle(): void
{
if ($this->fullSync) {
DB::table('backend_data_pool')->truncate();
}
$query = DB::connection('pgsql')
->table('backend.transactions as t')
->join('backend.transaction_outputs as tout', 't.id', '=', 'tout.transaction_id')
->where('t.status', 'done')
->select([
't.id as transaction_id',
'tout.prompt_id',
't.corporate_entity',
't.tx_amount',
't.tx_currency',
't.tx_date',
't.status',
't.risk_score',
'tout.output_key',
'tout.content',
]);
$query->chunk($this->batchSize, function ($records) {
DB::table('backend_data_pool')->insert(
$records->map(fn($r) => [
...(array) $r,
'synced_at' => now(),
])->toArray()
);
});
}
}
```
#### TransformDataPoolToProduction
```php
// app/Jobs/TransformDataPoolToProduction.php
class TransformDataPoolToProduction implements ShouldQueue
{
public function handle(): void
{
$transactionIds = DB::table('backend_data_pool')
->distinct()
->pluck('transaction_id');
foreach ($transactionIds as $transactionId) {
$outputs = DB::table('backend_data_pool')
->where('transaction_id', $transactionId)
->get();
$company = $this->createOrUpdateCompany($outputs);
$this->createOrUpdateTransaction($company, $outputs);
}
}
private function createOrUpdateCompany($outputs): Company
{
$first = $outputs->first();
return Company::updateOrCreate(
['name' => $first->corporate_entity],
[
'sector' => $this->extractValue($outputs, 'corporate_sector'),
'country' => $this->extractValue($outputs, 'corporate_countryofincorporation'),
'headquarters' => $this->extractValue($outputs, 'corporate_HQ'),
'kyc_risk_level' => $this->calculateKycRiskLevel($first->risk_score),
'summary' => $this->extractValue($outputs, 'corporate_summary'),
]
);
}
}
```
### Commands
```bash
# Manuelle Sync-Commands
php artisan backend:sync-data-pool --full
php artisan backend:sync-data-pool --incremental
php artisan backend:transform-data-pool
php artisan backend:rebuild # Full Sync + Transform
```
---
## Scheduling & Automation
### Laravel Scheduler Configuration
```php
// routes/console.php
// Incremental Sync: 0:00, 6:00, 12:00, 18:00
Schedule::job(new SyncBackendDataPool(fullSync: false, batchSize: 1000))
->everySixHours()
->name('sync-backend-data-pool-incremental')
->withoutOverlapping(1800);
// Full Sync: Sonntag 3:00 Uhr
Schedule::job(new SyncBackendDataPool(fullSync: true, batchSize: 1000))
->weeklyOn(0, '03:00')
->name('sync-backend-data-pool-full')
->withoutOverlapping(3600);
// Transformation: 0:30, 6:30, 12:30, 18:30
Schedule::job(new TransformDataPoolToProduction(batchSize: 100))
->cron('30 */6 * * *')
->name('transform-data-pool-to-production')
->withoutOverlapping(1800);
```
### Aktivierung
#### Lokale Entwicklung
```bash
php artisan schedule:work
```
#### Produktion (Crontab)
```bash
crontab -e
# Füge hinzu:
* * * * * cd /path/to/project && php artisan schedule:run >> /dev/null 2>&1
```
#### Mit Supervisor
```ini
[program:laravel-scheduler]
command=php /path/to/project/artisan schedule:work
autostart=true
autorestart=true
```
---
## Sicherheit & Authentication
### Laravel Fortify
Die Applikation nutzt **Laravel Fortify** für Authentication:
```php
// config/fortify.php - Enabled Features
Features::registration(),
Features::resetPasswords(),
Features::emailVerification(),
Features::updateProfileInformation(),
Features::updatePasswords(),
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]),
```
### Two-Factor Authentication (2FA)
- QR-Code basierte 2FA mit TOTP
- Recovery Codes
- Passwort-Bestätigung erforderlich
### Authorization
- Route Middleware: `auth`, `verified`
- Password Confirmation für sensible Aktionen
### Session Management
- Standard Laravel Session-Driver
- CSRF Protection aktiviert
---
## Deployment-Architektur
### Docker Setup
```yaml
# docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- .:/var/www/html
environment:
- DB_CONNECTION2=pgsql
- DB_HOST2=db
- DB_PORT2=5432
```
### Kubernetes CronJob
```yaml
# k8s-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: backend-sync
spec:
schedule: "0 */6 * * *" # Every 6 hours
jobTemplate:
spec:
template:
spec:
containers:
- name: sync
image: risk-platform:latest
command: ["php", "artisan", "backend:sync-data-pool", "--incremental"]
```
### Environment Configuration
```bash
# .env (Production)
APP_ENV=production
APP_DEBUG=false
DB_CONNECTION2=pgsql
DB_HOST2=postgres.internal
DB_PORT2=5432
DB_DATABASE2=risk_platform_db
DB_USERNAME2=risk_user
DB_PASSWORD2=***
CACHE_STORE=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
```
### Scaling Considerations
**Horizontal Scaling:**
- Stateless Laravel App → Load Balancer → Multiple App Instances
- Shared Redis für Sessions/Cache
- Shared PostgreSQL Cluster
**Performance Optimization:**
- Redis Caching für Companies/Transactions
- Database Indexing (transaction_id, output_key, company_id)
- Query Optimization mit Eager Loading
- CDN für Static Assets
---
## Monitoring & Logging
### Laravel Logs
```bash
# Application Logs
tail -f storage/logs/laravel.log
# Nur Sync-Logs
tail -f storage/logs/laravel.log | grep SyncBackendDataPool
# Nur Transform-Logs
tail -f storage/logs/laravel.log | grep TransformDataPoolToProduction
```
### Queue Monitoring
```bash
php artisan queue:work --tries=3
php artisan queue:monitor
```
### Database Performance Monitoring
```sql
-- Sync-Performance (Records pro Minute)
SELECT
DATE_TRUNC('minute', synced_at) as minute,
COUNT(*) as records_synced
FROM public.backend_data_pool
WHERE synced_at >= NOW() - INTERVAL '1 hour'
GROUP BY DATE_TRUNC('minute', synced_at)
ORDER BY minute DESC;
```
### Optional: Laravel Telescope
```bash
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
```
---
## Zusammenfassung
### System-Eigenschaften
**Monolithische Laravel-Applikation** mit klarer Layer-Trennung
**ETL-Pipeline** mit zweistufiger Transformation (Sync → Transform)
**Automatisierung** via Laravel Scheduler (alle 6h)
**Reactive Frontend** mit Livewire Volt + Flux UI
**Skalierbar** via Queue-System und horizontaler App-Skalierung
**Sicher** mit Fortify Authentication + 2FA
**Testbar** mit Pest Testing Framework
### Performance-Metriken
| Operation | Datenmenge | Dauer | Frequenz |
|-----------|-----------|-------|----------|
| Incremental Sync | ~100-500 Records | 5-10s | Alle 6h |
| Full Sync | 7,471 Records | 30-60s | Sonntag 3am |
| Transformation | 74 Transactions | 30-60s | Alle 6h (30min nach Sync) |
| Page Load (Companies) | 20 Records | <100ms | On Demand |
### Nächste Schritte
- [ ] API-Dokumentation (OpenAPI/Swagger)
- [ ] Testing-Guide & Coverage-Ziele
- [ ] Deployment-Runbooks
- [ ] Entwickler-Onboarding-Guide
- [ ] Performance-Optimierung (Caching-Strategie)
---
**Erstellt:** 2025-11-24
**Version:** 1.0
**Autor:** Risk Intelligence Platform Team