# Sports Platform — Enterprise Architecture (Laravel 12 / PHP 8.4)

This is Phase 1 of the build (per the requested START ORDER):
1. Folder Architecture ✅
2. Database Design ✅
3. Migrations — Core + RSS/AI Content Engine modules ✅ (this batch)
4. Models, Services, Repositories, API Layer, Admin Panel, Frontend, Live Scores, SEO, Scheduler, Queues, Testing, Deployment → subsequent batches

---

## 1. Folder Structure

```
app/
├── Actions/                      # Single-purpose invokable action classes
│   ├── Match/
│   │   ├── UpdateLiveMatchAction.php
│   │   ├── FinalizeMatchAction.php
│   │   └── RecalculateStandingsAction.php
│   ├── News/
│   │   ├── ImportRssFeedAction.php
│   │   ├── RewriteArticleAction.php
│   │   ├── DetectDuplicateArticleAction.php
│   │   └── GenerateSeoMetaAction.php
│   └── Search/
│       └── IndexSearchableEntityAction.php
│
├── Console/
│   ├── Commands/
│   │   ├── Live/UpdateLiveMatchesCommand.php
│   │   ├── Live/UpdateStandingsCommand.php
│   │   ├── Rss/ImportRssSourcesCommand.php
│   │   ├── Rss/RewritePendingArticlesCommand.php
│   │   └── Maintenance/CleanupOldLogsCommand.php
│   └── Kernel.php                # scheduler definitions
│
├── Contracts/
│   ├── AI/
│   │   └── AIProviderInterface.php
│   ├── Repositories/
│   │   ├── TeamRepositoryInterface.php
│   │   ├── PlayerRepositoryInterface.php
│   │   ├── MatchRepositoryInterface.php
│   │   └── NewsRepositoryInterface.php
│   └── Sports/
│       └── SportsDataProviderInterface.php   # API-Football / Sportmonks abstraction
│
├── DTOs/
│   ├── AI/
│   │   ├── RewriteResultDTO.php
│   │   ├── SeoResultDTO.php
│   │   ├── SummaryResultDTO.php
│   │   ├── TranslationResultDTO.php
│   │   ├── ClassificationResultDTO.php
│   │   └── DuplicateCheckResultDTO.php
│   └── Sports/
│       ├── MatchDTO.php
│       ├── TeamDTO.php
│       └── PlayerDTO.php
│
├── Enums/
│   ├── AI/AIProviderEnum.php
│   ├── AI/AICapabilityEnum.php
│   ├── MatchStatusEnum.php
│   ├── MatchEventTypeEnum.php
│   ├── ArticleStatusEnum.php
│   ├── RssImportStatusEnum.php
│   └── LanguageEnum.php
│
├── Events/
│   ├── MatchStatusChanged.php
│   ├── MatchEventRecorded.php
│   ├── ArticleRewritten.php
│   └── DuplicateArticleDetected.php
│
├── Listeners/
│   ├── BroadcastMatchUpdate.php
│   ├── InvalidateMatchCache.php
│   └── NotifyFavoriteSubscribers.php
│
├── Http/
│   ├── Controllers/
│   │   ├── Api/V1/...
│   │   ├── Admin/...
│   │   └── Web/...
│   ├── Requests/
│   ├── Resources/
│   └── Middleware/
│
├── Models/                       # Eloquent models (1 per table, Phase 2)
│
├── Observers/
│   ├── MatchObserver.php
│   └── ArticleObserver.php
│
├── Policies/
│
├── Repositories/
│   ├── Eloquent/
│   │   ├── TeamRepository.php
│   │   ├── PlayerRepository.php
│   │   └── MatchRepository.php
│
├── Services/
│   ├── AI/
│   │   ├── AIManager.php                 # facade over active provider
│   │   ├── AIProviderFactory.php         # resolves provider from config/DB setting
│   │   └── Providers/
│   │       ├── OpenAIProvider.php
│   │       ├── AnthropicProvider.php
│   │       ├── GeminiProvider.php
│   │       ├── DeepSeekProvider.php
│   │       ├── MistralProvider.php
│   │       └── OllamaProvider.php
│   ├── Sports/
│   │   ├── ApiFootballProvider.php
│   │   ├── SportmonksProvider.php
│   │   └── SportsDataManager.php
│   ├── Rss/
│   │   ├── RssFeedReaderService.php
│   │   ├── ContentCleanerService.php
│   │   ├── ImageDownloaderService.php
│   │   └── SimilarityDetectionService.php
│   └── Seo/
│       ├── MetaGeneratorService.php
│       └── SchemaGeneratorService.php
│
└── Traits/
    ├── HasUuid.php
    ├── HasSlug.php
    └── Cacheable.php

config/
├── ai.php                        # provider-agnostic AI config, active provider, per-provider settings
├── sports_data.php
└── seo.php

database/
├── migrations/
├── seeders/
└── factories/
```

## 2. Design Principles Applied

- **Provider abstraction (Sports Data & AI)**: the rest of the app depends only on `SportsDataProviderInterface` and `AIProviderInterface` — never on a concrete vendor SDK. Swapping API-Football ↔ Sportmonks, or OpenAI ↔ Claude ↔ Gemini ↔ DeepSeek ↔ Mistral ↔ Ollama, is a config/admin-setting change, not a code change.
- **Repository Pattern**: controllers/services never touch Eloquent query builders directly for core domain reads — they go through repository interfaces, bound in a `RepositoryServiceProvider`.
- **DTOs**: all cross-boundary data (API responses, AI provider responses) is normalized into DTOs before touching the domain layer, so a malformed/vendor-specific payload never leaks into business logic.
- **Actions**: single-purpose, testable, queueable units (`ImportRssFeedAction`, `RewriteArticleAction`, etc.) — these are what the scheduler and jobs actually call.
- **Observers/Events**: match state transitions and article publication are event-driven, so cache invalidation, notifications, and search indexing stay decoupled.
