diff --git a/plans/2025-12-15T15:42:00Z_clean-architecture-migration.md b/plans/2025-12-15T15:42:00Z_clean-architecture-migration.md new file mode 100644 index 000000000..ca2dd2470 --- /dev/null +++ b/plans/2025-12-15T15:42:00Z_clean-architecture-migration.md @@ -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/ + - 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/ + - PageViewMapper.ts + +Rules: + • Domain ↔ ORM mapping only happens in adapters + • Mappers are pure functions + • Boilerplate is acceptable and expected + +⸻ + +8. Repositories + +Core + +/core//domain/repositories + - IPageViewRepository.ts + +Only interfaces. + +Adapters + +/adapters/persistence/typeorm/ + - PageViewTypeOrmRepository.ts + +/adapters/persistence/inmemory/ + - 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. \ No newline at end of file