394 lines
15 KiB
Markdown
394 lines
15 KiB
Markdown
|
|
# AAMOS Infrastrukturanalys — Del 2
|
||
|
|
## LandveX Enterprise Platform — Återanvändningsbarhet & Anpassningskostnad
|
||
|
|
|
||
|
|
**Datum:** 2026-07-02
|
||
|
|
**Analytiker:** Subagent (AAMOS Infrastructure Analysis)
|
||
|
|
**Status:** Fortsättning på Del 1 (Auth/Identity Service: 5/5 ⭐, Låg anpassningskostnad)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Sammanfattning
|
||
|
|
|
||
|
|
| Komponent | Nuvarande användning | Återanvändningsbarhet | Anpassningskostnad |
|
||
|
|
|-----------|---------------------|----------------------|-------------------|
|
||
|
|
| **1. API-gateway** | Nginx reverse proxy + Rust-tjänster | ⭐⭐⭐⭐⭐ (5/5) | **Låg** |
|
||
|
|
| **2. Databaslager** | PostgreSQL + Redis + PostGIS | ⭐⭐⭐⭐ (4/5) | **Låg** |
|
||
|
|
| **3. Event-buss** | Hermes (Redis pub/sub + JSONL fallback) | ⭐⭐⭐⭐⭐ (5/5) | **Låg** |
|
||
|
|
| **4. Frontend-ramverk** | React 19 + Vite + Tailwind (Ouroboros) | ⭐⭐⭐ (3/5) | **Medium** |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. API-gateway
|
||
|
|
|
||
|
|
### Nuvarande användning
|
||
|
|
|
||
|
|
AAMOS använder **Nginx** som central reverse proxy och API-gateway för alla 28+ Rust-tjänster:
|
||
|
|
|
||
|
|
```
|
||
|
|
Nginx (amos.wavult.com)
|
||
|
|
├── /api/svc/rule-engine/ → port 3201
|
||
|
|
├── /api/svc/api-gateway/ → port 3206 (intern gateway)
|
||
|
|
├── /api/svc/ledger/ → port 3250
|
||
|
|
├── /api/svc/bank-import/ → port 3253
|
||
|
|
├── /api/svc/sie-import/ → port 3254
|
||
|
|
├── /api/svc/people-core/ → port 3271
|
||
|
|
├── /api/svc/operations-core/ → port 3272
|
||
|
|
├── /api/svc/commerce-rust/ → port 3280
|
||
|
|
├── /api/svc/analytics-rust/ → port 3281
|
||
|
|
├── /api/svc/crm-rust/ → port 3282
|
||
|
|
├── /api/svc/campaign-rust/ → port 3283
|
||
|
|
├── /api/svc/hr-rust/ → port 3284
|
||
|
|
├── /api/svc/payroll-rust/ → port 3285
|
||
|
|
├── /api/svc/invoice-rust/ → port 3286
|
||
|
|
├── /api/svc/compliance-rust/ → port 3287
|
||
|
|
├── /api/svc/landvex-engine/ → port 3391
|
||
|
|
├── /api/svc/quixzoom-engine/ → port 3390
|
||
|
|
└── ... (28 tjänster totalt)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Konfigurationsfiler:**
|
||
|
|
- `/etc/nginx/conf.d/aamos-upstreams.conf` — upstream-blocks
|
||
|
|
- `/etc/nginx/snippets/rust-services.conf` — location-blocks
|
||
|
|
- URL-prefix: `/api/svc/<service>/`
|
||
|
|
|
||
|
|
### Återanvändningsbarhet: ⭐⭐⭐⭐⭐ (5/5)
|
||
|
|
|
||
|
|
**Varför hög återanvändningsbarhet:**
|
||
|
|
|
||
|
|
1. **Standardiserat mönster** — Varje tjänst följer samma konvention: `/api/svc/<namn>/` → `<namn>.wavult.com:<port>`
|
||
|
|
2. **Ingen affärslogik i gateway** — Nginx gör endast routing, SSL-terminering och rate limiting. All affärslogik finns i tjänsterna.
|
||
|
|
3. **Deklarativ konfiguration** — Nya tjänster läggs till med två rader (upstream + location)
|
||
|
|
4. **Health checks** — Varje tjänst exponerar `/health` som Nginx kan använda
|
||
|
|
5. **TLS/SSL centraliserat** — Certifikat hanteras på ett ställe
|
||
|
|
|
||
|
|
**Exempel på återanvändning:**
|
||
|
|
```nginx
|
||
|
|
# Lägg till ny tjänst (2 rader)
|
||
|
|
upstream rust_new_service {
|
||
|
|
server localhost:3400;
|
||
|
|
}
|
||
|
|
|
||
|
|
location /api/svc/new-service/ {
|
||
|
|
proxy_pass http://rust_new_service/;
|
||
|
|
proxy_set_header Host $host;
|
||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Anpassningskostnad: **Låg**
|
||
|
|
|
||
|
|
| Aspekt | Kostnad | Kommentar |
|
||
|
|
|--------|---------|-----------|
|
||
|
|
| **Ny tjänst** | ~5 min | Kopiera befintligt block, ändra port |
|
||
|
|
| **Ny domän** | ~15 min | SSL-cert + server block |
|
||
|
|
| **Rate limiting** | ~10 min | Nginx `limit_req_zone` |
|
||
|
|
| **Auth** | ~30 min | JWT-validering i Nginx (lua) eller i tjänsterna |
|
||
|
|
| **Load balancing** | ~20 min | Flera instanser i upstream |
|
||
|
|
|
||
|
|
**Risker:**
|
||
|
|
- Nginx-konfigurationen växer med varje tjänst (28+ redan)
|
||
|
|
- Ingen service discovery — alla tjänster är hårdkodade
|
||
|
|
- Ingen centraliserad API-dokumentation (Swagger/OpenAPI)
|
||
|
|
|
||
|
|
**Rekommendation:** Behåll nuvarande lösning. Vid >50 tjänster, överväg en dynamisk service registry (Consul/Etcd).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Databaslager
|
||
|
|
|
||
|
|
### Nuvarande användning
|
||
|
|
|
||
|
|
AAMOS använder en **polyglot persistence**-strategi:
|
||
|
|
|
||
|
|
| Databas | Användning | Tjänster |
|
||
|
|
|---------|-----------|----------|
|
||
|
|
| **PostgreSQL** | Primär transaktionsdatabas | aamos-ledger, people-core, operations-core |
|
||
|
|
| **Redis** | Cache + Event bus (Hermes) | Alla tjänster |
|
||
|
|
| **PostGIS** | Geografisk data (implicit via PostgreSQL) | quixzoom-engine, landvex-engine |
|
||
|
|
| **ClickHouse** | Analys och rapportering | aamos-analytics-engine |
|
||
|
|
|
||
|
|
**PostgreSQL-schema (aamos-ledger):**
|
||
|
|
```sql
|
||
|
|
-- Kärntabeller
|
||
|
|
ledger_accounts -- Kontoplan (BAS 2024)
|
||
|
|
ledger_journal_entries -- Verifikationer (IMMUTABLE)
|
||
|
|
ledger_journal_lines -- Kontolinjer (dubbel bokföring)
|
||
|
|
ledger_periods -- Perioder (open/closed)
|
||
|
|
ledger_audit_log -- Audit trail (append-only)
|
||
|
|
ledger_reconciliation_items -- Kontoavstämning
|
||
|
|
```
|
||
|
|
|
||
|
|
### Återanvändningsbarhet: ⭐⭐⭐⭐ (4/5)
|
||
|
|
|
||
|
|
**Varför hög återanvändningsbarhet:**
|
||
|
|
|
||
|
|
1. **Standardiserat schema** — Alla tjänster följer samma mönster: `tenant_id`, `trace_id`, `created_at`
|
||
|
|
2. **Multi-tenant design** — `tenant_id` på varje rad möjliggör SaaS-modell
|
||
|
|
3. **Audit-first** — Varje operation loggas i `ledger_audit_log`
|
||
|
|
4. **JSONB för flexibilitet** — `metadata`-fält möjliggör utökning utan migrationer
|
||
|
|
5. **BAS 2024 standard** — Svensk kontoplan kan bytas mot IFRS/US GAAP
|
||
|
|
|
||
|
|
**Exempel på återanvändning (ny tjänst):**
|
||
|
|
```sql
|
||
|
|
-- Samma mönster för ny tjänst
|
||
|
|
CREATE TABLE new_service_entities (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
tenant_id TEXT NOT NULL,
|
||
|
|
trace_id TEXT NOT NULL,
|
||
|
|
user_id TEXT NOT NULL,
|
||
|
|
data JSONB NOT NULL DEFAULT '{}',
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Anpassningskostnad: **Låg**
|
||
|
|
|
||
|
|
| Aspekt | Kostnad | Kommentar |
|
||
|
|
|--------|---------|-----------|
|
||
|
|
| **Ny tenant** | ~1 min | `INSERT INTO tenants ...` |
|
||
|
|
| **Ny kontoplan** | ~2 timmar | Kopiera BAS 2024, anpassa |
|
||
|
|
| **Ny tabell** | ~15 min | Följ befintligt mönster |
|
||
|
|
| **Migration** | ~30 min | `ALTER TABLE` eller ny tabell |
|
||
|
|
| **Backup/restore** | ~10 min | `pg_dump` / `pg_restore` |
|
||
|
|
| **PostGIS** | ~1 timme | Aktivera extension, spatiala index |
|
||
|
|
|
||
|
|
**Risker:**
|
||
|
|
- PostgreSQL är single-node (ingen replikering dokumenterad)
|
||
|
|
- ClickHouse är separat — data dupliceras potentiellt
|
||
|
|
- Ingen connection pooling (PgBouncer) dokumenterad
|
||
|
|
- `ledger_journal_entries` växer obegränsat — partitionering behövs vid skala
|
||
|
|
|
||
|
|
**Rekommendation:**
|
||
|
|
- Sätt upp PostgreSQL-replikering (primary-replica) för läs-skala
|
||
|
|
- Överväg PgBouncer vid >100 samtidiga anslutningar
|
||
|
|
- Partitionera `ledger_journal_entries` per `fiscal_year`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Event-buss (Hermes)
|
||
|
|
|
||
|
|
### Nuvarande användning
|
||
|
|
|
||
|
|
**Hermes** är AAMOS event fabric — en thin adapter mellan tjänster:
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────┐
|
||
|
|
│ HERMES EVENT FABRIC │
|
||
|
|
├─────────────────────────────────────────────────────────────┤
|
||
|
|
│ Transport: Redis pub/sub (primär) │
|
||
|
|
│ Fallback: JSONL-fil (/tmp/hermes/events.jsonl) │
|
||
|
|
│ Kanal: aamos:hermes │
|
||
|
|
├─────────────────────────────────────────────────────────────┤
|
||
|
|
│ Event envelope: │
|
||
|
|
│ { id, trace_id, correlation_id, event_type, source, │
|
||
|
|
│ tenant_id, user_id, entity_type, entity_id, │
|
||
|
|
│ decision_source, payload, ts, schema_version } │
|
||
|
|
└─────────────────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
**Användning i aamos-ledger:**
|
||
|
|
```javascript
|
||
|
|
// Vid varje operation
|
||
|
|
await hermes.emit('finance.journal.posted', ctx, {
|
||
|
|
entry_id: id, entry_number, period, fiscal_year
|
||
|
|
});
|
||
|
|
|
||
|
|
await hermes.emit('finance.account.created', ctx, {
|
||
|
|
account_number, name, coa_standard
|
||
|
|
});
|
||
|
|
|
||
|
|
await hermes.emit('finance.period.closed', ctx, {
|
||
|
|
period, fiscal_year, closed_by
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Återanvändningsbarhet: ⭐⭐⭐⭐⭐ (5/5)
|
||
|
|
|
||
|
|
**Varför maximal återanvändningsbarhet:**
|
||
|
|
|
||
|
|
1. **Universal event envelope** — Samma schema för ALLA händelser oavsett tjänst
|
||
|
|
2. **Trace propagation** — `trace_id` och `correlation_id` följer genom hela systemet
|
||
|
|
3. **Tenant isolation** — `tenant_id` i varje event säkerställer multi-tenant
|
||
|
|
4. **Decision source** — `'user'|'system'|'agent'` möjliggör audit och AI-spårning
|
||
|
|
5. **Fallback-arkitektur** — Redis nere? JSONL-fil garanterar durabilitet
|
||
|
|
6. **Schema versioning** — `schema_version: '1.0'` möjliggör evolution
|
||
|
|
|
||
|
|
**Exempel på återanvändning (ny tjänst):**
|
||
|
|
```javascript
|
||
|
|
import hermes from './hermes.mjs';
|
||
|
|
|
||
|
|
// Publicera
|
||
|
|
await hermes.emit('crm.deal.won', ctx, { deal_id, value, customer });
|
||
|
|
|
||
|
|
// Prenumerera
|
||
|
|
await hermes.subscribe((event) => {
|
||
|
|
if (event.event_type === 'finance.journal.posted') {
|
||
|
|
// Uppdatera CRM med betalningsstatus
|
||
|
|
}
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Anpassningskostnad: **Låg**
|
||
|
|
|
||
|
|
| Aspekt | Kostnad | Kommentar |
|
||
|
|
|--------|---------|-----------|
|
||
|
|
| **Nytt event** | ~2 min | `hermes.emit('domain.action', ctx, payload)` |
|
||
|
|
| **Ny prenumerant** | ~5 min | `hermes.subscribe(handler)` |
|
||
|
|
| **Ny kanal** | ~5 min | Ändra `HERMES_CHANNEL` env-var |
|
||
|
|
| **Redis-cluster** | ~1 timme | Konfigurera sentinel/cluster |
|
||
|
|
| **Kafka-migrering** | ~1 dag | Byta transport, behålla envelope |
|
||
|
|
|
||
|
|
**Risker:**
|
||
|
|
- Redis pub/sub är fire-and-forget (ingen persistens utanför JSONL)
|
||
|
|
- Inga dead-letter queues dokumenterade
|
||
|
|
- Ingen event replay-mekanism
|
||
|
|
- JSONL-filen växer obegränsat — rotation behövs
|
||
|
|
|
||
|
|
**Rekommendation:**
|
||
|
|
- Sätt upp log rotation för JSONL-filen
|
||
|
|
- Överväg Redis Streams (istället för pub/sub) för persistens
|
||
|
|
- Dokumentera event-katalog (alla event_type som används)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Frontend-ramverk (Ouroboros)
|
||
|
|
|
||
|
|
### Nuvarande användning
|
||
|
|
|
||
|
|
**Ouroboros** är LandveX Fortnox-klon — ett React-baserat webbgränssnitt:
|
||
|
|
|
||
|
|
```
|
||
|
|
Ouroboros Frontend
|
||
|
|
├── React 19.2.7
|
||
|
|
├── Vite 8.1.0 (build tool)
|
||
|
|
├── TypeScript 6.0.2
|
||
|
|
├── Tailwind CSS 4.3.1
|
||
|
|
├── React Router 7.18.0
|
||
|
|
├── Recharts 3.9.0 (diagram)
|
||
|
|
├── Lucide React 1.21.0 (ikoner)
|
||
|
|
└── API-client: Fetch + localStorage JWT
|
||
|
|
```
|
||
|
|
|
||
|
|
**Struktur:**
|
||
|
|
```
|
||
|
|
src/
|
||
|
|
├── App.tsx # Huvudkomponent (just nu: Vite-default)
|
||
|
|
├── main.tsx # Entry point
|
||
|
|
├── index.css # Tailwind + global styles
|
||
|
|
├── components/
|
||
|
|
│ └── Layout.tsx # Sidebar + navigation
|
||
|
|
├── contexts/
|
||
|
|
│ └── AuthContext.tsx # JWT auth (mock just nu)
|
||
|
|
├── types/
|
||
|
|
│ └── index.ts # TypeScript interfaces
|
||
|
|
└── utils/
|
||
|
|
└── api.ts # API client (ej kopplad till backend)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Nuvarande status:**
|
||
|
|
- ✅ Byggt med modern stack (React 19, Vite, Tailwind)
|
||
|
|
- ✅ TypeScript för typsäkerhet
|
||
|
|
- ✅ Layout-komponent med navigation
|
||
|
|
- ✅ AuthContext med JWT-hantering
|
||
|
|
- ⚠️ **App.tsx är fortfarande Vite-default** (räknare, loggor)
|
||
|
|
- ⚠️ **API-klienten är ej kopplad till aamos-ledger** (localhost:3000)
|
||
|
|
- ⚠️ **Auth är mock** (hardkodad användare)
|
||
|
|
- ⚠️ **Inga sidor implementerade** (endast Layout)
|
||
|
|
|
||
|
|
### Återanvändningsbarhet: ⭐⭐⭐ (3/5)
|
||
|
|
|
||
|
|
**Varför medelhög återanvändningsbarhet:**
|
||
|
|
|
||
|
|
1. **Modern stack** — React 19 + Vite + Tailwind är branschstandard
|
||
|
|
2. **Komponentbaserad** — Layout, AuthContext kan återanvändas
|
||
|
|
3. **TypeScript** — Typer definierade för alla domänobjekt
|
||
|
|
4. **API-abstraktion** — `api.ts` ger ett lager ovanpå fetch
|
||
|
|
|
||
|
|
**Begränsningar:**
|
||
|
|
1. **Ej kopplad till backend** — API-klienten pekar på fel URL (`localhost:3000` istället för `amos.wavult.com:3250`)
|
||
|
|
2. **Mock-auth** — Ingen riktig JWT-flöde implementerat
|
||
|
|
3. **Ingen state management** — Ingen Zustand/Redux/React Query
|
||
|
|
4. **Inga formulär** — Ingen react-hook-form eller liknande
|
||
|
|
5. **Inga tester** — Inget test-ramverk konfigurerat
|
||
|
|
|
||
|
|
### Anpassningskostnad: **Medium**
|
||
|
|
|
||
|
|
| Aspekt | Kostnad | Kommentar |
|
||
|
|
|--------|---------|-----------|
|
||
|
|
| **Koppla till backend** | ~2 timmar | Uppdatera API_BASE_URL, anpassa endpoints |
|
||
|
|
| **Implementera sidor** | ~2-3 dagar | Dashboard, verifikat, kontoplan, rapporter |
|
||
|
|
| **JWT-auth** | ~4 timmar | Integrera med aamos-ledger auth.mjs |
|
||
|
|
| **State management** | ~1 dag | Zustand eller React Query för server-state |
|
||
|
|
| **Formulär** | ~1 dag | react-hook-form för verifikat-inmatning |
|
||
|
|
| **Tester** | ~2 dagar | Vitest + React Testing Library |
|
||
|
|
| **Mobilanpassning** | ~1 dag | Tailwind responsive (redan delvis) |
|
||
|
|
|
||
|
|
**Risker:**
|
||
|
|
- Stor gap mellan design (Layout.tsx) och implementation (App.tsx)
|
||
|
|
- API-klienten har typer som inte matchar backend (Voucher vs journal_entry)
|
||
|
|
- Ingen error handling eller loading states
|
||
|
|
- Ingen pagination för stora listor (verifikat, konton)
|
||
|
|
|
||
|
|
**Rekommendation:**
|
||
|
|
1. **Omedelbart:** Uppdatera `API_BASE_URL` till `https://amos.wavult.com/api/ledger`
|
||
|
|
2. **Sprint 1:** Implementera Dashboard-sida med riktig data
|
||
|
|
3. **Sprint 2:** Verifikat-lista och detaljvy
|
||
|
|
4. **Sprint 3:** Verifikat-inmatning med formulär
|
||
|
|
5. **Sprint 4:** Rapporter (P&L, balansräkning)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Jämförelse med Auth/Identity Service (Del 1)
|
||
|
|
|
||
|
|
| Komponent | Återanvändningsbarhet | Anpassningskostnad | Nyckelskillnad |
|
||
|
|
|-----------|----------------------|-------------------|----------------|
|
||
|
|
| **Auth/Identity** | ⭐⭐⭐⭐⭐ | Låg | Redan abstraherad, tenant-aware |
|
||
|
|
| **API-gateway** | ⭐⭐⭐⭐⭐ | Låg | Samma mönster, deklarativ config |
|
||
|
|
| **Databaslager** | ⭐⭐⭐⭐ | Låg | Standardiserat schema, men skalning behövs |
|
||
|
|
| **Event-buss** | ⭐⭐⭐⭐⭐ | Låg | Universal envelope, fallback-arkitektur |
|
||
|
|
| **Frontend** | ⭐⭐⭐ | Medium | Modern stack men ej kopplad till backend |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Rekommendationer per komponent
|
||
|
|
|
||
|
|
### API-gateway: ✅ Behåll
|
||
|
|
- Nginx är beprövad, stabil och kräver minimal anpassning
|
||
|
|
- Överväg endast dynamisk service registry vid >50 tjänster
|
||
|
|
|
||
|
|
### Databaslager: ⚠️ Förbättra
|
||
|
|
- Sätt upp PostgreSQL-replikering
|
||
|
|
- Partitionera `ledger_journal_entries`
|
||
|
|
- Överväg PgBouncer för connection pooling
|
||
|
|
|
||
|
|
### Event-buss: ✅ Behåll, förbättra
|
||
|
|
- Hermes-envelopen är utmärkt
|
||
|
|
- Lägg till log rotation för JSONL
|
||
|
|
- Dokumentera event-katalog
|
||
|
|
|
||
|
|
### Frontend: 🔴 Prioritera
|
||
|
|
- Största gapet mellan design och implementation
|
||
|
|
- Koppla till backend omedelbart
|
||
|
|
- Implementera sidor i prioritetsordning: Dashboard → Verifikat → Rapporter
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Nästa steg
|
||
|
|
|
||
|
|
1. **Omedelbart:** Koppla Ouroboros frontend till aamos-ledger backend
|
||
|
|
2. **Vecka 1:** Implementera Dashboard med riktig data
|
||
|
|
3. **Vecka 2:** Implementera verifikat-lista och detaljvy
|
||
|
|
4. **Vecka 3:** PostgreSQL-replikering och partitionering
|
||
|
|
5. **Vecka 4:** Hermes event-katalog och log rotation
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
*Analys baserad på:*
|
||
|
|
- `aamos-ledger/index.mjs` — Ledger Engine API
|
||
|
|
- `aamos-ledger/hermes.mjs` — Event fabric
|
||
|
|
- `aamos-ledger/schema.sql` — Databasschema
|
||
|
|
- `ouroboros-frontend/` — React frontend
|
||
|
|
- `memory/nginx-upstreams-result.md` — Nginx konfiguration
|
||
|
|
- `landvex-plugin-architecture.md` — Plugin-arkitektur
|