From 2abe74c96a2a661c80510c2e8c5d57ad796da54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20Sch=C3=B6dl?= Date: Sat, 18 Jul 2026 19:44:54 +0200 Subject: [PATCH 1/3] make docker compose persistent per default --- docker-compose.yml | 32 ++++++++++++++++++++++++++++++-- infra/docker-compose.yml | 4 ++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9fe97ab5..19931ca4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,6 +41,23 @@ services: - action: rebuild path: ./.env + postgres: + image: postgres:16-alpine + restart: always + environment: + POSTGRES_USER: ${DB_USER:-postgres} + POSTGRES_PASSWORD: ${DB_PASSWORD:-secretpass} + POSTGRES_DB: ${DB_NAME:-cookingdb} + expose: + - "5432" + volumes: + - db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-cookingdb}"] + interval: 5s + timeout: 5s + retries: 5 + spring-api: build: ./services/spring-api ports: @@ -48,11 +65,19 @@ services: environment: AI_RECIPE_SERVICE_URL: http://py-recipe-service:8080 AI_HELP_SERVICE_URL: http://py-help-service:8080 + DB_URL: jdbc:postgresql://postgres:5432/${DB_NAME:-cookingdb} + DB_USER: ${DB_USER:-postgres} + DB_PASSWORD: ${DB_PASSWORD:-secretpass} + JPA_DDL_AUTO: update env_file: - ./.env depends_on: - - py-help-service - - py-recipe-service + postgres: + condition: service_healthy + py-help-service: + condition: service_started + py-recipe-service: + condition: service_started develop: watch: - action: rebuild @@ -99,3 +124,6 @@ services: volumes: - ./api/openapi.yaml:/usr/share/nginx/html/openapi/openapi.yaml:ro - ./api/openapi-internal.yaml:/usr/share/nginx/html/openapi/openapi-internal.yaml:ro + +volumes: + db-data: diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index ac8d6194..122d166e 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -33,7 +33,7 @@ services: - backend postgres-db: - image: postgres:15-alpine + image: postgres:16-alpine restart: always env_file: .env environment: @@ -86,4 +86,4 @@ services: - spring-api volumes: - db-data: \ No newline at end of file + db-data: From cd24b501f0c48d0bc5e788398b1d2ada88b0a420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20Sch=C3=B6dl?= Date: Sat, 18 Jul 2026 19:45:08 +0200 Subject: [PATCH 2/3] drop best practises md --- best_practices.md | 284 --------------------------------------- infra/k8s/namespace.yaml | 2 +- 2 files changed, 1 insertion(+), 285 deletions(-) delete mode 100644 best_practices.md diff --git a/best_practices.md b/best_practices.md deleted file mode 100644 index b4aeac0c..00000000 --- a/best_practices.md +++ /dev/null @@ -1,284 +0,0 @@ -# Best Practices Microservices -Concrete Tips and Hints -1. โœ… Microservice Architecture & Design Best Practices - - Single Responsibility Services: Each microservice should encapsulate one specific domain or business capability. Avoid feature creep and overlapping responsibilities. - Stateless Services: Design services to be stateless. Store session/context in tokens (e.g., JWT) or shared databases/cache layers like Redis if necessary. - Language Boundary Awareness: Carefully define the interface (OpenAPI spec) between Spring Boot (Java) and Langchain (Python). Use JSON over HTTP, not internal data structures. - -2. ๐Ÿ“ API-Driven Design and OpenAPI Usage -Design First - - Teams should define and review the OpenAPI specs collaboratively before implementing any logic. - Use tools like Swagger Editor or Stoplight. - -Use OpenAPI Code Generators - - Java (Spring Boot): springdoc-openapi or OpenAPI Generator. Use Spring Boot 4.x and Java 25 (LTS) - Python: openapi-python-client - Versioning: Always version APIs (e.g., /api/v1/resource) from day one to prevent breaking clients during iteration. - -API-First, Contract-Strong -Task Concrete Tool / Command -Edit & lint spec `npx @redocly/cli lint api/openapi.yaml` (Spectral rules) -Generate Java stubs `openapi-generator-cli generate -i api/openapi.yaml -g spring -o services/spring-order/generated` -Generate Python client `openapi-python-client --path api/openapi.yaml --output services/py-recommender/client` -Generate TypeScript SDK `npx openapi-typescript api/openapi.yaml -o web-client/src/api.ts` -Mock server for client `npx prism mock api/openapi.yaml` (runs on port 4010) - -Never merge without running the linter; add it to a pre-commit hook. - -โžก๏ธ pre-commit.com Include .pre-commit-config.yaml in your repo and register: - -```yaml -# .pre-commit-config.yaml -repos: - - repo: https://github.com/Redocly/openapi-cli - rev: v1.0.0-beta.92 - hooks: - - id: openapi-cli-lint -``` - -Run with: - -pre-commit run -a - -Mono-repo Layout - -```t -repo/ -โ”œโ”€โ”€ api/ # Single source of truth -โ”‚ โ”œโ”€โ”€ openapi.yaml # Versioned spec (v1, v2โ€ฆ) -โ”‚ โ””โ”€โ”€ scripts/ # helper scripts for code-gen -โ”œโ”€โ”€ services/ -โ”‚ โ”œโ”€โ”€ spring-order/ # Java 25, Spring Boot 4.x -โ”‚ โ””โ”€โ”€ py-recommender/ # Python 3.12, LangChain -โ”œโ”€โ”€ web-client/ # Any JS framework -โ”œโ”€โ”€ infra/ # docker-compose, k8s Helm charts, Terraform -โ””โ”€โ”€ .github/workflows/ # CI pipelines -``` - - The OpenAPI spec lives once in /api. Every language consumes it via code-gen. - No hand-written DTOs. Use records (immutable) for DTOs in Java. - -๐Ÿ‘€ Minimal gen-all.sh helper (drop in /api/scripts) - -```bash -#!/usr/bin/env bash -set -euo pipefail - -openapi-generator-cli generate -i api/openapi.yaml -g spring \ - -o services/spring-order/generated --skip-validate-spec - -openapi-python-client --path api/openapi.yaml \ - --output services/py-recommender/client --config api/scripts/py-config.json - -npx openapi-typescript api/openapi.yaml -o web-client/src/api.ts -``` - -Make it executable and callable from CI; run ./api/scripts/gen-all.sh after each spec change. - -๐Ÿ’ก Tip: Add a Git hook (post-checkout, post-merge) to run ./api/scripts/gen-all.sh automatically. This ensures generated clients are always up-to-date after switching branches or pulling changes. -3. ๐Ÿ” Security Best Practices - - Use OAuth2 / OIDC via API gateway (e.g., Keycloak, GitHub, Auth0). - Pass JWTs through HTTP headers. - Each service must verify the token using a shared public key. - Gateways (like Traefik or NGINX) can centralize token validation. - -๐Ÿ‘‰ Tip: Place the gateway as the entrypoint to intercept and validate tokens before forwarding to services. -4. โš™๏ธ Development and Deployment Practices - - Contract Testing: Use Pact or similar tools to ensure API contract fidelity between producer (Spring Boot) and consumer (Langchain). - Service Discovery: Use an API gateway (e.g., Traefik, NGINX) to route and secure APIs. Avoid direct service-to-service calls across languages unless encapsulated. - Consistent Error Handling: Use a unified error schema in OpenAPI: {code, message, details}. Enforce it across all services and in the OpenAPI spec. - CI/CD Pipeline: Automate OpenAPI linting, stub generation, and testing in GitHub Actions. Always validate that OpenAPI specs match implementation on each build. - Docker Image Publishing: Push container images to a registry with semantic version tags (e.g., ghcr.io/org/service:1.0.0) and/or Git commit SHA (:sha-abc123) to ensure traceability and reproducibility across environments. - -GitHub Actions Pipeline Example - -```yaml -# .github/workflows/ci.yml -name: CI - -on: - push: - branches: - - main - paths-ignore: - - 'README.md' - pull_request: - -concurrency: ci-${{ github.ref }} - -jobs: - generate-and-test: - runs-on: ubuntu-latest - strategy: - matrix: - service: [spring-order, py-recommender, web-client] - - steps: - - uses: actions/checkout@v4 - - - name: Lint OpenAPI - run: npx @redocly/cli lint api/openapi.yaml - - - name: Generate code - run: ./api/scripts/gen-all.sh - shell: bash - - - name: Cache Maven / npm / pip - uses: actions/cache@v4 - with: - path: | - ~/.m2/repository - ~/.cache/pip - ~/.npm - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - - name: Build & test - working-directory: services/${{ matrix.service }} - run: | - case ${{ matrix.service }} in - spring-order) ./mvnw verify ;; - py-recommender) pip install -r req.txt && pytest ;; - web-client) npm ci && npm run test -- --watchAll=false ;; - esac - - contract-test: - needs: generate-and-test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Pact verification - run: ./scripts/run-pact.sh -``` - -Local Development and Runtime -Area Hands-on Choice Why -Service discovery Simple API Gateway. Leads to one ingress, easier CORS, security. -Data isolation One PostgreSQL database per service (logical schema ok). Classic microservice rule โ†’ avoids coupling -Dev containers devcontainer.json + VS Code, Docker-in-Docker enabled Requires zero local setup. -Local orchestration Include a file called docker-compose up in /infra Offers DBs + gateway + both services in one command. -Cross-service call Python โ†’ Java via generated TypeScript-style client (py-recommender/client). Consistent via OpenAPI through reusing same OpenAPI spec, avoids hidden internal formats -Observability Spring Boot Actuator + Prometheus; Include LangChain's tracing to OpenTelementry; Scrape via Docker Compose Production-grade insights with minimal code. -5. ๐Ÿ’ป Client Integration - - Import generated api.ts in React or similar. - Runtime base URL comes from .env file: VITE_API_URL=http://localhost:8080 - For CORS, configure gateway: - -```yaml -spring: - cloud: - gateway: - globalcors: - corsConfigurations: - '[/**]': - allowedOrigins: "*" - allowedMethods: "*" - - Use SWR or React Query for caching and retries. -``` - -6. ๐Ÿค Collaboration Best Practices -Ritual Concrete Action -API review Weekly 15-min sync to review openapi.yaml changes -Definition of Done PR must include: โœ… Passing CI, โœ… Updated spec, โœ… Short doc (/docs/adr-xyz.md) -Doc automation Generate and publish OpenAPI docs with: redoc-cli bundle api/openapi.yaml -o docs/api.html โ†’ deploy with GH Pages((/docs folder)). -Issue template Add checkboxes in the "Describe Changes" section: โ€œAffects API?โ€, โ€œSpec updated?โ€ ; Create a PR template -7. ๐Ÿงช Write Tests - - Consider implementing integration and end-to-end (E2E) tests per microservice. - These tests should include interactions with the database and any external APIs to ensure that services behave correctly as the system evolves. - This helps maintain stability and avoid regressions when introducing new features. - -8. ๐Ÿšซ What Not To Do - - โŒ No direct HTTP calls without generated client - โŒ No shared DTOs/utilities outside the OpenAPI spec - โŒ No long-running branches (max 2 days โ†’ rebase/merge) - โŒ No manual production deploys โ€” everything must go through CI/CD - -# Best Practices Monitoring with Kubernetes -โœ… Monitoring Best Practices in Kubernetes -1. Separate Namespace - -Deploy Prometheus and Grafana in a dedicated namespace (e.g., monitoring) to isolate observability tools from application microservices. -This brings benefits like: - - Easier user permission management - Resource limits (CPU/memory) via Kubernetes quotas - Clear separation for upgrades, troubleshooting, and maintenance - -2. Persistent Volumes - -Ensure that Prometheus and Grafana retain important data across restarts: - - Use Persistent Volumes (PVs) for Prometheus to keep historical metrics - Use PVs for Grafana to persist dashboards and configuration - -3. Label-Based Discovery - -Label your services and pods consistently so Prometheus can discover them automatically. -This ensures that metrics are still collected after updates or scaling. - -Recommended labels: - -```yaml -app: my-service -monitoring: "true" -``` - -PrometheusSelector Example: - -```yaml -matchLabels: - monitoring: "true" -``` - -4. Use ServiceMonitor / PodMonitor - -Use special configuration files called ServiceMonitor and PodMonitor to tell Prometheus which services or pods it should collect data from: - -These objects are part of the Prometheus Operator and help define what Prometheus should monitor. They automatically adapt to changes in your deployments. -5. Dashboards as Code - -Avoid manual configuration in environments: - - Store Grafana dashboards as version-controlled files (JSON) - Use Helm or config maps to provision dashboards and data sources - This ensures consistency and easier collaboration - -6. Optional: Access Control - -Secure your monitoring tools: - - Protect Prometheus and Grafana with authentication (e.g., via Rancher) - Use TLS for encrypted connections - Apply role-based access control (RBAC) to manage user permissions - -7. Version Visibility - -Expose application version information as a custom Prometheus metric: - - Helps track which version is currently deployed - Makes it easier to correlate performance changes or issues with specific releases - Visualize versions in Grafana dashboards - -8. Alerting Rules - -Set up meaningful and actionable alerts: - - Define rules such as: - High error rate - Pod restart count > 5 - Slow response time - - Use PrometheusRule CRDs (Custom Resource Definitions) if working with Prometheus Operator - - Connect to Alertmanager for routing alerts to: - Email - Slack - PagerDuty diff --git a/infra/k8s/namespace.yaml b/infra/k8s/namespace.yaml index 6f7487e8..b61d90b1 100644 --- a/infra/k8s/namespace.yaml +++ b/infra/k8s/namespace.yaml @@ -1,5 +1,5 @@ --- -# Two namespaces per best_practices.md: +# Two namespaces: # app โ€” all four microservices + database # monitoring โ€” Prometheus & Grafana (deployed separately, later) # From 3aa1e5718232bb9060e7ed3610a0d504f22f5f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20Sch=C3=B6dl?= Date: Sat, 18 Jul 2026 21:09:03 +0200 Subject: [PATCH 3/3] cleanup .env example --- .env.example | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index c109e629..dbf065d5 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,7 @@ # either `google_genai`, `openai` (Logos) or `local` PROVIDER=google_genai -# Google Gemini API key for the GenAI services โ€” https://aistudio.google.com/apikey +# Google Gemini API keys for the GenAI services โ€” https://aistudio.google.com/apikey GEMINI_MODEL=gemini-3.1-flash-lite GEMINI_HELP_SERVICE_KEY= GEMINI_RECIPE_SERVICE_KEY= @@ -16,11 +16,10 @@ LOCAL_BASE_URL=http://host.docker.internal:1234/v1 LOCAL_MODEL=google/gemma-4-e4b LOCAL_KEY=not-needed -# Spring API (optional variables, if not defined in-memory db is automatically set up) -# DB_URL=jdbc:postgresql://host:5432/cooking +# Postgres database for Spring API (uncomment to override default values) +# DB_NAME=cookingdb # DB_USER=postgres -# DB_PASSWORD= +# DB_PASSWORD=secretpass # JWT_SECRET= # any string โ‰ฅ 32 chars -# JPA_DDL_AUTO=update # only set when using persistent db INTERNAL_AUTH_SECRET=replaceThisWithYourSecret