feat(core): add 8 AGENTS.md files (package, layer, domain levels)
This commit is contained in:
48
packages/core/AGENTS.md
Normal file
48
packages/core/AGENTS.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# @repo/core — Clean Architecture Core
|
||||
|
||||
Business logic package. All use cases, entities, interfaces, and DI live here.
|
||||
|
||||
## Layers (dependencies point inward only)
|
||||
|
||||
```
|
||||
entities/ → NOTHING (innermost, zero deps)
|
||||
application/ → entities/ only
|
||||
interface-adapters/→ application/, entities/
|
||||
infrastructure/ → application/, entities/, @repo/cms-client, external libs
|
||||
di/ → all internal layers
|
||||
```
|
||||
|
||||
## Import Rules
|
||||
|
||||
| Layer | Can import from | NEVER import from |
|
||||
|---|---|---|
|
||||
| entities/ | NOTHING | Everything else |
|
||||
| application/ | entities/ only | infrastructure/, interface-adapters/ |
|
||||
| interface-adapters/ | application/, entities/ | infrastructure/ |
|
||||
| infrastructure/ | application/, entities/, @repo/cms-client | interface-adapters/, apps/* |
|
||||
| di/ | All internal layers | apps/* |
|
||||
|
||||
## DI Resolution Table
|
||||
|
||||
| Symbol | Interface | Production | Mock |
|
||||
|---|---|---|---|
|
||||
| IUsersRepository | IUsersRepository | PayloadUsersRepository (Plan 3) | MockUsersRepository |
|
||||
| IArticlesRepository | IArticlesRepository | PayloadArticlesRepository (Plan 3) | MockArticlesRepository |
|
||||
| IAuthenticationService | IAuthenticationService | BetterAuthService (future) | MockAuthenticationService |
|
||||
| ITelemetryService | ITelemetryService | OTelSentryService (future) | MockTelemetryService |
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- Models: `{name}.ts` with Zod schema + type export
|
||||
- Errors: `{domain}.ts` with Error subclasses
|
||||
- Interfaces: `{name}.repository.interface.ts` or `{name}.service.interface.ts`
|
||||
- Use cases: `{verb}-{noun}.use-case.ts`
|
||||
- Controllers: `{noun}.controller.ts`
|
||||
- Infra: `{provider}-{name}.repository.ts` or `mock-{name}.repository.ts`
|
||||
- DI modules: `{domain}.module.ts`
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
cd packages/core && pnpm vitest run
|
||||
```
|
||||
28
packages/core/src/application/AGENTS.md
Normal file
28
packages/core/src/application/AGENTS.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Application Layer — Use Cases + Interfaces
|
||||
|
||||
## Rules
|
||||
|
||||
- Imports from entities/ ONLY
|
||||
- NEVER imports from infrastructure/ or interface-adapters/
|
||||
- Repository interfaces define data access contracts
|
||||
- Service interfaces define external service contracts
|
||||
- Use cases get dependencies via `getInjection()` — never direct import of implementations
|
||||
|
||||
## Adding a New Use Case
|
||||
|
||||
1. Create `src/application/use-cases/{domain}/{verb}-{noun}.use-case.ts`
|
||||
2. Get dependencies via DI: `const repo = getInjection("IMyRepository")`
|
||||
3. Implement business logic using entities and interfaces only
|
||||
4. Write test in `tests/unit/use-cases/{domain}/` using `initializeContainer()`/`destroyContainer()` pattern
|
||||
|
||||
## Adding a New Repository Interface
|
||||
|
||||
1. Create `src/application/repositories/{name}.repository.interface.ts`
|
||||
2. Define interface methods returning entity types
|
||||
3. Export from `src/application/repositories/index.ts`
|
||||
4. Create mock implementation in infrastructure/
|
||||
5. Register in DI container (add symbol, module binding)
|
||||
|
||||
## Adding a New Service Interface
|
||||
|
||||
Same as repository, but in `src/application/services/`
|
||||
30
packages/core/src/application/use-cases/auth/AGENTS.md
Normal file
30
packages/core/src/application/use-cases/auth/AGENTS.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Auth Domain — Business Rules
|
||||
|
||||
## Responsibility
|
||||
|
||||
Authentication and authorization: sign-in, sign-up, sign-out, session management.
|
||||
|
||||
## Business Rules
|
||||
|
||||
- Passwords are hashed via IAuthenticationService (never stored plain)
|
||||
- Sessions expire after 7 days (configured in mock, real impl may differ)
|
||||
- Sign-up requires unique username
|
||||
- Sign-in verifies password via IAuthenticationService.verifyPassword()
|
||||
- Sign-out invalidates session and returns blank cookie
|
||||
|
||||
## Error Cases
|
||||
|
||||
- `AuthenticationError` — wrong credentials (sign-in) or username taken (sign-up)
|
||||
- `UnauthenticatedError` — invalid/expired session
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `IUsersRepository` — user lookup and creation
|
||||
- `IAuthenticationService` — password hashing, session management
|
||||
|
||||
## Adding a New Auth Use Case
|
||||
|
||||
1. Create `{verb}-{noun}.use-case.ts` in this folder
|
||||
2. Get deps via `getInjection("IUsersRepository")`, `getInjection("IAuthenticationService")`
|
||||
3. Write test first in `tests/unit/use-cases/auth/`
|
||||
4. Use `initializeContainer()`/`destroyContainer()` pattern in tests
|
||||
29
packages/core/src/application/use-cases/content/AGENTS.md
Normal file
29
packages/core/src/application/use-cases/content/AGENTS.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Content Domain — Business Rules
|
||||
|
||||
## Responsibility
|
||||
|
||||
Article management: creation, retrieval, publishing workflow.
|
||||
|
||||
## Business Rules
|
||||
|
||||
- Articles must have a title and content
|
||||
- Slugs auto-generated from title if not provided
|
||||
- New articles default to "draft" status
|
||||
- Filtering by status, authorId supported
|
||||
- Pagination via limit/offset
|
||||
|
||||
## Error Cases
|
||||
|
||||
- `NotFoundError` — article doesn't exist (future: update/delete)
|
||||
- `UnauthorizedError` — user can't edit article (future)
|
||||
- `InputParseError` — missing required fields (handled by controller)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `IArticlesRepository` — article CRUD operations
|
||||
|
||||
## Adding a New Content Use Case
|
||||
|
||||
1. Create `{verb}-{noun}.use-case.ts` in this folder
|
||||
2. Get deps via `getInjection("IArticlesRepository")`
|
||||
3. Write test first in `tests/unit/use-cases/content/`
|
||||
37
packages/core/src/di/AGENTS.md
Normal file
37
packages/core/src/di/AGENTS.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# DI — InversifyJS Container
|
||||
|
||||
## Resolution Table
|
||||
|
||||
| Symbol Key | Interface | Production | Mock |
|
||||
|---|---|---|---|
|
||||
| IUsersRepository | IUsersRepository | (future) | MockUsersRepository |
|
||||
| IArticlesRepository | IArticlesRepository | (future) | MockArticlesRepository |
|
||||
| IAuthenticationService | IAuthenticationService | (future) | MockAuthenticationService |
|
||||
| ITelemetryService | ITelemetryService | (future) | MockTelemetryService |
|
||||
|
||||
## How to Register a New Dependency
|
||||
|
||||
1. Add Symbol to `types.ts` → `DI_SYMBOLS`
|
||||
2. Add return type to `DI_RETURN_TYPES` interface
|
||||
3. Create module in `modules/{domain}.module.ts`
|
||||
4. Bind interface to implementation (production) and mock (test)
|
||||
5. Load module in `container.ts` → `initializeContainer()` and `destroyContainer()`
|
||||
|
||||
## Container Lifecycle
|
||||
|
||||
- Production: `initializeContainer()` runs automatically (not in test env)
|
||||
- Tests: Call `initializeContainer()` in `beforeEach`, `destroyContainer()` in `afterEach`
|
||||
- Test environments swap to mock implementations via module bindings
|
||||
|
||||
## DO NOT
|
||||
|
||||
- Import from apps/*
|
||||
- Import framework-specific code (Next.js, TanStack, etc.)
|
||||
- Use the container outside of this package — expose via `getInjection()` only
|
||||
|
||||
## tsconfig Requirements (DO NOT REMOVE)
|
||||
|
||||
- `experimentalDecorators: true`
|
||||
- `emitDecoratorMetadata: true`
|
||||
- `types: ["reflect-metadata"]`
|
||||
- `import "reflect-metadata"` at top of container.ts
|
||||
33
packages/core/src/entities/AGENTS.md
Normal file
33
packages/core/src/entities/AGENTS.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Entities Layer — Innermost, Zero Dependencies
|
||||
|
||||
## Rules
|
||||
|
||||
- NEVER import from application/, infrastructure/, interface-adapters/, or di/
|
||||
- NEVER import external libraries except Zod (for schema validation)
|
||||
- Everything here is pure — no side effects, no I/O, no async
|
||||
- Models are Zod schemas with inferred TypeScript types
|
||||
- Errors are custom Error subclasses with domain-specific semantics
|
||||
|
||||
## Adding a New Model
|
||||
|
||||
1. Create `src/entities/models/{name}.ts`
|
||||
2. Define Zod schema and export inferred type:
|
||||
```typescript
|
||||
import { z } from "zod";
|
||||
export const {name}Schema = z.object({ ... });
|
||||
export type {Name} = z.infer<typeof {name}Schema>;
|
||||
```
|
||||
3. Export from `src/entities/models/index.ts`
|
||||
|
||||
## Adding a New Error
|
||||
|
||||
1. Create or edit `src/entities/errors/{domain}.ts`
|
||||
2. Extend Error with constructor accepting message + options:
|
||||
```typescript
|
||||
export class {Name}Error extends Error {
|
||||
constructor(message: string, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
}
|
||||
}
|
||||
```
|
||||
3. Export from `src/entities/errors/index.ts`
|
||||
24
packages/core/src/infrastructure/AGENTS.md
Normal file
24
packages/core/src/infrastructure/AGENTS.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Infrastructure Layer — Implementations
|
||||
|
||||
## Rules
|
||||
|
||||
- Implements interfaces from application/
|
||||
- Imports from application/ and entities/
|
||||
- NEVER imported by application/ or entities/
|
||||
- Can import external libraries (Drizzle, Better Auth, Sentry, etc.)
|
||||
- Can import @repo/cms-client
|
||||
- Always provide a mock implementation for every real implementation
|
||||
|
||||
## Naming
|
||||
|
||||
- Real: `{provider}-{name}.repository.ts` (e.g., `payload-users.repository.ts`)
|
||||
- Mock: `mock-{name}.repository.ts`
|
||||
- All implementations must use `@injectable()` decorator for InversifyJS
|
||||
|
||||
## Adding a New Implementation
|
||||
|
||||
1. Create `src/infrastructure/repositories/{provider}-{name}.repository.ts`
|
||||
2. Implement the interface from application/
|
||||
3. Add `@injectable()` decorator
|
||||
4. Create corresponding mock: `mock-{name}.repository.ts`
|
||||
5. Register both in DI module (production binds real, test binds mock)
|
||||
29
packages/core/src/interface-adapters/controllers/AGENTS.md
Normal file
29
packages/core/src/interface-adapters/controllers/AGENTS.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Controllers — Interface Adapters
|
||||
|
||||
## Rules
|
||||
|
||||
- Controllers validate input using Zod schemas from entities/
|
||||
- Controllers call use cases — NEVER contain business logic themselves
|
||||
- Controllers handle error mapping (domain errors to appropriate responses)
|
||||
- Import from application/ and entities/ only
|
||||
- NEVER import from infrastructure/
|
||||
|
||||
## Pattern
|
||||
|
||||
```typescript
|
||||
const inputSchema = z.object({ ... });
|
||||
|
||||
export async function myController(input: Partial<z.infer<typeof inputSchema>>) {
|
||||
const { data, error } = inputSchema.safeParse(input);
|
||||
if (error) throw new InputParseError("Invalid data", { cause: error });
|
||||
return await myUseCase(data);
|
||||
}
|
||||
```
|
||||
|
||||
## Adding a New Controller
|
||||
|
||||
1. Create `src/interface-adapters/controllers/{domain}/{name}.controller.ts`
|
||||
2. Define Zod input schema
|
||||
3. Validate input with `safeParse`, throw `InputParseError` on failure
|
||||
4. Call use case and return result
|
||||
5. Write test in `tests/unit/controllers/{domain}/`
|
||||
Reference in New Issue
Block a user