refactor use cases
This commit is contained in:
264
docs/architecture/DOMAIN_OBJECTS.md
Normal file
264
docs/architecture/DOMAIN_OBJECTS.md
Normal file
@@ -0,0 +1,264 @@
|
||||
Domain Objects Design Guide (Clean Architecture)
|
||||
|
||||
This document defines all domain object types used in the Core and assigns strict responsibilities and boundaries.
|
||||
|
||||
Its goal is to remove ambiguity between:
|
||||
• Entities
|
||||
• Value Objects
|
||||
• Aggregate Roots
|
||||
• Domain Services
|
||||
• Domain Events
|
||||
|
||||
The rules in this document are non-negotiable.
|
||||
|
||||
⸻
|
||||
|
||||
Core Principle
|
||||
|
||||
Domain objects represent business truth.
|
||||
|
||||
They:
|
||||
• outlive APIs and UIs
|
||||
• must remain stable over time
|
||||
• must not depend on technical details
|
||||
|
||||
If a class answers a business question, it belongs here.
|
||||
|
||||
⸻
|
||||
|
||||
1. Entities
|
||||
|
||||
Definition
|
||||
|
||||
An Entity is a domain object that:
|
||||
• has a stable identity
|
||||
• changes over time
|
||||
• represents a business concept
|
||||
|
||||
Identity matters more than attributes.
|
||||
|
||||
⸻
|
||||
|
||||
Responsibilities
|
||||
|
||||
Entities MUST:
|
||||
• own their identity
|
||||
• enforce invariants on state changes
|
||||
• expose behavior, not setters
|
||||
|
||||
Entities MUST NOT:
|
||||
• depend on DTOs or transport models
|
||||
• access repositories or services
|
||||
• perform IO
|
||||
• know about frameworks
|
||||
|
||||
⸻
|
||||
|
||||
Creation Rules
|
||||
• New entities are created via create()
|
||||
• Existing entities are reconstructed via rehydrate()
|
||||
|
||||
core/<context>/domain/entities/
|
||||
|
||||
|
||||
⸻
|
||||
|
||||
Example
|
||||
• League
|
||||
• Season
|
||||
• Race
|
||||
• Driver
|
||||
|
||||
⸻
|
||||
|
||||
2. Value Objects
|
||||
|
||||
Definition
|
||||
|
||||
A Value Object is a domain object that:
|
||||
• has no identity
|
||||
• is immutable
|
||||
• is defined by its value
|
||||
|
||||
⸻
|
||||
|
||||
Responsibilities
|
||||
|
||||
Value Objects MUST:
|
||||
• validate their own invariants
|
||||
• be immutable
|
||||
• be comparable by value
|
||||
|
||||
Value Objects MUST NOT:
|
||||
• contain business workflows
|
||||
• reference entities
|
||||
• perform IO
|
||||
|
||||
⸻
|
||||
|
||||
Creation Rules
|
||||
• create() for new domain meaning
|
||||
• fromX() for interpreting external formats
|
||||
|
||||
core/<context>/domain/value-objects/
|
||||
|
||||
|
||||
⸻
|
||||
|
||||
Example
|
||||
• Money
|
||||
• LeagueName
|
||||
• RaceTimeOfDay
|
||||
• SeasonSchedule
|
||||
|
||||
⸻
|
||||
|
||||
3. Aggregate Roots
|
||||
|
||||
Definition
|
||||
|
||||
An Aggregate Root is an entity that:
|
||||
• acts as the consistency boundary
|
||||
• protects invariants across related entities
|
||||
|
||||
All access to the aggregate happens through the root.
|
||||
|
||||
⸻
|
||||
|
||||
Responsibilities
|
||||
|
||||
Aggregate Roots MUST:
|
||||
• enforce consistency rules
|
||||
• control modifications of child entities
|
||||
|
||||
Aggregate Roots MUST NOT:
|
||||
• expose internal collections directly
|
||||
• allow partial updates bypassing rules
|
||||
|
||||
⸻
|
||||
|
||||
Example
|
||||
• League (root)
|
||||
• Season (root)
|
||||
|
||||
⸻
|
||||
|
||||
4. Domain Services
|
||||
|
||||
Definition
|
||||
|
||||
A Domain Service encapsulates domain logic that:
|
||||
• does not naturally belong to a single entity
|
||||
• involves multiple domain objects
|
||||
|
||||
⸻
|
||||
|
||||
Responsibilities
|
||||
|
||||
Domain Services MAY:
|
||||
• coordinate entities
|
||||
• calculate derived domain values
|
||||
|
||||
Domain Services MUST:
|
||||
• operate only on domain types
|
||||
• remain stateless
|
||||
|
||||
Domain Services MUST NOT:
|
||||
• access repositories
|
||||
• orchestrate use cases
|
||||
• perform IO
|
||||
|
||||
core/<context>/domain/services/
|
||||
|
||||
|
||||
⸻
|
||||
|
||||
Example
|
||||
• SeasonConfigurationFactory
|
||||
• ChampionshipAggregator
|
||||
• StrengthOfFieldCalculator
|
||||
|
||||
⸻
|
||||
|
||||
5. Domain Events
|
||||
|
||||
Definition
|
||||
|
||||
A Domain Event represents something that:
|
||||
• has already happened
|
||||
• is important to the business
|
||||
|
||||
⸻
|
||||
|
||||
Responsibilities
|
||||
|
||||
Domain Events MUST:
|
||||
• be immutable
|
||||
• carry minimal information
|
||||
|
||||
Domain Events MUST NOT:
|
||||
• contain behavior
|
||||
• perform side effects
|
||||
|
||||
core/<context>/domain/events/
|
||||
|
||||
|
||||
⸻
|
||||
|
||||
Example
|
||||
• RaceCompleted
|
||||
• SeasonActivated
|
||||
|
||||
⸻
|
||||
|
||||
6. What Does NOT Belong in Domain Objects
|
||||
|
||||
❌ DTOs
|
||||
❌ API Models
|
||||
❌ View Models
|
||||
❌ Repositories
|
||||
❌ Framework Types
|
||||
❌ Logging
|
||||
❌ Configuration
|
||||
|
||||
If it depends on infrastructure, it does not belong here.
|
||||
|
||||
⸻
|
||||
|
||||
7. Dependency Rules
|
||||
|
||||
Entities → Value Objects
|
||||
Entities → Domain Services
|
||||
Domain Services → Entities
|
||||
|
||||
Reverse dependencies are forbidden.
|
||||
|
||||
⸻
|
||||
|
||||
8. Testing Requirements
|
||||
|
||||
Domain Objects MUST:
|
||||
• have unit tests for invariants
|
||||
• be tested without mocks
|
||||
|
||||
Domain Services MUST:
|
||||
• have deterministic tests
|
||||
|
||||
⸻
|
||||
|
||||
Mental Model (Final)
|
||||
|
||||
Entities protect state.
|
||||
Value Objects protect meaning.
|
||||
Aggregate Roots protect consistency.
|
||||
Domain Services protect cross-entity rules.
|
||||
Domain Events describe facts.
|
||||
|
||||
⸻
|
||||
|
||||
Final Summary
|
||||
• Domain objects represent business truth
|
||||
• They are pure and framework-free
|
||||
• They form the most stable part of the system
|
||||
|
||||
If domain objects are clean, everything else becomes easier.
|
||||
252
docs/architecture/SERVICES.md
Normal file
252
docs/architecture/SERVICES.md
Normal file
@@ -0,0 +1,252 @@
|
||||
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
|
||||
• SeasonApplicationService
|
||||
|
||||
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.
|
||||
Reference in New Issue
Block a user