Files
gridpilot.gg/docs/architecture/shared/SERVICES.md
2026-01-11 13:04:33 +01:00

251 lines
4.3 KiB
Markdown

Services Design Guide (Clean Architecture)
This document defines all service types used across the system and assigns clear, non-overlapping responsibilities.
It exists to remove ambiguity around the word “service”, which is heavily overloaded.
The rules below are strict.
Overview
The system contains four distinct service categories, each in a different layer:
1. Frontend Services
2. API Services
3. Core Application Services
4. Core Domain Services
They must never be mixed.
1. Frontend Services
Purpose
Frontend services orchestrate UI-driven workflows.
They answer the question:
“How does the UI obtain and submit data?”
Responsibilities
Frontend services MAY:
• call API clients
• apply client-side guards (blockers, throttles)
• create View Models
• orchestrate multiple API calls
• handle recoverable UI errors
Frontend services MUST NOT:
• contain business rules
• validate domain invariants
• modify domain state
• know about core domain objects
Placement
apps/website/lib/services/
Example
• LeagueService
• RaceService
• AuthService
Each service is UI-facing, not business-facing.
2. API Services (Application Services)
Purpose
API services adapt HTTP-level concerns to core use cases.
They answer the question:
“How does an external client interact with the core?”
Responsibilities
API services MAY:
• orchestrate multiple use cases
• perform authorization checks
• map transport input to use-case input
• coordinate transactions
API services MUST NOT:
• contain domain logic
• enforce business invariants
• build domain entities
• return domain objects
Placement
apps/api/**/ApplicationService.ts
Example
• LeagueApplicationService
API services are delivery-layer coordinators.
3. Core Application Services (Use Cases)
Purpose
Core application services implement business use cases.
They answer the question:
“What does the system do?”
Responsibilities
Use Cases MUST:
• accept primitive input only
• create Value Objects
• create or modify Entities
• enforce business rules
• call repositories via ports
• communicate results via output ports
Use Cases MUST NOT:
• know about HTTP, UI, or frameworks
• return DTOs
• perform persistence directly
Placement
core/<context>/application/commands/
core/<context>/application/queries/
Example
• CreateLeagueUseCase
• ApplyPenaltyUseCase
• GetLeagueStandingsQuery
Use Cases define system behavior.
4. Core Domain Services
Purpose
Domain services encapsulate domain logic that does not belong to a single entity.
They answer the question:
“What rules exist that span multiple domain objects?”
Responsibilities
Domain services MAY:
• coordinate multiple entities
• compute derived domain values
• enforce cross-aggregate rules
Domain services MUST:
• use only domain concepts
• return domain objects or primitives
Domain services MUST NOT:
• access repositories
• depend on application services
• perform IO
Placement
core/<context>/domain/services/
Example
• SeasonConfigurationFactory
• ChampionshipAggregator
• StrengthOfFieldCalculator
Domain services protect business integrity.
Dependency Rules (Non-Negotiable)
Frontend Service
→ API Client
→ API Service
→ Core Use Case
→ Domain Service / Entity
Reverse dependencies are forbidden.
Anti-Patterns (Forbidden)
❌ Frontend calling core directly
❌ API service constructing domain entities
❌ Use case returning DTOs
❌ Domain service accessing repositories
❌ Single class acting as multiple service types
Naming Conventions
Layer Naming
Frontend *Service
API *ApplicationService
Core Application *UseCase, *Query
Core Domain *Service, *Factory, *Calculator
Mental Model (Final)
Services coordinate.
Use Cases decide.
Domain enforces truth.
Adapters translate.
If a class violates this mental model, it is in the wrong layer.
Final Summary
• “Service” means different things in different layers
• Mixing service types causes architectural decay
• Clean Architecture remains simple when roles stay pure
This document defines the only allowed service roles in the system.