wip
This commit is contained in:
@@ -1,22 +1,24 @@
|
|||||||
Clean Architecture plan for GridPilot (NestJS-focused)
|
# Clean Architecture plan for GridPilot (NestJS‑focused)
|
||||||
|
|
||||||
This document defines the target architecture and rules for GridPilot.
|
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.
|
It is written as a **developer-facing contract**: what goes where, what is allowed, and what is forbidden.
|
||||||
|
|
||||||
⸻
|
---
|
||||||
|
|
||||||
1. Architectural goals
|
## 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
|
|
||||||
|
|
||||||
⸻
|
* 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
|
---
|
||||||
|
|
||||||
|
## 2. High-level structure
|
||||||
|
|
||||||
|
```
|
||||||
/apps
|
/apps
|
||||||
/api # NestJS API (delivery mechanism)
|
/api # NestJS API (delivery mechanism)
|
||||||
/website # Next.js website (delivery mechanism)
|
/website # Next.js website (delivery mechanism)
|
||||||
@@ -47,232 +49,581 @@ It is written as a developer-facing contract: what goes where, what is allowed,
|
|||||||
/builders
|
/builders
|
||||||
/fakes
|
/fakes
|
||||||
/fixtures
|
/fixtures
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
⸻
|
## 3. Dependency rule (non-negotiable)
|
||||||
|
|
||||||
3. Dependency rule (non-negotiable)
|
Dependencies **must only point inward**:
|
||||||
|
|
||||||
Dependencies must only point inward:
|
|
||||||
|
|
||||||
|
```
|
||||||
apps → adapters → core
|
apps → adapters → core
|
||||||
|
```
|
||||||
|
|
||||||
Forbidden:
|
Forbidden:
|
||||||
• core importing from adapters
|
|
||||||
• core importing from apps
|
|
||||||
• domain entities importing ORM, NestJS, or framework code
|
|
||||||
|
|
||||||
⸻
|
* `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.
|
## 4. Core rules
|
||||||
|
|
||||||
Core MAY contain:
|
The Core contains **only business rules**.
|
||||||
• Domain entities
|
|
||||||
• Value objects
|
|
||||||
• Domain services
|
|
||||||
• Domain events
|
|
||||||
• Repository interfaces
|
|
||||||
• Application use cases
|
|
||||||
• Application-level ports
|
|
||||||
|
|
||||||
Core MUST NOT contain:
|
### Core MAY contain:
|
||||||
• ORM entities
|
|
||||||
• Persistence implementations
|
|
||||||
• In-memory repositories
|
|
||||||
• NestJS decorators
|
|
||||||
• TypeORM decorators
|
|
||||||
• HTTP / GraphQL / IPC concerns
|
|
||||||
• Faker, demo data, seeds
|
|
||||||
|
|
||||||
⸻
|
* Domain entities
|
||||||
|
* Value objects
|
||||||
|
* Domain services
|
||||||
|
* Domain events
|
||||||
|
* Repository interfaces
|
||||||
|
* Application use cases
|
||||||
|
* Application-level ports
|
||||||
|
|
||||||
5. Domain entities
|
### 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:
|
Domain entities:
|
||||||
• Represent business concepts
|
|
||||||
• Enforce invariants
|
* Represent business concepts
|
||||||
• Contain behavior
|
* Enforce invariants
|
||||||
• Are immutable or controlled via methods
|
* Contain behavior
|
||||||
|
* Are immutable or controlled via methods
|
||||||
|
|
||||||
Example characteristics:
|
Example characteristics:
|
||||||
• Private constructors
|
|
||||||
• Static factory methods
|
|
||||||
• Explicit validation
|
|
||||||
• Value objects for identity
|
|
||||||
|
|
||||||
Domain entities must never:
|
* Private constructors
|
||||||
• Be decorated with @Entity, @Column, etc.
|
* Static factory methods
|
||||||
• Be reused as persistence models
|
* Explicit validation
|
||||||
• Know how they are stored
|
* Value objects for identity
|
||||||
|
|
||||||
⸻
|
Domain entities **must never**:
|
||||||
|
|
||||||
6. Persistence entities (ORM)
|
* Be decorated with `@Entity`, `@Column`, etc.
|
||||||
|
* Be reused as persistence models
|
||||||
|
* Know how they are stored
|
||||||
|
|
||||||
Persistence entities live in adapters and are data-only.
|
---
|
||||||
|
|
||||||
|
## 6. Persistence entities (ORM)
|
||||||
|
|
||||||
|
Persistence entities live in adapters and are **data-only**.
|
||||||
|
|
||||||
|
```
|
||||||
/adapters/persistence/typeorm/<context>
|
/adapters/persistence/typeorm/<context>
|
||||||
- PageViewOrmEntity.ts
|
- PageViewOrmEntity.ts
|
||||||
|
```
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
• No business logic
|
|
||||||
• No validation
|
|
||||||
• No behavior
|
|
||||||
• Flat data structures
|
|
||||||
|
|
||||||
ORM entities are not domain entities.
|
* No business logic
|
||||||
|
* No validation
|
||||||
|
* No behavior
|
||||||
|
* Flat data structures
|
||||||
|
|
||||||
⸻
|
ORM entities are **not domain entities**.
|
||||||
|
|
||||||
7. Mapping (anti-corruption layer)
|
---
|
||||||
|
|
||||||
|
## 7. Mapping (anti-corruption layer)
|
||||||
|
|
||||||
Mapping between domain and persistence is explicit and isolated.
|
Mapping between domain and persistence is explicit and isolated.
|
||||||
|
|
||||||
|
```
|
||||||
/adapters/persistence/typeorm/<context>
|
/adapters/persistence/typeorm/<context>
|
||||||
- PageViewMapper.ts
|
- PageViewMapper.ts
|
||||||
|
```
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
• Domain ↔ ORM mapping only happens in adapters
|
|
||||||
• Mappers are pure functions
|
|
||||||
• Boilerplate is acceptable and expected
|
|
||||||
|
|
||||||
⸻
|
* Domain ↔ ORM mapping only happens in adapters
|
||||||
|
* Mappers are pure functions
|
||||||
|
* Boilerplate is acceptable and expected
|
||||||
|
|
||||||
8. Repositories
|
---
|
||||||
|
|
||||||
Core
|
## 8. Repositories
|
||||||
|
|
||||||
|
### Core
|
||||||
|
|
||||||
|
```
|
||||||
/core/<context>/domain/repositories
|
/core/<context>/domain/repositories
|
||||||
- IPageViewRepository.ts
|
- IPageViewRepository.ts
|
||||||
|
```
|
||||||
|
|
||||||
Only interfaces.
|
Only interfaces.
|
||||||
|
|
||||||
Adapters
|
### Adapters
|
||||||
|
|
||||||
|
```
|
||||||
/adapters/persistence/typeorm/<context>
|
/adapters/persistence/typeorm/<context>
|
||||||
- PageViewTypeOrmRepository.ts
|
- PageViewTypeOrmRepository.ts
|
||||||
|
|
||||||
/adapters/persistence/inmemory/<context>
|
/adapters/persistence/inmemory/<context>
|
||||||
- InMemoryPageViewRepository.ts
|
- InMemoryPageViewRepository.ts
|
||||||
|
```
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
• Repositories translate between domain entities and storage models
|
|
||||||
• Repositories implement core interfaces
|
|
||||||
• Repositories hide all persistence details from the core
|
|
||||||
|
|
||||||
⸻
|
* 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.
|
## 9. In-memory repositories
|
||||||
|
|
||||||
|
In-memory repositories are **test adapters**, not core infrastructure.
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
• Never placed in /core
|
|
||||||
• Allowed only in /adapters/persistence/inmemory
|
|
||||||
• Used for unit tests and integration tests
|
|
||||||
• Must behave like real repositories
|
|
||||||
|
|
||||||
⸻
|
* 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.
|
## 10. NestJS API (`/apps/api`)
|
||||||
|
|
||||||
Responsibilities:
|
The NestJS API is a **delivery mechanism**.
|
||||||
• Controllers
|
|
||||||
• DTOs
|
|
||||||
• Request validation
|
|
||||||
• Dependency injection
|
|
||||||
• Adapter selection (prod vs test)
|
|
||||||
|
|
||||||
Forbidden:
|
### Responsibilities:
|
||||||
• Business logic
|
|
||||||
• Domain rules
|
|
||||||
• Persistence logic
|
|
||||||
|
|
||||||
NestJS modules are composition roots.
|
* Controllers
|
||||||
|
* DTOs
|
||||||
|
* Request validation
|
||||||
|
* Dependency injection
|
||||||
|
* Adapter selection (prod vs test)
|
||||||
|
|
||||||
⸻
|
### Forbidden:
|
||||||
|
|
||||||
11. Dependency injection
|
* Business logic
|
||||||
|
* Domain rules
|
||||||
|
* Persistence logic
|
||||||
|
|
||||||
DI is configured only in apps.
|
NestJS modules are **composition roots**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Dependency injection
|
||||||
|
|
||||||
|
DI is configured **only in apps**.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
provide: IPageViewRepository
|
provide: IPageViewRepository
|
||||||
useClass: PageViewTypeOrmRepository
|
useClass: PageViewTypeOrmRepository
|
||||||
|
```
|
||||||
|
|
||||||
Switching implementations (e.g. InMemory vs TypeORM) happens outside the core.
|
Switching implementations (e.g. InMemory vs TypeORM) happens **outside the core**.
|
||||||
|
|
||||||
⸻
|
---
|
||||||
|
|
||||||
12. Testing strategy
|
## 12. Testing strategy
|
||||||
|
|
||||||
Domain tests
|
Tests are **clients of the system**, not part of the architecture.
|
||||||
• Test entities and value objects directly
|
|
||||||
• No adapters
|
|
||||||
• No frameworks
|
|
||||||
|
|
||||||
Use case tests
|
### Core principles
|
||||||
• Core + in-memory adapters
|
|
||||||
• No NestJS
|
|
||||||
|
|
||||||
API tests
|
* `/tests` contains test cases
|
||||||
• NestJS TestingModule
|
* `/testing` contains **test support only** (helpers, factories, fixtures)
|
||||||
• Explicit adapter overrides
|
* Production code must never import from `/testing`
|
||||||
|
|
||||||
⸻
|
---
|
||||||
|
|
||||||
13. Testing helpers
|
## 13. Test support structure (`/testing`)
|
||||||
|
|
||||||
Testing helpers live in /testing.
|
The `/testing` folder is a **test-support library**, not a layer.
|
||||||
|
It follows a single, strict structure:
|
||||||
|
|
||||||
Contexts
|
```
|
||||||
• One context per bounded context
|
/testing
|
||||||
• Provide repositories + use cases
|
/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
|
||||||
|
```
|
||||||
|
|
||||||
Factories
|
---
|
||||||
• Create valid domain objects
|
|
||||||
• Express intent, not randomness
|
|
||||||
|
|
||||||
Builders
|
## 14. Rules for test support
|
||||||
• Build complex aggregates fluently
|
|
||||||
|
|
||||||
Fakes
|
The project uses **factories only**.
|
||||||
• Replace external systems (payments, email, etc.)
|
There is **no separate concept of fixtures**.
|
||||||
|
|
||||||
⸻
|
All test data is produced via factories.
|
||||||
|
This avoids duplication, drift, and ambiguity.
|
||||||
|
|
||||||
14. Read models and projections
|
---
|
||||||
|
|
||||||
Not every query needs a domain entity.
|
### Factories (the single source of test data)
|
||||||
|
|
||||||
|
Factories are the **only approved way** to create test data.
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
• Reports, analytics, dashboards may use DTOs or projections
|
|
||||||
• No forced mapping into domain entities
|
|
||||||
• Domain entities are for behavior, not reporting
|
|
||||||
|
|
||||||
⸻
|
* 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
|
||||||
|
|
||||||
15. Golden rules
|
Factories must **NOT**:
|
||||||
• 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
|
|
||||||
|
|
||||||
⸻
|
* Use repositories
|
||||||
|
* Perform IO
|
||||||
|
* Contain assertions
|
||||||
|
* Contain domain logic
|
||||||
|
|
||||||
16. Summary
|
---
|
||||||
|
|
||||||
This architecture ensures:
|
### Fakes
|
||||||
• Long-term maintainability
|
|
||||||
• Framework independence
|
|
||||||
• Safe refactoring
|
|
||||||
• Clear ownership of responsibilities
|
|
||||||
|
|
||||||
If a class violates a rule, it is in the wrong layer.
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Hard separation
|
||||||
|
|
||||||
|
| Concern | Purpose | Location |
|
||||||
|
| --------- | ------------------- | ------------------------------- |
|
||||||
|
| Migration | Schema | adapters/persistence/migrations |
|
||||||
|
| Bootstrap | Required start data | adapters/bootstrap |
|
||||||
|
| 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.
|
||||||
|
|||||||
Reference in New Issue
Block a user