95 lines
4.3 KiB
Markdown
95 lines
4.3 KiB
Markdown
# Phase 4: Application Layer + Background Workers
|
|
|
|
**Status:** ⬜ Not Started
|
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
|
**Domain:** backend
|
|
**Depends on:** Phase 1 (Domain), Phase 2 (Storage), Phase 3 (Scraping)
|
|
|
|
## Objective
|
|
|
|
Wire scraping + storage together via use-case orchestrators in the Application layer
|
|
and background services that execute pollers on configurable intervals.
|
|
|
|
## Tasks
|
|
|
|
- [ ] Implement use cases in `Marathon.Application/UseCases/`:
|
|
- `PullUpcomingEventsUseCase(IOddsScraper, IEventRepository, ISnapshotRepository)`
|
|
- `ExecuteAsync(CancellationToken)` → fetch upcoming events, persist new ones,
|
|
capture initial pre-match snapshots for each
|
|
- `PullLiveOddsUseCase(IOddsScraper, IEventRepository, ISnapshotRepository)`
|
|
- `ExecuteAsync(CancellationToken)` → for each currently-live event, fetch a
|
|
fresh snapshot, persist it
|
|
- `PullResultsUseCase(IOddsScraper, IEventRepository, IResultRepository)`
|
|
- `ExecuteAsync(DateRange range, IReadOnlyList<EventId>? selection, CancellationToken)`
|
|
→ fetch results for completed events (all or selected)
|
|
- `ExportToExcelUseCase(IExcelExporter, IEventRepository)`
|
|
- `ExecuteAsync(DateRange, ExportKind, CancellationToken)`
|
|
- [ ] Implement background services in `Marathon.Infrastructure/Workers/`:
|
|
- `UpcomingEventsPoller : BackgroundService` — runs `PullUpcomingEventsUseCase` on
|
|
a configurable cron-like schedule (default: every 6 hours)
|
|
- `LiveOddsPoller : BackgroundService` — runs `PullLiveOddsUseCase` every
|
|
`Scraping:PollingIntervalSeconds` seconds
|
|
- Both honor `CancellationToken`, log via `ILogger<T>`, and skip cycles gracefully
|
|
on errors (don't crash the host)
|
|
- [ ] Add `WorkerOptions` POCO bound to `Workers:*` config:
|
|
```csharp
|
|
public sealed class WorkerOptions {
|
|
public string UpcomingScheduleCron { get; init; } = "0 0 */6 * * *"; // every 6h
|
|
public bool LivePollerEnabled { get; init; } = true;
|
|
public bool UpcomingPollerEnabled { get; init; } = true;
|
|
}
|
|
```
|
|
Use `Cronos` package or simple TimeSpan for upcoming schedule.
|
|
- [ ] Add DI extension `AddMarathonApplication(IServiceCollection, IConfiguration)`
|
|
in `Marathon.Application/DependencyInjection.cs`:
|
|
- Registers all use cases
|
|
- [ ] Update `Marathon.Infrastructure/DependencyInjection.cs` to also register
|
|
`BackgroundService`s under `services.AddHostedService<T>()`.
|
|
- [ ] Tests in `Marathon.Application.Tests`:
|
|
- Mock `IOddsScraper` + repos with NSubstitute
|
|
- Test: `PullUpcomingEventsUseCase` persists new events, skips duplicates
|
|
- Test: `PullLiveOddsUseCase` writes a snapshot per live event
|
|
- Test: `PullResultsUseCase` respects `selection` filter (when null, fetches all)
|
|
- Test: `ExportToExcelUseCase` invokes `IExcelExporter.ExportAsync` with correct
|
|
date range
|
|
- [ ] Tests in `Marathon.Infrastructure.Tests/Workers/`:
|
|
- Test: `LiveOddsPoller` invokes use case at configured interval (use FakeTimeProvider)
|
|
- Test: poller continues after a use-case exception (logs, doesn't propagate)
|
|
|
|
## Files to Modify/Create
|
|
|
|
- `src/Marathon.Application/UseCases/*.cs`
|
|
- `src/Marathon.Application/DependencyInjection.cs`
|
|
- `src/Marathon.Infrastructure/Workers/UpcomingEventsPoller.cs`
|
|
- `src/Marathon.Infrastructure/Workers/LiveOddsPoller.cs`
|
|
- `src/Marathon.Infrastructure/Configuration/WorkerOptions.cs`
|
|
- `tests/Marathon.Application.Tests/UseCases/**`
|
|
- `tests/Marathon.Infrastructure.Tests/Workers/**`
|
|
|
|
## Acceptance Criteria
|
|
|
|
- Compiles (Big Bang).
|
|
- Use cases depend only on Application abstractions (no Infrastructure refs).
|
|
- Workers honor cancellation and don't crash on transient errors.
|
|
- All variable timing/enabling is configurable.
|
|
|
|
## Notes
|
|
|
|
- Use `IHostedService` from `Microsoft.Extensions.Hosting` — works in WPF host via
|
|
`Host.CreateApplicationBuilder()` pattern (Phase 5 will expose this).
|
|
- For the cron-style upcoming poller, prefer the `Cronos` package (small, mature)
|
|
over hand-rolled scheduling.
|
|
- Big Bang: compile-only smoke check.
|
|
|
|
## Review Checklist
|
|
|
|
- [ ] Use cases have no Infrastructure dependencies
|
|
- [ ] Both pollers configurable (interval, enable/disable)
|
|
- [ ] Cancellation propagated correctly
|
|
- [ ] Errors logged, not propagated out of `ExecuteAsync`
|
|
|
|
## Handoff to Next Phase
|
|
|
|
<!-- Filled by Phase 4 implementer. Phase 5 needs to know how to start the host
|
|
including these BackgroundServices. -->
|