Files
fete/specs/013-auto-delete-expired/plan.md
nitrix 4bfaee685c
All checks were successful
CI / backend-test (push) Successful in 58s
CI / frontend-test (push) Successful in 23s
CI / frontend-e2e (push) Successful in 1m10s
CI / build-and-publish (push) Has been skipped
Auto-delete expired events via daily scheduled cleanup job
Adds a Spring @Scheduled job (daily at 03:00) that deletes all events
whose expiry_date is before CURRENT_DATE using a native SQL DELETE.
RSVPs are cascade-deleted via the existing FK constraint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:58:35 +01:00

4.2 KiB

Implementation Plan: Auto-Delete Expired Events

Branch: 013-auto-delete-expired | Date: 2026-03-09 | Spec: spec.md Input: Feature specification from /specs/013-auto-delete-expired/spec.md

Summary

Add a scheduled background job that runs daily and deletes all events whose expiryDate has passed. Deletion is performed via a native SQL query (DELETE FROM events WHERE expiry_date < CURRENT_DATE). Cascade deletion of RSVPs is handled by the existing ON DELETE CASCADE FK constraint. No API or frontend changes required.

Technical Context

Language/Version: Java 25, Spring Boot 3.5.x Primary Dependencies: Spring Scheduling (@Scheduled), Spring Data JPA (for native query) Storage: PostgreSQL (existing, Liquibase migrations) Testing: JUnit 5, Spring Boot Test, Testcontainers (existing) Target Platform: Linux server (Docker) Project Type: Web service (backend only for this feature) Performance Goals: N/A — daily batch job on small dataset Constraints: Single native DELETE query, no entity loading Scale/Scope: Self-hosted, small-scale — typically < 100 events total

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle Status Notes
I. Privacy by Design PASS Deleting expired data supports privacy goals. No PII logged.
II. Test-Driven Methodology PASS Tests written before implementation (TDD).
III. API-First Development N/A No API changes — this is a backend-internal job.
IV. Simplicity & Quality PASS Single query, no over-engineering.
V. Dependency Discipline PASS Uses only existing Spring dependencies (@Scheduled).
VI. Accessibility N/A No frontend changes.

Project Structure

Documentation (this feature)

specs/013-auto-delete-expired/
├── plan.md              # This file
├── spec.md              # Feature specification
├── research.md          # Phase 0 output (minimal — no unknowns)
├── data-model.md        # Phase 1 output
└── checklists/
    └── requirements.md  # Spec quality checklist

Source Code (repository root)

backend/src/main/java/de/fete/
├── application/service/
│   └── ExpiredEventCleanupJob.java          # NEW: Scheduled job
├── domain/port/out/
│   └── EventRepository.java                 # MODIFIED: Add deleteExpired method
└── adapter/out/persistence/
    ├── EventJpaRepository.java              # MODIFIED: Add native DELETE query
    └── EventPersistenceAdapter.java         # MODIFIED: Implement deleteExpired

backend/src/test/java/de/fete/
├── application/service/
│   └── ExpiredEventCleanupJobTest.java      # NEW: Unit test for job
└── adapter/out/persistence/
    └── EventPersistenceAdapterIntegrationTest.java  # NEW or MODIFIED: Integration test for native query

Structure Decision: Backend-only change. Follows existing hexagonal architecture: port defines the contract, adapter implements with native query, service layer schedules the job.

Design

Hexagonal Flow

@Scheduled trigger
    → ExpiredEventCleanupJob (application/service)
        → EventRepository.deleteExpired() (domain/port/out)
            → EventPersistenceAdapter.deleteExpired() (adapter/out/persistence)
                → EventJpaRepository native query (adapter/out/persistence)
                    → DELETE FROM events WHERE expiry_date < CURRENT_DATE

Key Decisions

  1. Port method: int deleteExpired() on EventRepository — returns count of deleted events for logging.
  2. Native query: @Modifying @Query(value = "DELETE FROM events WHERE expiry_date < CURRENT_DATE", nativeQuery = true) on EventJpaRepository.
  3. Schedule: @Scheduled(cron = "0 0 3 * * *") — runs daily at 03:00 server time. Low-traffic window.
  4. Logging: INFO-level log after each run: "Deleted {count} expired event(s)". No log if count is 0 (or DEBUG-level).
  5. Transaction: The native DELETE runs in a single transaction — atomic, no partial state.
  6. Enable scheduling: Add @EnableScheduling to FeteApplication (or a config class).