# Implementation Plan: Auto-Delete Expired Events

**Branch**: `013-auto-delete-expired` | **Date**: 2026-03-09 | **Spec**: [spec.md](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)

```text
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)

```text
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).
