Files
gridpilot.gg/plans/2025-12-15T15:42:00Z_clean-architecture-migration.md
2025-12-15 15:09:10 +01:00

5.4 KiB

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

  1. 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

  1. 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

  1. 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

  1. 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

  1. 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.

  1. 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

  1. 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

  1. 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

  1. 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.

  1. Dependency injection

DI is configured only in apps.

Example:

provide: IPageViewRepository useClass: PageViewTypeOrmRepository

Switching implementations (e.g. InMemory vs TypeORM) happens outside the core.

  1. 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

  1. 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.)

  1. 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

  1. 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

  1. 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.