NewsCore ist ein cloud-natives, hochverfügbares Nachrichtenportal: redaktionelle Inhalte aus einem CMS, ausgeliefert über eine GraphQL-API (Backend-for-Frontend) an ein server-seitig gerendertes Nuxt-3-Frontend — ausgelegt für High-Traffic und GitOps-Betrieb auf OpenShift/Kubernetes.
Ein echter End-to-End-Lauf: Startseite mit Ressort-Filter → Artikeldetail → Volltextsuche —
Nuxt 3 SSR über die GraphQL-API (auch als MP4).
- Highlights
- Screenshots
- Features
- Architektur
- Quick Start
- Teststrategie
- CI/CD Pipeline
- Projektstruktur
- Defect Management
- Architecture Decision Records
- Roadmap
- Lauffähiger Vertical Slice in einem Befehl —
docker compose up --buildstartet GraphQL-Gateway + SSR-Frontend. - N+1-frei by design — nested
author/categorywerden per DataLoader (@BatchMapping) gebatcht. - Austauschbare Datenquelle — Resolver kennen nur Service-Interfaces; In-Memory-Mock, Sophora-CMS und ElasticSearch sind reine Profil-Schalter, ohne Resolver-Änderung.
- Hohe Testabdeckung, in CI erzwungen — Backend 98 %, Frontend 96 % (Gate ≥ 80 %).
- Dokumentierte Entscheidungen — jede Technologiewahl als ADR.
![]() |
![]() |
![]() |
| Startseite — Ressort-Filter + Artikel-Grid | Artikeldetail — Breadcrumb, Autor, Cover | Suche — search("klima") → 2 Treffer |
| Feature | Status | Technologie | Anmerkung |
|---|---|---|---|
| GraphQL-BFF-Gateway | ✅ | Spring Boot 3, spring-graphql, Java 21 |
Schema-first |
| N+1-Vermeidung | ✅ | DataLoader via @BatchMapping |
author + category |
| Application-Cache | ✅ | Caffeine (@Cacheable) |
ADR-003 |
| SSR-Frontend | ✅ | Nuxt 3, Vue 3, TypeScript | useAsyncData |
| GraphQL-Client | ✅ | urql (ssrExchange) |
ADR-001 |
| Edge-Caching | ✅ | Varnish (VCL) + Apache (TLS/gzip/Header) | ADR-008, event-getriebenes PURGE |
| Volltextsuche | ✅ | ElasticSearch 8 (Profil-gesteuert) | ADR-004, Testcontainers |
| CMS-Datenquelle | ✅ | Sophora-HTTP-Adapter (Profil sophora) |
ADR-010, Default In-Memory |
| Event-Streaming | ✅ | Kafka + Avro/Schema-Registry + DLQ | ADR-011, EmbeddedKafka + Mock-Registry |
| Aktive Cache-Invalidierung | ✅ | article.events → ES-Reindex + Evict |
schließt den Loop zu ADR-003 |
| Analytics → PostgreSQL | ✅ | analytics-service (Node, kafkajs + pg) | search.events + user.events (Pageviews) → Postgres |
| Observability-Endpoint | ✅ | Actuator + /actuator/prometheus |
Grafana folgt |
| Lokales Stack-Setup | ✅ | docker-compose (ES + Kafka + Postgres) | ein Befehl |
| CI-Pipeline | ✅ | GitLab CI (5 Stages) | Coverage-Gate |
| K8s/OpenShift + GitOps | ✅ | Helm-Umbrella-Chart + ArgoCD | ADR-006, HPA/PDB/NetworkPolicy |
| Observability | ✅ | Prometheus + Grafana + Loki | ADR-007, Dashboards/Alerts/JSON-Logs |
| Distributed Tracing | ✅ | OpenTelemetry + Tempo (OTLP) | ADR-009, Trace↔Logs-Korrelation |
flowchart TD
User([Browser]) -->|HTTPS| Apache[Apache Reverse Proxy<br/>TLS · Rate-Limit]
Apache --> Varnish[Varnish Cache]
Varnish -->|Cache-Miss| Nuxt[Nuxt 3 SSR<br/>urql]
Nuxt -->|GraphQL| GW[GraphQL API Gateway<br/>Spring Boot · BFF<br/>DataLoader · Caffeine]
GW --> CMS[(Sophora CMS)]
GW --> ES[(ElasticSearch 8)]
GW --> PG[(PostgreSQL)]
GW --> Kafka{{Apache Kafka}}
Kafka --> Analytics[Analytics-Service]
GW --> Prom[Prometheus / Grafana / Loki]
classDef done fill:#ecfdf5,stroke:#00a862,color:#0b1220;
classDef soon fill:#f1f5f9,stroke:#94a3b8,color:#475569,stroke-dasharray:4 3;
class Nuxt,GW,ES,Kafka,Analytics,PG,Prom,Apache,Varnish done;
class CMS soon;
Grün = in dieser Iteration umgesetzt · gestrichelt = vorgesehen (Roadmap). Begründung der zentralen Schicht: ADR-002 — GraphQL BFF.
docker compose up --build
# Frontend: http://localhost:3000
# GraphiQL: http://localhost:8080/graphiql# Terminal 1 — API-Gateway (Maven Wrapper lädt Maven beim ersten Lauf)
cd apps/api-gateway && ./mvnw spring-boot:run
# Terminal 2 — Frontend
cd apps/frontend && npm install && npm run devgit config core.hooksPath .githooks
chmod +x .githooks/*Strikte Testpyramide — Bulk an Unit-Tests, gezielte Integrationstests, wenige E2E (Roadmap):
┌────────────────────┐
│ E2E (Playwright) │ 🔜 Roadmap
├────────────────────┤
│ Integrationstests │ GraphQlTester · Testcontainers (ES) · EmbeddedKafka
├────────────────────┤
│ Unit-Tests │ JUnit 5 · Vitest (Bulk)
└────────────────────┘
| Schicht | Tooling | Umfang |
|---|---|---|
| Backend | JUnit 5, Mockito, AssertJ | 52 Unit-Tests |
| Backend | GraphQlTester, Testcontainers (ES), EmbeddedKafka (+ Mock-Schema-Registry) |
11 Integrationstests |
| Frontend | Vitest, Vue Test Utils, happy-dom | 16 Tests |
| analytics-service | Vitest (gemockt: kafkajs + pg) | 12 Tests |
cd apps/api-gateway && ./mvnw verify # Backend: Tests + JaCoCo-Gate
cd apps/frontend && npm run test:unit -- --run --coverage
cd services/analytics-service && npm run test:unit -- --run --coverageCoverage-Gate ≥ 80 % in allen Modulen erzwungen (Frontend ~95 %, analytics-service 100 % der Logik-Schicht). Flaky-Test-Policy: FLAKY-TESTS.md.
GitHub Actions (.github/workflows/ci.yml) baut und testet bei jedem
Push/PR alle Module parallel (Status oben als Badge): api-gateway (./mvnw verify inkl.
Testcontainers-ES + EmbeddedKafka), frontend, analytics-service (Lint + Tests + Build) sowie
helm lint/template.
Die produktive GitLab-CI + ArgoCD-GitOps-Pipeline (.gitlab-ci.yml) bildet den
Deploy-Pfad gemäß Spec ab — fünf Stages mit pfadbasierten rules:
validate ─→ test ─→ build ─→ security ─→ deploy-staging ─→ deploy-prod (manuell)
lint unit/IT images trivy ArgoCD sync ArgoCD sync
- validate — ESLint +
type-check(Frontend), Checkstyle (Backend) - test — Unit + Integration, Coverage als Report/Artifact
- build — Container-Images → Registry (nur
main/develop) - security — Trivy-Scan, kein Merge bei HIGH/CRITICAL CVEs
- deploy — GitOps via ArgoCD (Staging automatisch, Prod manuelles Gate)
newscore/
├── apps/
│ ├── api-gateway/ # Spring Boot 3 · GraphQL BFF · ES · Kafka (eigene README)
│ └── frontend/ # Nuxt 3 · urql · SSR (eigene README)
├── services/
│ └── analytics-service/ # Node/TS · Kafka → PostgreSQL (eigene README)
├── infrastructure/
│ ├── helm/newscore/ # Umbrella-Chart (Deploy/Svc/HPA/PDB/Ingress/NetworkPolicy/ServiceMonitor)
│ ├── argocd/ # ArgoCD Application-Manifeste (staging/prod)
│ ├── prometheus/ # Scrape-Config + Alert-Rules
│ ├── grafana/ # Provisioning (Datasources) + Dashboards
│ ├── loki/ # Promtail-Config (Log-Aggregation)
│ ├── varnish/ # default.vcl (Edge-Cache, TTLs, PURGE)
│ └── apache/ # Reverse-Proxy (TLS/gzip/Security-Header)
├── docs/
│ ├── adr/ # Architecture Decision Records
│ ├── defects/ # Defect-Template + Bugs
│ └── images/banner.svg
├── .github/ # Actions-CI, Issue/PR-Templates, Dependabot, CODEOWNERS
├── .githooks/ # pre-commit · commit-msg · pre-push
├── docker-compose.yml # lokales Stack-Setup (ES + Kafka + Registry + Postgres)
├── .gitlab-ci.yml # GitLab-CI/GitOps-Pipeline (Deploy)
├── CONTRIBUTING.md · FLAKY-TESTS.md · LICENSE
└── README.md
Bugs werden in docs/defects/ nach dem
Defect-Template dokumentiert (Severity, Repro-Schritte,
Root Cause, Regressionstest).
| ADR | Entscheidung |
|---|---|
| ADR-001 | urql als GraphQL-Client im Frontend |
| ADR-002 | GraphQL-Gateway nach BFF-Pattern |
| ADR-003 | Caffeine als In-Process-Cache |
| ADR-004 | ElasticSearch als Volltextsuche (Profil-gesteuert) |
| ADR-005 | Kafka-Eventing mit JSON-Serialisierung + DLQ |
| ADR-006 | Helm-Umbrella-Chart + ArgoCD-GitOps |
| ADR-007 | Observability mit Prometheus, Grafana und Loki |
| ADR-008 | Varnish-Edge-Cache + Apache-Reverse-Proxy |
| ADR-009 | Distributed Tracing mit OpenTelemetry + Tempo |
| ADR-010 | Sophora-CMS als austauschbare Datenquelle |
| ADR-011 | Avro + Schema-Registry (ersetzt ADR-005 §2) |
- ✅ Volltextsuche: ElasticSearch-
SearchService(profil-gesteuert, Testcontainers) — erledigt - ✅ Eventing (Epic 4): Kafka-Producer/Consumer, DLQ, aktive Cache-Invalidierung, Analytics → Postgres — erledigt
- ✅ Plattform (Epic 5): Helm-Umbrella-Chart, ArgoCD-Apps, HPA/PDB, NetworkPolicies (OpenShift) — erledigt
- ✅ Observability (Epic 6): Prometheus-Scrape/Alerts, Grafana-Dashboards, JSON-Logs → Loki — erledigt
- ✅ Caching (Epic 3): Varnish-VCL + Apache-Proxy, event-getriebenes
PURGE— erledigt - ✅ Tracing: OpenTelemetry + Tempo (OTLP), Trace↔Logs-Korrelation — erledigt
- ✅ Datenanbindung: Sophora-CMS-HTTP-Adapter (Profil
sophora), Default In-Memory — erledigt - ✅ Analytics-Eventing:
user.pageview-Producer im Frontend — erledigt - ✅ Eventing-Härtung: Avro + Confluent Schema-Registry (ersetzt JSON aus ADR-005) — erledigt
- Frontend: GraphQL-Codegen, Playwright-E2E, i18n, PWA


