This commit is contained in:
2025-12-15 15:09:10 +01:00
parent 95d0bf5aee
commit bc759a7d36

View File

@@ -0,0 +1,278 @@
Clean Architecture plan for GridPilot (NestJS-focused)
This document defines the target architecture and rules for GridPilot.
It is written as a developer-facing contract: what goes where, what is allowed, and what is forbidden.
1. Architectural goals
• Strict Clean Architecture (dependency rule enforced)
• Domain-first design (DDD-inspired)
• Frameworks are delivery mechanisms, not architecture
• Business logic is isolated from persistence, UI, and infrastructure
• Explicit composition roots
• High testability without mocking the domain
2. High-level structure
/apps
/api # NestJS API (delivery mechanism)
/website # Next.js website (delivery mechanism)
/electron # Electron app (delivery mechanism)
/core # Business rules (framework-agnostic)
/analytics
/automation
/identity
/media
/notifications
/racing
/social
/shared
/adapters # Technical implementations (details)
/persistence
/typeorm
/inmemory
/auth
/media
/notifications
/logging
/testing # Test-only code (never shipped)
/contexts
/factories
/builders
/fakes
/fixtures
3. Dependency rule (non-negotiable)
Dependencies must only point inward:
apps → adapters → core
Forbidden:
• core importing from adapters
• core importing from apps
• domain entities importing ORM, NestJS, or framework code
4. Core rules
The Core contains only business rules.
Core MAY contain:
• Domain entities
• Value objects
• Domain services
• Domain events
• Repository interfaces
• Application use cases
• Application-level ports
Core MUST NOT contain:
• ORM entities
• Persistence implementations
• In-memory repositories
• NestJS decorators
• TypeORM decorators
• HTTP / GraphQL / IPC concerns
• Faker, demo data, seeds
5. Domain entities
Domain entities:
• Represent business concepts
• Enforce invariants
• Contain behavior
• Are immutable or controlled via methods
Example characteristics:
• Private constructors
• Static factory methods
• Explicit validation
• Value objects for identity
Domain entities must never:
• Be decorated with @Entity, @Column, etc.
• Be reused as persistence models
• Know how they are stored
6. Persistence entities (ORM)
Persistence entities live in adapters and are data-only.
/adapters/persistence/typeorm/<context>
- PageViewOrmEntity.ts
Rules:
• No business logic
• No validation
• No behavior
• Flat data structures
ORM entities are not domain entities.
7. Mapping (anti-corruption layer)
Mapping between domain and persistence is explicit and isolated.
/adapters/persistence/typeorm/<context>
- PageViewMapper.ts
Rules:
• Domain ↔ ORM mapping only happens in adapters
• Mappers are pure functions
• Boilerplate is acceptable and expected
8. Repositories
Core
/core/<context>/domain/repositories
- IPageViewRepository.ts
Only interfaces.
Adapters
/adapters/persistence/typeorm/<context>
- PageViewTypeOrmRepository.ts
/adapters/persistence/inmemory/<context>
- InMemoryPageViewRepository.ts
Rules:
• Repositories translate between domain entities and storage models
• Repositories implement core interfaces
• Repositories hide all persistence details from the core
9. In-memory repositories
In-memory repositories are test adapters, not core infrastructure.
Rules:
• Never placed in /core
• Allowed only in /adapters/persistence/inmemory
• Used for unit tests and integration tests
• Must behave like real repositories
10. NestJS API (/apps/api)
The NestJS API is a delivery mechanism.
Responsibilities:
• Controllers
• DTOs
• Request validation
• Dependency injection
• Adapter selection (prod vs test)
Forbidden:
• Business logic
• Domain rules
• Persistence logic
NestJS modules are composition roots.
11. Dependency injection
DI is configured only in apps.
Example:
provide: IPageViewRepository
useClass: PageViewTypeOrmRepository
Switching implementations (e.g. InMemory vs TypeORM) happens outside the core.
12. Testing strategy
Domain tests
• Test entities and value objects directly
• No adapters
• No frameworks
Use case tests
• Core + in-memory adapters
• No NestJS
API tests
• NestJS TestingModule
• Explicit adapter overrides
13. Testing helpers
Testing helpers live in /testing.
Contexts
• One context per bounded context
• Provide repositories + use cases
Factories
• Create valid domain objects
• Express intent, not randomness
Builders
• Build complex aggregates fluently
Fakes
• Replace external systems (payments, email, etc.)
14. Read models and projections
Not every query needs a domain entity.
Rules:
• Reports, analytics, dashboards may use DTOs or projections
• No forced mapping into domain entities
• Domain entities are for behavior, not reporting
15. Golden rules
• Domain entities never depend on infrastructure
• ORM entities never contain behavior
• Repositories are anti-corruption layers
• Adapters are replaceable
• Apps wire everything together
• Tests never leak into production code
16. Summary
This architecture ensures:
• Long-term maintainability
• Framework independence
• Safe refactoring
• Clear ownership of responsibilities
If a class violates a rule, it is in the wrong layer.