514 lines
11 KiB
Markdown
514 lines
11 KiB
Markdown
# Backup Service Development Guide
|
|
|
|
## Prerequisites
|
|
|
|
- **Node.js:** v20.x or higher
|
|
- **npm:** v10.x or higher
|
|
- **Docker:** For running PostgreSQL locally
|
|
- **WSL2:** (Windows users) For running Docker and tests
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
### 1. Clone and Install
|
|
|
|
```bash
|
|
# Navigate to service directory
|
|
cd backend/services/backup-service
|
|
|
|
# Install dependencies
|
|
npm install
|
|
```
|
|
|
|
### 2. Environment Setup
|
|
|
|
Copy the example environment file:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
Configure the following environment variables:
|
|
|
|
```bash
|
|
# Database Configuration
|
|
DATABASE_URL="postgresql://postgres:password@localhost:5433/rwa_backup?schema=public"
|
|
|
|
# Server Configuration
|
|
APP_PORT=3002
|
|
APP_ENV="development"
|
|
|
|
# Service-to-Service Authentication
|
|
SERVICE_JWT_SECRET="your-super-secret-service-jwt-key-min-32-chars"
|
|
ALLOWED_SERVICES="identity-service,recovery-service"
|
|
|
|
# Encryption
|
|
BACKUP_ENCRYPTION_KEY="your-256-bit-encryption-key-in-hex-64-chars"
|
|
BACKUP_ENCRYPTION_KEY_ID="key-v1"
|
|
|
|
# Rate Limiting
|
|
MAX_RETRIEVE_PER_DAY=3
|
|
MAX_STORE_PER_MINUTE=10
|
|
|
|
# Audit
|
|
AUDIT_LOG_RETENTION_DAYS=365
|
|
```
|
|
|
|
### 3. Start Database
|
|
|
|
```bash
|
|
# Start PostgreSQL container
|
|
docker-compose up -d
|
|
|
|
# Verify database is running
|
|
docker-compose ps
|
|
```
|
|
|
|
### 4. Setup Database Schema
|
|
|
|
```bash
|
|
# Generate Prisma client
|
|
npm run prisma:generate
|
|
|
|
# Run migrations
|
|
npm run prisma:migrate
|
|
```
|
|
|
|
### 5. Start Development Server
|
|
|
|
```bash
|
|
# Start with hot-reload
|
|
npm run start:dev
|
|
|
|
# Or start in debug mode
|
|
npm run start:debug
|
|
```
|
|
|
|
The service will be available at `http://localhost:3002`.
|
|
|
|
---
|
|
|
|
## Project Scripts
|
|
|
|
### Development
|
|
|
|
| Script | Description |
|
|
|--------|-------------|
|
|
| `npm run start` | Start the service |
|
|
| `npm run start:dev` | Start with hot-reload |
|
|
| `npm run start:debug` | Start with debugger |
|
|
| `npm run start:prod` | Start production build |
|
|
| `npm run build` | Build for production |
|
|
|
|
### Database
|
|
|
|
| Script | Description |
|
|
|--------|-------------|
|
|
| `npm run prisma:generate` | Generate Prisma client |
|
|
| `npm run prisma:migrate` | Run development migrations |
|
|
| `npm run prisma:migrate:prod` | Run production migrations |
|
|
| `npm run prisma:studio` | Open Prisma Studio GUI |
|
|
|
|
### Testing
|
|
|
|
| Script | Description |
|
|
|--------|-------------|
|
|
| `npm run test` | Run all tests |
|
|
| `npm run test:unit` | Run unit tests only |
|
|
| `npm run test:e2e:mock` | Run E2E tests with mocks |
|
|
| `npm run test:e2e:db` | Run E2E tests with real DB |
|
|
| `npm run test:cov` | Generate coverage report |
|
|
| `npm run test:watch` | Run tests in watch mode |
|
|
|
|
### Docker
|
|
|
|
| Script | Description |
|
|
|--------|-------------|
|
|
| `npm run docker:build` | Build Docker image |
|
|
| `npm run docker:up` | Start Docker compose stack |
|
|
| `npm run docker:down` | Stop Docker compose stack |
|
|
| `npm run db:test:up` | Start test database |
|
|
| `npm run db:test:down` | Stop test database |
|
|
|
|
---
|
|
|
|
## Code Structure
|
|
|
|
### Adding a New Feature
|
|
|
|
Follow the DDD + Hexagonal architecture pattern:
|
|
|
|
#### 1. Domain Layer (Core Business Logic)
|
|
|
|
Create entities and value objects first:
|
|
|
|
```typescript
|
|
// src/domain/entities/new-entity.entity.ts
|
|
export class NewEntity {
|
|
// Properties
|
|
private id: bigint;
|
|
private data: string;
|
|
|
|
// Factory method
|
|
static create(params: CreateParams): NewEntity {
|
|
// Validation and creation logic
|
|
return new NewEntity(params);
|
|
}
|
|
|
|
// Domain methods
|
|
performAction(): void {
|
|
// Business logic
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2. Repository Interface
|
|
|
|
Define the data access contract:
|
|
|
|
```typescript
|
|
// src/domain/repositories/new-entity.repository.interface.ts
|
|
export interface NewEntityRepository {
|
|
save(entity: NewEntity): Promise<NewEntity>;
|
|
findById(id: bigint): Promise<NewEntity | null>;
|
|
// ... other methods
|
|
}
|
|
```
|
|
|
|
#### 3. Application Layer (Use Cases)
|
|
|
|
Create commands/queries and handlers:
|
|
|
|
```typescript
|
|
// src/application/commands/create-new-entity/create-new-entity.command.ts
|
|
export class CreateNewEntityCommand {
|
|
constructor(
|
|
public readonly data: string,
|
|
// ... other fields
|
|
) {}
|
|
}
|
|
|
|
// src/application/commands/create-new-entity/create-new-entity.handler.ts
|
|
@Injectable()
|
|
export class CreateNewEntityHandler {
|
|
constructor(
|
|
@Inject('NewEntityRepository')
|
|
private readonly repository: NewEntityRepository,
|
|
) {}
|
|
|
|
async execute(command: CreateNewEntityCommand): Promise<Result> {
|
|
const entity = NewEntity.create({ data: command.data });
|
|
await this.repository.save(entity);
|
|
return { id: entity.id.toString() };
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 4. Infrastructure Layer
|
|
|
|
Implement the repository:
|
|
|
|
```typescript
|
|
// src/infrastructure/persistence/repositories/new-entity.repository.impl.ts
|
|
@Injectable()
|
|
export class NewEntityRepositoryImpl implements NewEntityRepository {
|
|
constructor(private readonly prisma: PrismaService) {}
|
|
|
|
async save(entity: NewEntity): Promise<NewEntity> {
|
|
const data = await this.prisma.newEntity.create({
|
|
data: this.toDatabase(entity),
|
|
});
|
|
return this.toDomain(data);
|
|
}
|
|
|
|
// Mapping methods
|
|
private toDatabase(entity: NewEntity) { /* ... */ }
|
|
private toDomain(data: PrismaModel) { /* ... */ }
|
|
}
|
|
```
|
|
|
|
#### 5. API Layer
|
|
|
|
Create controller and DTOs:
|
|
|
|
```typescript
|
|
// src/api/dto/request/create-new-entity.dto.ts
|
|
export class CreateNewEntityDto {
|
|
@IsNotEmpty()
|
|
@IsString()
|
|
data: string;
|
|
}
|
|
|
|
// src/api/controllers/new-entity.controller.ts
|
|
@Controller('new-entity')
|
|
@UseGuards(ServiceAuthGuard)
|
|
export class NewEntityController {
|
|
constructor(
|
|
private readonly service: NewEntityApplicationService,
|
|
) {}
|
|
|
|
@Post()
|
|
async create(@Body() dto: CreateNewEntityDto) {
|
|
return this.service.create(dto);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
### Required Variables
|
|
|
|
| Variable | Description | Example |
|
|
|----------|-------------|---------|
|
|
| `DATABASE_URL` | PostgreSQL connection string | `postgresql://...` |
|
|
| `SERVICE_JWT_SECRET` | JWT secret (min 32 chars) | `your-secret-key...` |
|
|
| `ALLOWED_SERVICES` | Comma-separated service list | `identity-service,recovery-service` |
|
|
| `BACKUP_ENCRYPTION_KEY` | 256-bit key in hex (64 chars) | `0123456789abcdef...` |
|
|
| `BACKUP_ENCRYPTION_KEY_ID` | Key identifier for rotation | `key-v1` |
|
|
|
|
### Optional Variables
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `APP_PORT` | `3002` | Server port |
|
|
| `APP_ENV` | `development` | Environment mode |
|
|
| `MAX_RETRIEVE_PER_DAY` | `3` | Rate limit for retrieves |
|
|
| `MAX_STORE_PER_MINUTE` | `10` | Rate limit for stores |
|
|
| `AUDIT_LOG_RETENTION_DAYS` | `365` | Audit log retention |
|
|
|
|
---
|
|
|
|
## Database Management
|
|
|
|
### Prisma CLI Commands
|
|
|
|
```bash
|
|
# Generate Prisma client after schema changes
|
|
npx prisma generate
|
|
|
|
# Create a new migration
|
|
npx prisma migrate dev --name migration_name
|
|
|
|
# Apply migrations in production
|
|
npx prisma migrate deploy
|
|
|
|
# Reset database (development only)
|
|
npx prisma migrate reset
|
|
|
|
# Open Prisma Studio
|
|
npx prisma studio
|
|
|
|
# Push schema without migrations (development)
|
|
npx prisma db push
|
|
```
|
|
|
|
### Schema Changes Workflow
|
|
|
|
1. Modify `prisma/schema.prisma`
|
|
2. Create migration: `npx prisma migrate dev --name descriptive_name`
|
|
3. Test locally
|
|
4. Commit migration files
|
|
5. Apply in staging/production: `npx prisma migrate deploy`
|
|
|
|
---
|
|
|
|
## Debugging
|
|
|
|
### VSCode Launch Configuration
|
|
|
|
Add to `.vscode/launch.json`:
|
|
|
|
```json
|
|
{
|
|
"version": "0.2.0",
|
|
"configurations": [
|
|
{
|
|
"type": "node",
|
|
"request": "launch",
|
|
"name": "Debug Nest.js",
|
|
"runtimeArgs": [
|
|
"-r",
|
|
"ts-node/register",
|
|
"-r",
|
|
"tsconfig-paths/register"
|
|
],
|
|
"args": ["${workspaceFolder}/src/main.ts"],
|
|
"envFile": "${workspaceFolder}/.env"
|
|
},
|
|
{
|
|
"type": "node",
|
|
"request": "launch",
|
|
"name": "Debug Jest Tests",
|
|
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
|
"args": ["--runInBand", "--config", "jest.config.js"],
|
|
"console": "integratedTerminal",
|
|
"internalConsoleOptions": "neverOpen"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Debug Logging
|
|
|
|
Enable verbose logging:
|
|
|
|
```typescript
|
|
// In main.ts or app.module.ts
|
|
app.useLogger(['log', 'error', 'warn', 'debug', 'verbose']);
|
|
```
|
|
|
|
---
|
|
|
|
## Code Style
|
|
|
|
### TypeScript Configuration
|
|
|
|
The project uses strict TypeScript settings:
|
|
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"strict": true,
|
|
"noImplicitAny": true,
|
|
"strictNullChecks": true,
|
|
"noUnusedLocals": true,
|
|
"noUnusedParameters": true
|
|
}
|
|
}
|
|
```
|
|
|
|
### Naming Conventions
|
|
|
|
| Type | Convention | Example |
|
|
|------|------------|---------|
|
|
| Files | kebab-case | `backup-share.entity.ts` |
|
|
| Classes | PascalCase | `BackupShareEntity` |
|
|
| Interfaces | PascalCase with I prefix | `IBackupShareRepository` |
|
|
| Functions | camelCase | `storeBackupShare()` |
|
|
| Constants | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
|
|
| Environment | UPPER_SNAKE_CASE | `DATABASE_URL` |
|
|
|
|
### File Organization
|
|
|
|
```
|
|
feature/
|
|
├── feature.command.ts # Command object
|
|
├── feature.handler.ts # Command handler
|
|
├── feature.spec.ts # Unit tests
|
|
└── index.ts # Barrel export
|
|
```
|
|
|
|
---
|
|
|
|
## Common Development Tasks
|
|
|
|
### Adding a New Endpoint
|
|
|
|
1. Create DTO in `src/api/dto/request/`
|
|
2. Add validation decorators
|
|
3. Create handler in `src/application/commands/` or `queries/`
|
|
4. Add method to controller
|
|
5. Write unit tests
|
|
6. Write E2E tests
|
|
|
|
### Adding a New Environment Variable
|
|
|
|
1. Add to `.env.example` with description
|
|
2. Add to `src/config/index.ts`
|
|
3. Update this documentation
|
|
4. Update deployment configs
|
|
|
|
### Updating Database Schema
|
|
|
|
1. Modify `prisma/schema.prisma`
|
|
2. Run `npx prisma migrate dev --name description`
|
|
3. Update domain entities if needed
|
|
4. Update repository mappings
|
|
5. Write migration tests
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
#### Prisma Client Not Generated
|
|
|
|
```bash
|
|
Error: @prisma/client did not initialize yet
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
npm run prisma:generate
|
|
```
|
|
|
|
#### Database Connection Failed
|
|
|
|
```bash
|
|
Error: Can't reach database server
|
|
```
|
|
|
|
**Solution:**
|
|
1. Check if Docker is running: `docker ps`
|
|
2. Check DATABASE_URL in `.env`
|
|
3. Restart database: `docker-compose restart backup-db`
|
|
|
|
#### Port Already in Use
|
|
|
|
```bash
|
|
Error: listen EADDRINUSE: address already in use :::3002
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Find and kill the process
|
|
netstat -ano | findstr :3002
|
|
taskkill /PID <PID> /F
|
|
```
|
|
|
|
#### TypeScript Compilation Errors
|
|
|
|
```bash
|
|
# Clear build cache
|
|
rm -rf dist
|
|
npm run build
|
|
```
|
|
|
|
### Getting Help
|
|
|
|
- Check existing issues on GitHub
|
|
- Review NestJS documentation
|
|
- Review Prisma documentation
|
|
- Ask in team Slack channel
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### Security
|
|
|
|
1. Never commit secrets to git
|
|
2. Use environment variables for all configs
|
|
3. Validate all inputs with class-validator
|
|
4. Sanitize logs (no sensitive data)
|
|
5. Use parameterized queries (Prisma handles this)
|
|
|
|
### Performance
|
|
|
|
1. Use database indexes for frequently queried fields
|
|
2. Implement pagination for list endpoints
|
|
3. Use connection pooling (Prisma default)
|
|
4. Cache frequently accessed data if needed
|
|
|
|
### Code Quality
|
|
|
|
1. Write unit tests for all handlers
|
|
2. Write E2E tests for all endpoints
|
|
3. Use TypeScript strict mode
|
|
4. Follow DDD principles
|
|
5. Keep controllers thin, logic in handlers
|