Files
Reklamator/specs/001-build-an-application/data-model.md
T
gurixandClaude 05e201f1fc Add implementation planning artifacts for anonymous feedback platform
Complete Phase 0 (Research) and Phase 1 (Design) of implementation planning workflow:

- plan.md: Technical context, constitution check, and project structure
- research.md: Technology decisions for AI integration, file storage, authentication, security
- data-model.md: Domain entities with validation rules and state transitions
- contracts/: API specifications for submission, dashboard, and admin endpoints
- quickstart.md: Developer setup guide with test-first workflow
- CLAUDE.md: Updated agent context with tech stack

All constitutional requirements validated. Ready for task generation phase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 22:31:26 +02:00

411 lines
15 KiB
Markdown

# Data Model: Anonymous Feedback Platform (Reklamator)
**Branch**: `001-build-an-application` | **Date**: 2025-10-15
This document defines the domain entities, their attributes, relationships, validation rules, and state transitions for the Reklamator application.
## Entity Overview
```
Product (1) ----< (N) Feedback
| |
| |---< (N) Attachment
| |
| |---- (1) AnalysisResult
|
|----< (N) ProductOwner
Administrator (manages all entities)
```
---
## Entity Definitions
### 1. Feedback Submission
**Description**: Represents a single feedback item submitted by an anonymous user.
**Storage Location**: `data/products/{product_id}/feedback/{feedback_id}/`
#### Attributes
| Field | Type | Required | Validation | Description |
|-------|------|----------|------------|-------------|
| `id` | UUID v4 | Yes | Auto-generated | Unique identifier |
| `product_id` | String | Yes | Must reference existing product | Associated product identifier |
| `original_text` | String | Yes* | 1-10,000 characters | Original feedback text (*empty if file-only submission) |
| `original_language` | String (ISO 639-1) | No | 2-char code | Detected language (e.g., "en", "de", "ja") |
| `submission_timestamp` | ISO 8601 DateTime | Yes | Auto-generated | When feedback was submitted (UTC) |
| `status` | Enum | Yes | See Status enum below | Current processing/review status |
| `category` | Enum | No | See Category enum below | AI-assigned category (null if analysis pending/failed) |
| `attachment_count` | Integer | Yes | 0-3 | Number of attached files |
#### Status Enum
- `submitted` - Initial state after successful submission
- `analyzing` - AI analysis in progress
- `analysis_failed` - AI analysis encountered error
- `analyzed` - AI analysis completed successfully
- `reviewed` - Product owner has reviewed
- `in_progress` - Product owner marked as being worked on
- `resolved` - Product owner marked as resolved
- `rejected` - Product owner marked as not actionable
#### Category Enum (AI-assigned)
- `idea` - New concept or suggestion
- `feature_request` - Request for specific functionality
- `bug` - Problem or defect report
- `complaint` - Negative feedback about existing functionality
#### Validation Rules
- FR-002: `original_text` max length 10,000 characters
- FR-011: At least one of (`original_text`, `attachment_count > 0`) must be true
- FR-012: Cannot be empty (no text AND no attachments)
- FR-027: `submission_timestamp` immutable after creation
#### State Transitions
```
submitted → analyzing → analyzed → {reviewed, in_progress, resolved, rejected}
analysis_failed (terminal state until manual retry)
```
#### File Representation (metadata.yaml)
```yaml
id: "a3f2c1d5-8b4e-4f1a-9c2d-7e6f5a4b3c2d"
product_id: "001-acme-app"
original_language: "en"
submission_timestamp: "2025-10-15T14:32:10Z"
status: "analyzed"
category: "bug"
attachment_count: 2
attachments:
- filename: "screenshot.png"
size_bytes: 245678
mime_type: "image/png"
- filename: "error_log.txt"
size_bytes: 1234
mime_type: "text/plain"
```
---
### 2. Product/Service
**Description**: Represents a product or service for which feedback can be collected.
**Storage Location**: `data/products/{product_id}/config.yaml`
#### Attributes
| Field | Type | Required | Validation | Description |
|-------|------|----------|------------|-------------|
| `id` | String | Yes | Unique, URL-safe slug | Product identifier (e.g., "001-acme-app") |
| `name` | String | Yes | 1-100 characters | Display name |
| `description` | String | No | Max 500 characters | Product description |
| `target_language` | String (ISO 639-1) | Yes | 2-char code | Preferred language for AI translations |
| `submission_url_slug` | String | Yes | URL-safe, unique | URL path for submission form (e.g., "/submit/acme-app") |
| `created_date` | ISO 8601 Date | Yes | Auto-generated | When product was registered |
| `status` | Enum | Yes | "active" or "archived" | Current status |
| `assigned_owner_ids` | List[String] | No | Must reference existing users | Product owner user IDs |
#### Validation Rules
- FR-046: `id` must be unique across all products
- FR-047: `target_language` must be valid ISO 639-1 code
- FR-049: `submission_url_slug` must be unique and URL-safe (alphanumeric + hyphens)
- FR-053: Cannot accept new feedback if `status` is "archived"
#### State Transitions
```
active ⇄ archived (bidirectional, admin only)
```
#### File Representation (config.yaml)
```yaml
id: "001-acme-app"
name: "Acme Mobile App"
description: "Customer feedback for Acme's flagship mobile application"
target_language: "en"
submission_url_slug: "acme-app"
created_date: "2025-10-01"
status: "active"
assigned_owner_ids:
- "owner-001"
- "owner-002"
statistics:
total_feedback_count: 127
last_submission: "2025-10-15T14:32:10Z"
```
---
### 3. Analysis Result
**Description**: Represents the AI-generated analysis of a feedback submission.
**Storage Location**: `data/products/{product_id}/feedback/{feedback_id}/analysis.md`
**Additional Storage**: Original text stored in `content.txt` for reference
#### Attributes (Markdown Format)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `translated_text` | Markdown | Yes* | Feedback translated to target language (*if different from original) |
| `summary` | Markdown | Yes | Concise 2-3 sentence summary in target language |
| `detected_category` | Enum | Yes | Category assigned by AI (idea/feature_request/bug/complaint) |
| `confidence_score` | Float (0.0-1.0) | Yes | AI confidence in categorization |
| `analysis_timestamp` | ISO 8601 DateTime | Yes | When analysis completed |
| `model_used` | String | Yes | AI model identifier (e.g., "claude-3-haiku-20240307") |
| `error_message` | String | No | Error details if analysis failed |
#### Validation Rules
- FR-014: `summary` should be 2-3 sentences maximum
- FR-015: `translated_text` required unless original language = target language
- FR-016: Original text preserved in `content.txt` alongside analysis
- FR-017: Detected language stored in feedback metadata.yaml `original_language`
#### File Representation (analysis.md)
```markdown
# Feedback Analysis
**Analyzed**: 2025-10-15T14:35:22Z
**Model**: claude-3-haiku-20240307
**Category**: bug (confidence: 0.92)
**Original Language**: de → **Target Language**: en
## Summary
User reports that the app crashes when uploading large files. This appears to be a bug affecting the file upload module, preventing users from submitting documents over 5MB.
## Translation
**Original (German):**
> Die App stürzt ab, wenn ich versuche, große Dateien hochzuladen. Jedes Mal wenn ich eine PDF über 5MB hochlade, friert die App ein und schließt sich.
**Translated (English):**
The app crashes when I try to upload large files. Every time I upload a PDF over 5MB, the app freezes and closes.
## Attachments
- screenshot.png (245 KB)
- error_log.txt (1 KB)
```
---
### 4. Attachment
**Description**: Represents a file (document or image) uploaded with feedback.
**Storage Location**: `data/products/{product_id}/feedback/{feedback_id}/attachments/{filename}`
#### Attributes (stored in feedback metadata.yaml)
| Field | Type | Required | Validation | Description |
|-------|------|----------|------------|-------------|
| `filename` | String | Yes | Sanitized, max 255 chars | Original filename (sanitized for safety) |
| `size_bytes` | Integer | Yes | Max 10,485,760 (10MB) | File size in bytes |
| `mime_type` | String | Yes | See allowed types | Validated MIME type |
| `upload_timestamp` | ISO 8601 DateTime | Yes | Auto-generated | When file was uploaded |
| `virus_scan_status` | Enum | Yes | "clean" or "infected" | ClamAV scan result |
#### Allowed MIME Types
- Documents: `application/pdf`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document` (DOCX), `text/plain`
- Images: `image/jpeg`, `image/png`, `image/gif`, `image/webp`
#### Validation Rules
- FR-004: Maximum 3 attachments per feedback
- FR-006: Maximum 10MB per file
- FR-026: Preserve original filename (sanitized)
- FR-059: Sanitize filename to prevent directory traversal
- FR-060: Must pass ClamAV virus scan before storage
#### Security Sanitization
- Remove directory traversal patterns: `../`, `..\\`, absolute paths
- Replace unsafe characters: `<>:"|?*`
- Limit filename length to 255 characters
- If duplicate filename, append counter: `file.pdf``file_2.pdf`
---
### 5. Product Owner
**Description**: Represents an authenticated user responsible for reviewing feedback for one or more products.
**Storage Location**: `data/users.yaml`
#### Attributes
| Field | Type | Required | Validation | Description |
|-------|------|----------|------------|-------------|
| `id` | String | Yes | Unique | User identifier (e.g., "owner-001") |
| `email` | String | Yes | Valid email, unique | Login email address |
| `password_hash` | String (bcrypt) | Yes | bcrypt format | Hashed password (never store plaintext) |
| `name` | String | Yes | 1-100 characters | Display name |
| `role` | Enum | Yes | "product_owner" | User role (always "product_owner" for this entity) |
| `assigned_product_ids` | List[String] | Yes | Must reference existing products | Products this owner can access |
| `created_date` | ISO 8601 Date | Yes | Auto-generated | Account creation date |
| `last_login` | ISO 8601 DateTime | No | Auto-updated | Last successful login |
#### Validation Rules
- FR-048: Can be assigned to multiple products
- FR-056: Must authenticate to access dashboard
- FR-063: Passwords hashed with bcrypt (cost factor 12)
- FR-033: Can only view feedback for `assigned_product_ids`
#### File Representation (users.yaml entry)
```yaml
users:
- id: "owner-001"
email: "jane.smith@example.com"
password_hash: "$2b$12$KIXxBt5H4vE2zT9vN8FqOe9Jx..."
name: "Jane Smith"
role: "product_owner"
assigned_product_ids:
- "001-acme-app"
- "002-beta-service"
created_date: "2025-09-15"
last_login: "2025-10-15T09:23:11Z"
```
---
### 6. Administrator
**Description**: Represents a privileged user who can register products, assign owners, and manage system configuration.
**Storage Location**: `data/users.yaml` (same file as Product Owners)
#### Attributes
| Field | Type | Required | Validation | Description |
|-------|------|----------|------------|-------------|
| `id` | String | Yes | Unique | User identifier (e.g., "admin-001") |
| `email` | String | Yes | Valid email, unique | Login email address |
| `password_hash` | String (bcrypt) | Yes | bcrypt format | Hashed password |
| `name` | String | Yes | 1-100 characters | Display name |
| `role` | Enum | Yes | "admin" | User role (always "admin" for this entity) |
| `assigned_product_ids` | List[String] | Yes | Empty list | Empty = access to all products |
| `created_date` | ISO 8601 Date | Yes | Auto-generated | Account creation date |
| `last_login` | ISO 8601 DateTime | No | Auto-updated | Last successful login |
#### Validation Rules
- Admin role grants full access regardless of `assigned_product_ids`
- FR-045: Can create/modify/archive products
- FR-048: Can assign/unassign product owners
- Same authentication requirements as Product Owner (FR-056, FR-063)
#### File Representation (users.yaml entry)
```yaml
users:
- id: "admin-001"
email: "admin@reklamator.local"
password_hash: "$2b$12$vL3Fx9..."
name: "System Administrator"
role: "admin"
assigned_product_ids: [] # Empty = all access
created_date: "2025-09-01"
last_login: "2025-10-15T10:45:33Z"
```
---
## Domain Rules & Invariants
### Cross-Entity Rules
1. **Product-Feedback Relationship** (1:N)
- Every Feedback must reference exactly one valid Product
- Product can have zero or many Feedback items
- Archived products cannot receive new feedback (FR-053)
2. **Feedback-Attachment Relationship** (1:N)
- Feedback can have 0-3 Attachments (FR-004, FR-010)
- Attachments cannot exist without parent Feedback (cascade delete)
3. **Feedback-AnalysisResult Relationship** (1:1)
- Every analyzed Feedback has exactly one AnalysisResult
- AnalysisResult created asynchronously after Feedback submission
- Original content preserved even if analysis fails (FR-020)
4. **Product-Owner Relationship** (N:M)
- Product can have 1 or more assigned Product Owners (FR-048)
- Product Owner can be assigned to multiple Products
- Admin users bypass assignment logic (implicit access to all)
5. **Anonymity Constraint** (Global)
- No IP addresses stored in Feedback metadata (FR-055, SC-010)
- No session tracking for anonymous submissions
- Rate limiting uses IP for abuse prevention only (not persisted)
### Deletion Rules
- **Feedback Deletion**: Deletes metadata.yaml, content.txt, analysis.md, and all attachments/
- **Product Archival**: Sets status to "archived", preserves all feedback (FR-052)
- **Product Owner Removal**: Unassigns from products, does not delete feedback
- **Cascade Protection**: Cannot delete Product with active feedback (archive instead)
---
## Storage Implementation Notes
### Directory Structure Example
```
data/
├── users.yaml # All users (admins + owners)
└── products/
├── 001-acme-app/
│ ├── config.yaml # Product metadata
│ └── feedback/
│ ├── a3f2c1d5-8b4e-.../
│ │ ├── metadata.yaml # Feedback + attachment metadata
│ │ ├── content.txt # Original feedback text
│ │ ├── analysis.md # AI analysis report
│ │ └── attachments/
│ │ ├── screenshot.png
│ │ └── error_log.txt
│ └── b7e1f3d2-4a9c-.../
│ ├── metadata.yaml
│ ├── content.txt
│ └── analysis.md # No attachments/ for this one
└── 002-beta-service/
├── config.yaml
└── feedback/
└── ...
```
### File Format Standards
- **YAML**: UTF-8 encoding, 2-space indentation, explicit type declarations
- **Markdown**: CommonMark specification, UTF-8 encoding
- **Text Files**: UTF-8 encoding with BOM handling
### Indexing Strategy (Performance)
For dashboard performance (SC-008, SC-014):
- Cache product feedback counts in `config.yaml` statistics
- Implement pagination (50 items per page)
- Load metadata.yaml only, defer analysis.md loading until detail view
- File modification times used for sorting (newest first per FR-041)
---
## Validation Summary by Functional Requirement
| FR | Validation Location | Rule |
|----|---------------------|------|
| FR-002 | Feedback.original_text | Max 10,000 characters |
| FR-004 | Feedback.attachment_count | 0-3 attachments |
| FR-006 | Attachment.size_bytes | Max 10MB per file |
| FR-011 | Feedback validation | At least text OR files required |
| FR-012 | Feedback validation | Cannot be completely empty |
| FR-046 | Product.id | Must be unique |
| FR-048 | Product.assigned_owner_ids | 1+ owners required |
| FR-053 | Product status check | Reject if archived |
| FR-063 | User.password_hash | bcrypt with cost 12 |
---
**Next Steps**: Define API contracts in `/contracts/` directory