Files
backend/docs/ARCHITECTURE.md
T

4.0 KiB

Visione Backend Architecture

Purpose

Visione backend is C++20 + Drogon API for municipal urban issue reporting. Citizens create reports. Operators own report workflow on platform. External intervention teams do not log into platform; operators can assign them and generate public read-only intervention links.

Source Of Truth

  • Route truth: controllers/*.h
  • Business truth: services/*.cc
  • Schema truth: migrations/*.sql
  • Contract truth: docs/openapi/visione.openapi.yaml merged with route scan
  • High-signal human summary: docs/API_SPEC.md and docs/LLM_CONTEXT.md

Code Layout

  • controllers/: HTTP input/output only, auth gate, status mapping.
  • services/: business rules, workflow, permission checks, orchestration.
  • repositories/: SQL serialization helpers and migration runner.
  • common/: auth, crypto, validation, HTTP helpers, email, CORS.
  • migrations/: ordered SQL schema evolution.
  • docs/openapi/: manual OpenAPI layer.
  • scripts/generate_openapi.py: extracts Drogon routes and merges manual spec.

Runtime Model

  • Auth uses bearer tokens from api_tokens.
  • OTP login uses otp_challenges and Mailpit in local dev.
  • Password hash uses PBKDF2-HMAC-SHA256. Legacy sha256$ hashes rejected.
  • DB access uses raw parameterized SQL through drogon::orm::DbClient.
  • No Drogon generated ORM models. No Mapper<T>.

Role Model

  • citizen: own reports only, own messages, feedback, notifications.
  • operator: can read all reports, manage workflow, assign teams, create flags.
  • admin: full admin + report management + moderation approval.

Effective permission helpers:

  • auth::canManageReports(user): admin or reports.manage
  • auth::canOperateReports(user): non-admin report manager
  • auth::mustChangePassword(user): blocks protected operational/admin flows

Workflow Ownership Rules

  • reports.assigned_operator_id is single platform owner of report.
  • assignments is external team assignment history only.
  • Team assignment can claim OPEN report directly: operator becomes assigned_operator_id, report moves to ASSIGNED.
  • take exists for OPEN -> UNDER_REVIEW without team assignment.
  • External team link is public, revocable, tied to current assignment, hashed in intervention_access_links.

Status Machine

  • Allowed statuses: OPEN, UNDER_REVIEW, ASSIGNED, IN_PROGRESS, RESOLVED, REJECTED, CLOSED
  • Allowed transitions:
    • OPEN -> UNDER_REVIEW | REJECTED
    • UNDER_REVIEW -> ASSIGNED | REJECTED
    • ASSIGNED -> IN_PROGRESS | REJECTED
    • IN_PROGRESS -> RESOLVED | REJECTED
    • RESOLVED -> CLOSED
    • REJECTED -> CLOSED

Main Persistence Objects

  • users, roles, permissions, user_roles, role_permissions
  • api_tokens, otp_challenges
  • report_categories, areas, team_categories, teams
  • reports, report_status_history, assignments, report_photos
  • messages, feedback, reward_events, notifications
  • moderation_flags
  • report_category_team_categories
  • intervention_access_links

HTTP/Error Conventions

  • 401: missing/invalid auth
  • 403: authenticated but forbidden
  • 404: missing resource or invalid public intervention token
  • 409: workflow/ownership conflict
  • 422: semantic payload/reference error

Controllers currently map service error strings heuristically. When adding new service errors, update controller mappers too.

OpenAPI Generation

scripts/generate_openapi.py scans ADD_METHOD_TO(...) in controller headers. Generated route skeletons merge with docs/openapi/visione.openapi.yaml. Build fails if manual spec contains operation without matching route, unless x-manual-only: true.

Output path:

  • build/openapi/visione.openapi.json

Current Gaps / Sharp Edges

  • No dedicated repository layer split yet; SQL still lives inside services.
  • AuthService::editUser() updates fields with dynamic SQL column name from hardcoded allowlist, not user input.
  • Public intervention summary currently excludes email and full surname, but still exposes citizen phone by design.
  • No frontend doc set here beyond API/OpenAPI docs.