# 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

Tests are **clients of the system**, not part of the architecture.

### Core principles

* `/tests` contains test cases
* `/testing` contains **test support only** (helpers, factories, fixtures)
* Production code must never import from `/testing`

---

## 13. Test support structure (`/testing`)

The `/testing` folder is a **test-support library**, not a layer.
It follows a single, strict structure:

```
/testing
  /fixtures        # Static reference data (NO logic, NO faker)
  /factories       # Domain object factories (faker allowed ONLY here)
  /fakes           # Fake implementations of external ports
  /helpers         # Generic test utilities (time, ids, assertions)
  index.ts
```

---

## 14. Rules for test support

The project uses **factories only**.
There is **no separate concept of fixtures**.

All test data is produced via factories.
This avoids duplication, drift, and ambiguity.

---

### Factories (the single source of test data)

Factories are the **only approved way** to create test data.

Rules:

* Factories create **valid domain objects** or **valid DTOs** (boundary only)
* One factory per concept
* Stateless
* One export per file
* File name === exported symbol
* Faker / randomness allowed here

Factories must **NOT**:

* Use repositories
* Perform IO
* Contain assertions
* Contain domain logic

---

### Fakes

Fakes replace **external systems only**.

Rules:

* Implement application ports
* Explicit behavior
* One export per file
* No domain logic

---

### Helpers

Helpers are **pure utilities**.

Rules:

* No domain knowledge
* No adapters
* No randomness

---

### Factories

* Create **valid domain objects**
* Stateless
* Deterministic unless randomness is required
* Faker is allowed **only here**

Factories **must NOT**:

* Use repositories
* Use DTOs
* Perform IO

---

### Fakes

* Implement application ports
* Replace external systems (payments, email, auth, etc.)
* Explicit, controllable behavior

---

### Helpers

* Pure utilities
* No domain logic
* No adapters

---

## 15. DTO usage in tests (strict rule)

DTOs are **boundary objects**, not domain concepts.

Rules:

* Domain tests: **never** use DTOs
* Domain factories: **never** use DTOs
* Use case tests: DTOs allowed **only if the use case explicitly accepts a DTO**
* API / controller tests: DTOs are expected and allowed

If DTOs are needed in tests, they must be created explicitly, e.g.:

```
/testing/dto-factories
```

This makes boundary tests obvious and prevents accidental coupling.

---

## 16. Concrete pseudo examples (authoritative)

The following examples show the **only intended usage**, aligned with **Screaming Architecture** and **one-export-per-file**.

Naming and placement are part of the rule.

---

### Naming rules (strict)

* One file = one export
* File name === exported symbol
* PascalCase only
* No suffixes like `.factory`, `.fixture`, `.fake`

Correct:

```
PageViewFactory.ts
PageViewFixture.ts
FakePaymentGateway.ts
```

---

### Example: Domain entity

```ts
// core/analytics/domain/entities/PageView.ts
export class PageView {
  static create(props) {}
  isMeaningfulView(): boolean {}
}
```

---

### Example: Fixture (static data)

```ts
// testing/analytics/fixtures/PageViewFixture.ts
export const PageViewFixture = {
  id: 'pv-1',
  entityType: 'league',
  entityId: 'l-1',
  visitorType: 'guest',
  sessionId: 's-1',
};
```

Rules demonstrated:

* Plain object
* One export
* No logic
* No faker

---

### Example: Factory (domain object creation)

```ts
// testing/analytics/factories/PageViewFactory.ts
export class PageViewFactory {
  static create(overrides = {}) {
    return PageView.create({
      id: randomId(),
      entityType: 'league',
      entityId: randomId(),
      visitorType: 'guest',
      sessionId: randomId(),
      ...overrides,
    });
  }
}
```

Rules demonstrated:

* Produces domain entity
* Faker / randomness allowed here
* One export per file
* No DTOs
* No repositories

---

### Example: Fake (external port)

```ts
// testing/racing/fakes/FakePaymentGateway.ts
export class FakePaymentGateway implements PaymentGatewayPort {
  charge() {
    return { success: true };
  }
}
```

Rules demonstrated:

* Implements a port
* Simulates external system
* One export per file
* No domain logic

---

### Example: Domain test

```ts
// tests/analytics/PageView.spec.ts
const pageView = PageViewFactory.create({ durationMs: 6000 });

expect(pageView.isMeaningfulView()).toBe(true);
```

Rules demonstrated:

* Talks domain language
* No DTOs
* No framework

---

### Example: Use case test (DTO allowed only if required)

```ts
// tests/analytics/RecordPageView.spec.ts
const command = RecordPageViewCommandDTO.create({ ... });

useCase.execute(command);
```

Rules demonstrated:

* DTO only used because it is the explicit input
* Boundary is under test

---

### Example: API test

```ts
// tests/api/analytics.e2e.spec.ts
POST /api/page-views
body = RecordPageViewCommandDTO
```

Rules demonstrated:

* DTOs expected
* HTTP boundary
* Full stack

---

## 17. Migration vs Bootstrap (production data)

Certain data exists **not because of business rules**, but because the application must be operable.
This is **not a testing concern** and **not a domain concern**.

---

### Migrations

**Purpose:**

* Create or evolve the database schema

Characteristics:

* Tables, columns, indices, constraints
* Versioned
* Deterministic
* Idempotent per version

Rules:

* No domain logic
* No factories
* No faker
* No test helpers
* No business decisions

Location:

```
/adapters/persistence/migrations
```

---

### Bootstrap (initial application data)

**Purpose:**

* Ensure the system is usable after first start

Examples:

* Initial admin user
* System roles
* Required system configuration entries

Characteristics:

* Runs at application startup
* Idempotent
* Environment-driven

Rules:

* Must NOT live in core
* Must NOT live in testing
* Must NOT live in repositories
* Must use application use cases

Location:

```
/adapters/bootstrap
```

Example (pseudo):

```ts
class EnsureInitialAdminUser {
  run(): Promise<void> {}
}
```

The application (e.g. NestJS) decides **when** this runs.
Adapters decide **how** it runs.

---

### Reference Data (registries exposed via API)

Some “data” is neither **domain behavior** nor **UI config**. It is **authoritative reference data** that outer layers need (website, API DTOs), but it is not a domain entity itself.

Examples:

* Scoring preset registries (preset identifiers, point systems, drop-week policies, default timing presets)
* Catalog-like lists needed by the UI to render forms safely without duplicating domain semantics

Rules:

* The website (presentation) must never own or hardcode domain catalogs.
* Core owns invariants, enumerations, and validation rules. If a rule is an invariant, it belongs in Core.
* Adapters may own **reference registries** when they are not domain entities, and when they represent curated “known lists” used for configuration and defaults.
* The API must expose reference registries via HTTP so the website remains a pure API consumer (no importing adapters or core).
* UI-only concerns (labels, icons, colors, layout defaults) remain in the website.

---

### Hard separation

| Concern        | Purpose                                        | Location                        |
| -------------- | ---------------------------------------------- | ------------------------------- |
| Migration      | Schema                                         | adapters/persistence/migrations |
| Bootstrap      | Required start data                             | adapters/bootstrap              |
| Reference Data | Authoritative registries consumed via API       | adapters/bootstrap + apps/api   |
| Factory        | Test data                                      | testing                         |

---

## 18. Final enforcement rules

* Domain knows nothing about migrations or bootstrap
* Repositories never auto-create data
* Factories are test-only
* Bootstrap is explicit and idempotent
* Migrations and bootstrap are never mixed

This section is authoritative.
