Plan: F1.Web Target Architecture
Recommend a pragmatic refactor that keeps Blazor idioms but reduces page-level orchestration. The main move is to stop letting page components own transport details and timer/state plumbing directly. Introduce thin typed services for API boundaries, keep lightweight UI composition in Razor components, and use the Australia selection page as the first exemplar refactor.
Steps
Phase 1: Stabilize service boundaries in F1.Web. Introduce typed frontend services around current HTTP usage so endpoint strings, status-code handling, and API response mapping live outside pages. Start with a selection-focused service and a race metadata/config service. This blocks later component cleanup.
Phase 1: Keep the existing IMockDateService as the single frontend-facing source of truth for mocked time, but treat it explicitly as a backend-backed cache/client rather than a browser-local utility. This should remain compatible with ITimeProvider. Parallel with step 1.
Phase 2: Refactor AustraliaSelection into orchestration plus presentation. The page should load data, hold only page-level UI state, and compose subcomponents rather than owning all selection form rendering and API parsing logic. Depends on 1.
Phase 2: Extract reusable presentational components from AustraliaSelection. First candidates are a driver selection form component and a bet strategy component. Add a countdown/prompt display component only if reuse becomes real; do not over-componentize prematurely. Depends on 3.
Phase 2: Replace Model="this" in AustraliaSelection with an explicit page/form state model so binding state is decoupled from component lifecycle and helper methods. Depends on 3.
Phase 3: Normalize error handling and response parsing inside the typed services. Pages should receive domain results or simple error strings rather than parsing JSON/text bodies themselves. Depends on 1.
Phase 3: Trim layout responsibilities where possible. MainLayout should stay an app shell and composition point; any growth in version-loading or environment-display logic should move behind a dedicated service/helper rather than expanding layout code further. Independent but lower priority.
Verification: split tests by concern. Keep page tests focused on orchestration/rendering, and add service-level tests for HTTP response handling and timer/deadline formatting. This should reduce brittle bUnit setups as page logic is extracted. Depends on 3 and 6.
Relevant files
src/F1.Web/Pages/AustraliaSelection.razor — current monolithic page; primary refactor target; currently owns loading, form state, save flow, countdown text, and timer lifecycle.
src/F1.Web/Pages/DateMockWidget.razor — example of UI that should stay thin and delegate server interaction/state to a service.
src/F1.Web/Services/MockDateService.cs — current backend-backed cached mock-date client; model for where transport logic should live.
src/F1.Web/Services/TimeProvider.cs — thin synchronous time abstraction backed by cached mock-date state.
src/F1.Web/Layout/MainLayout.razor — shell component; keep focused on composition rather than accumulating service logic further.
src/F1.Web/Program.cs — DI registration point for typed API services and supporting helpers.
src/F1.Web/Services/UserSession.cs — example of app-level state service with API access; useful reference for service lifetimes and responsibilities.
tests/F1.Web.Tests/AustraliaSelectionTests.cs — current page-heavy test file; should be decomposed as responsibilities move into services/components.
tests/F1.Web.Tests/MainLayoutTests.cs — reference for layout/component DI test setup.
Verification
Build with build.sh after each refactor phase to ensure API tests, web tests, and docker build remain green.
Add or update bUnit tests so extracted components verify rendering and binding behavior without needing full-page HTTP orchestration.
Add service-level tests around selection data loading/saving and countdown formatting so HTTP/status-code branches are exercised outside the page.
Run the Selenium/E2E flow that uses date mocking to verify the single-source-of-truth mock date still drives both widget state and frontend lock behavior.
Decisions
Included: a pragmatic target architecture for F1.Web and a decomposition strategy centered on AustraliaSelection.
Included: preserving Blazor component patterns and existing DI rather than introducing a heavy state-management framework.
Excluded: broad redesign of all pages right away; only extract patterns proven by AustraliaSelection first.
Excluded: introducing global client-side caching/state libraries unless current scoped services become insufficient.
Further Considerations
Prefer thin typed API clients over generic repository-style abstractions. The benefit here is centralized transport/error handling, not adding indirection for its own sake.
Keep countdown logic as a service/helper only if it improves testability or is reused; otherwise a dedicated presenter component may be enough.
When refactoring AustraliaSelection, preserve the current API contract and user-visible behavior first, then simplify internals in small steps.
My recommendation is to keep the target architecture narrow:
typed API services for transport and error handling
small presentational components for repeated UI sections
pages as orchestrators, not transport layers
no heavy client-state framework
If you’re happy with that direction, next I’ll do 3 and turn src/F1.Web/Pages/AustraliaSelection.razor into the exemplar refactor.
Plan: F1.Web Target Architecture
Recommend a pragmatic refactor that keeps Blazor idioms but reduces page-level orchestration. The main move is to stop letting page components own transport details and timer/state plumbing directly. Introduce thin typed services for API boundaries, keep lightweight UI composition in Razor components, and use the Australia selection page as the first exemplar refactor.
Steps
Phase 1: Stabilize service boundaries in F1.Web. Introduce typed frontend services around current HTTP usage so endpoint strings, status-code handling, and API response mapping live outside pages. Start with a selection-focused service and a race metadata/config service. This blocks later component cleanup.
Phase 1: Keep the existing IMockDateService as the single frontend-facing source of truth for mocked time, but treat it explicitly as a backend-backed cache/client rather than a browser-local utility. This should remain compatible with ITimeProvider. Parallel with step 1.
Phase 2: Refactor AustraliaSelection into orchestration plus presentation. The page should load data, hold only page-level UI state, and compose subcomponents rather than owning all selection form rendering and API parsing logic. Depends on 1.
Phase 2: Extract reusable presentational components from AustraliaSelection. First candidates are a driver selection form component and a bet strategy component. Add a countdown/prompt display component only if reuse becomes real; do not over-componentize prematurely. Depends on 3.
Phase 2: Replace Model="this" in AustraliaSelection with an explicit page/form state model so binding state is decoupled from component lifecycle and helper methods. Depends on 3.
Phase 3: Normalize error handling and response parsing inside the typed services. Pages should receive domain results or simple error strings rather than parsing JSON/text bodies themselves. Depends on 1.
Phase 3: Trim layout responsibilities where possible. MainLayout should stay an app shell and composition point; any growth in version-loading or environment-display logic should move behind a dedicated service/helper rather than expanding layout code further. Independent but lower priority.
Verification: split tests by concern. Keep page tests focused on orchestration/rendering, and add service-level tests for HTTP response handling and timer/deadline formatting. This should reduce brittle bUnit setups as page logic is extracted. Depends on 3 and 6.
Relevant files
src/F1.Web/Pages/AustraliaSelection.razor — current monolithic page; primary refactor target; currently owns loading, form state, save flow, countdown text, and timer lifecycle.
src/F1.Web/Pages/DateMockWidget.razor — example of UI that should stay thin and delegate server interaction/state to a service.
src/F1.Web/Services/MockDateService.cs — current backend-backed cached mock-date client; model for where transport logic should live.
src/F1.Web/Services/TimeProvider.cs — thin synchronous time abstraction backed by cached mock-date state.
src/F1.Web/Layout/MainLayout.razor — shell component; keep focused on composition rather than accumulating service logic further.
src/F1.Web/Program.cs — DI registration point for typed API services and supporting helpers.
src/F1.Web/Services/UserSession.cs — example of app-level state service with API access; useful reference for service lifetimes and responsibilities.
tests/F1.Web.Tests/AustraliaSelectionTests.cs — current page-heavy test file; should be decomposed as responsibilities move into services/components.
tests/F1.Web.Tests/MainLayoutTests.cs — reference for layout/component DI test setup.
Verification
Build with build.sh after each refactor phase to ensure API tests, web tests, and docker build remain green.
Add or update bUnit tests so extracted components verify rendering and binding behavior without needing full-page HTTP orchestration.
Add service-level tests around selection data loading/saving and countdown formatting so HTTP/status-code branches are exercised outside the page.
Run the Selenium/E2E flow that uses date mocking to verify the single-source-of-truth mock date still drives both widget state and frontend lock behavior.
Decisions
Included: a pragmatic target architecture for F1.Web and a decomposition strategy centered on AustraliaSelection.
Included: preserving Blazor component patterns and existing DI rather than introducing a heavy state-management framework.
Excluded: broad redesign of all pages right away; only extract patterns proven by AustraliaSelection first.
Excluded: introducing global client-side caching/state libraries unless current scoped services become insufficient.
Further Considerations
Prefer thin typed API clients over generic repository-style abstractions. The benefit here is centralized transport/error handling, not adding indirection for its own sake.
Keep countdown logic as a service/helper only if it improves testability or is reused; otherwise a dedicated presenter component may be enough.
When refactoring AustraliaSelection, preserve the current API contract and user-visible behavior first, then simplify internals in small steps.
My recommendation is to keep the target architecture narrow:
typed API services for transport and error handling
small presentational components for repeated UI sections
pages as orchestrators, not transport layers
no heavy client-state framework
If you’re happy with that direction, next I’ll do 3 and turn src/F1.Web/Pages/AustraliaSelection.razor into the exemplar refactor.