Full Code of ZhiXiao-Lin/nestify for AI

main d884848690a4 cached
241 files
541.9 KB
125.0k tokens
995 symbols
1 requests
Download .txt
Showing preview only (612K chars total). Download the full file or copy to clipboard to get everything.
Repository: ZhiXiao-Lin/nestify
Branch: main
Commit: d884848690a4
Files: 241
Total size: 541.9 KB

Directory structure:
gitextract_v0ktoj1e/

├── .gitignore
├── .npmrc
├── LICENSE
├── README.md
├── apps/
│   └── api/
│       ├── DATABASE.md
│       ├── REDIS_USAGE.md
│       ├── migrations/
│       │   └── 001_create_orders_tables.sql
│       ├── nest-cli.json
│       ├── package.json
│       ├── src/
│       │   ├── app.module.ts
│       │   ├── main.ts
│       │   ├── modules/
│       │   │   └── order/
│       │   │       ├── application/
│       │   │       │   ├── commands/
│       │   │       │   │   ├── cancel-order/
│       │   │       │   │   │   ├── cancel-order.command.ts
│       │   │       │   │   │   ├── cancel-order.dto.ts
│       │   │       │   │   │   ├── cancel-order.handler.spec.ts
│       │   │       │   │   │   └── cancel-order.handler.ts
│       │   │       │   │   ├── confirm-order/
│       │   │       │   │   │   ├── confirm-order.command.ts
│       │   │       │   │   │   ├── confirm-order.dto.ts
│       │   │       │   │   │   ├── confirm-order.handler.spec.ts
│       │   │       │   │   │   └── confirm-order.handler.ts
│       │   │       │   │   └── create-order/
│       │   │       │   │       ├── create-order.command.ts
│       │   │       │   │       ├── create-order.dto.ts
│       │   │       │   │       ├── create-order.handler.spec.ts
│       │   │       │   │       └── create-order.handler.ts
│       │   │       │   ├── event-handlers/
│       │   │       │   │   ├── order-confirmed.handler.ts
│       │   │       │   │   └── order-created.handler.ts
│       │   │       │   └── queries/
│       │   │       │       ├── get-order/
│       │   │       │       │   ├── get-order.handler.spec.ts
│       │   │       │       │   ├── get-order.handler.ts
│       │   │       │       │   ├── get-order.query.ts
│       │   │       │       │   └── order.response.dto.ts
│       │   │       │       └── list-orders/
│       │   │       │           ├── list-orders.handler.spec.ts
│       │   │       │           ├── list-orders.handler.ts
│       │   │       │           ├── list-orders.query.ts
│       │   │       │           └── order-list.response.dto.ts
│       │   │       ├── domain/
│       │   │       │   ├── entities/
│       │   │       │   │   ├── order-item.entity.spec.ts
│       │   │       │   │   ├── order-item.entity.ts
│       │   │       │   │   ├── order.entity.spec.ts
│       │   │       │   │   └── order.entity.ts
│       │   │       │   ├── events/
│       │   │       │   │   ├── order-cancelled.event.ts
│       │   │       │   │   ├── order-confirmed.event.ts
│       │   │       │   │   └── order-created.event.ts
│       │   │       │   ├── exceptions/
│       │   │       │   │   ├── invalid-order-state.exception.ts
│       │   │       │   │   └── order-not-found.exception.ts
│       │   │       │   ├── repositories/
│       │   │       │   │   └── order.repository.interface.ts
│       │   │       │   ├── services/
│       │   │       │   │   ├── order-pricing.service.spec.ts
│       │   │       │   │   └── order-pricing.service.ts
│       │   │       │   └── value-objects/
│       │   │       │       ├── money.vo.spec.ts
│       │   │       │       ├── money.vo.ts
│       │   │       │       ├── order-id.vo.spec.ts
│       │   │       │       ├── order-id.vo.ts
│       │   │       │       ├── order-status.vo.spec.ts
│       │   │       │       ├── order-status.vo.ts
│       │   │       │       ├── quantity.vo.spec.ts
│       │   │       │       └── quantity.vo.ts
│       │   │       ├── infrastructure/
│       │   │       │   ├── cache/
│       │   │       │   │   └── order-cache.service.ts
│       │   │       │   └── persistence/
│       │   │       │       └── kysely-order.repository.ts
│       │   │       ├── order.module.ts
│       │   │       └── presentation/
│       │   │           └── order.controller.ts
│       │   └── shared/
│       │       ├── api-response/
│       │       │   ├── api-response.dto.ts
│       │       │   ├── api-response.interceptor.ts
│       │       │   ├── api-response.service.ts
│       │       │   └── index.ts
│       │       ├── api-versioning/
│       │       │   ├── api-versioning.decorator.ts
│       │       │   ├── api-versioning.interceptor.ts
│       │       │   └── index.ts
│       │       ├── application/
│       │       │   ├── dto.base.ts
│       │       │   ├── query.interface.ts
│       │       │   └── use-case.interface.ts
│       │       ├── audit/
│       │       │   ├── audit.decorator.ts
│       │       │   ├── audit.interceptor.ts
│       │       │   ├── audit.service.ts
│       │       │   └── index.ts
│       │       ├── auth/
│       │       │   ├── auth.module.ts
│       │       │   ├── decorators/
│       │       │   │   ├── current-user.decorator.ts
│       │       │   │   └── index.ts
│       │       │   ├── dto/
│       │       │   │   ├── auth.dto.ts
│       │       │   │   └── index.ts
│       │       │   ├── guards/
│       │       │   │   ├── index.ts
│       │       │   │   ├── jwt-auth.guard.ts
│       │       │   │   ├── permissions.guard.ts
│       │       │   │   └── roles.guard.ts
│       │       │   ├── index.ts
│       │       │   ├── jwt/
│       │       │   │   ├── index.ts
│       │       │   │   ├── jwt.service.ts
│       │       │   │   └── jwt.types.ts
│       │       │   └── rbac/
│       │       │       ├── index.ts
│       │       │       └── rbac.service.ts
│       │       ├── base/
│       │       │   ├── base.entity.ts
│       │       │   ├── base.service.ts
│       │       │   ├── index.ts
│       │       │   └── pagination.dto.ts
│       │       ├── cache/
│       │       │   ├── cache.decorator.ts
│       │       │   ├── cache.interceptor.ts
│       │       │   ├── cache.service.ts
│       │       │   └── index.ts
│       │       ├── circuit-breaker/
│       │       │   ├── circuit-breaker.decorator.ts
│       │       │   ├── circuit-breaker.module.ts
│       │       │   ├── circuit-breaker.service.ts
│       │       │   └── index.ts
│       │       ├── database/
│       │       │   ├── database.module.ts
│       │       │   ├── database.types.ts
│       │       │   └── index.ts
│       │       ├── domain/
│       │       │   ├── aggregate-root.ts
│       │       │   ├── domain-event-publisher.ts
│       │       │   ├── domain-event.ts
│       │       │   ├── entity.ts
│       │       │   ├── index.ts
│       │       │   └── value-object.ts
│       │       ├── errors/
│       │       │   ├── business.exception.ts
│       │       │   ├── error-codes.ts
│       │       │   ├── error.filter.ts
│       │       │   └── index.ts
│       │       ├── feature-flags/
│       │       │   ├── feature-flags.decorator.ts
│       │       │   ├── feature-flags.guard.ts
│       │       │   ├── feature-flags.service.ts
│       │       │   └── index.ts
│       │       ├── file-upload/
│       │       │   ├── file-upload.decorator.ts
│       │       │   ├── file-upload.interceptor.ts
│       │       │   ├── file-upload.service.ts
│       │       │   └── index.ts
│       │       ├── health/
│       │       │   ├── health.controller.ts
│       │       │   ├── health.module.ts
│       │       │   ├── index.ts
│       │       │   └── indicators/
│       │       │       ├── database.indicator.ts
│       │       │       ├── nats.indicator.ts
│       │       │       ├── redis.indicator.ts
│       │       │       └── rustfs.indicator.ts
│       │       ├── infrastructure/
│       │       │   ├── messaging/
│       │       │   │   ├── event-bus.interface.ts
│       │       │   │   ├── event-bus.service.ts
│       │       │   │   └── messaging.interface.ts
│       │       │   ├── persistence/
│       │       │   │   ├── repository.interface.ts
│       │       │   │   └── unit-of-work.interface.ts
│       │       │   └── storage/
│       │       │       └── storage.interface.ts
│       │       ├── metrics/
│       │       │   ├── index.ts
│       │       │   ├── metrics.controller.ts
│       │       │   ├── metrics.interceptor.ts
│       │       │   └── metrics.service.ts
│       │       ├── openapi/
│       │       │   ├── index.ts
│       │       │   ├── openapi-common.dto.ts
│       │       │   └── openapi-decorators.ts
│       │       ├── presentation/
│       │       │   ├── filters/
│       │       │   │   ├── domain-exception.filter.ts
│       │       │   │   └── http-exception.filter.ts
│       │       │   └── interceptors/
│       │       │       └── logging.interceptor.ts
│       │       ├── rate-limiting/
│       │       │   ├── index.ts
│       │       │   ├── rate-limiting.decorator.ts
│       │       │   ├── rate-limiting.guard.ts
│       │       │   └── rate-limiting.service.ts
│       │       ├── redis/
│       │       │   ├── index.ts
│       │       │   └── redis.module.ts
│       │       ├── retry/
│       │       │   ├── index.ts
│       │       │   ├── retry.module.ts
│       │       │   └── retry.service.ts
│       │       ├── serialization/
│       │       │   ├── example.ts
│       │       │   ├── index.ts
│       │       │   └── serializer.ts
│       │       ├── tenant/
│       │       │   ├── index.ts
│       │       │   ├── tenant.decorator.ts
│       │       │   ├── tenant.guard.ts
│       │       │   ├── tenant.interceptor.ts
│       │       │   └── tenant.service.ts
│       │       ├── testing/
│       │       │   ├── index.ts
│       │       │   └── testing.utils.ts
│       │       ├── tracking/
│       │       │   ├── index.ts
│       │       │   └── tracking.interceptor.ts
│       │       ├── transform/
│       │       │   ├── index.ts
│       │       │   └── transform.interceptor.ts
│       │       ├── utils/
│       │       │   ├── guard.ts
│       │       │   └── result.ts
│       │       └── validation/
│       │           ├── index.ts
│       │           ├── validation-options.ts
│       │           └── validation.pipe.ts
│       ├── test/
│       │   └── jest-e2e.json
│       ├── tsconfig.build.json
│       └── tsconfig.json
├── biome.json
├── docker/
│   ├── Dockerfile
│   ├── Dockerfile.dev
│   ├── docker-compose.prod.yml
│   └── docker-compose.yml
├── docs/
│   ├── architecture.md
│   └── ddd-patterns.md
├── nest-cli.json
├── package.json
├── packages/
│   ├── bullmq/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── bullmq.module-definition.ts
│   │   │   ├── bullmq.module.ts
│   │   │   ├── bullmq.service.ts
│   │   │   ├── bullmq.types.ts
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── etcd/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── config.service.ts
│   │   │   ├── etcd.module.ts
│   │   │   ├── etcd.service.ts
│   │   │   ├── etcd.types.ts
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── kysely/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   ├── kysely.logger.spec.ts
│   │   │   │   └── kysely.module.spec.ts
│   │   │   ├── index.ts
│   │   │   ├── kysely-module-options.interface.ts
│   │   │   ├── kysely.logger.ts
│   │   │   ├── kysely.module-definition.ts
│   │   │   ├── kysely.module.ts
│   │   │   └── kysely.service.ts
│   │   └── tsconfig.json
│   ├── logger/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── logger.module-definition.ts
│   │   │   ├── logger.module.ts
│   │   │   ├── logger.service.ts
│   │   │   ├── logger.types.ts
│   │   │   └── logging.interceptor.ts
│   │   └── tsconfig.json
│   ├── nats/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── nats.module-definition.ts
│   │   │   ├── nats.module.ts
│   │   │   ├── nats.service.ts
│   │   │   └── nats.types.ts
│   │   └── tsconfig.json
│   ├── redisson/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   ├── redisson.module.spec.ts
│   │   │   │   └── types.spec.ts
│   │   │   ├── index.ts
│   │   │   ├── redisson-module-options.interface.ts
│   │   │   ├── redisson.module-definition.ts
│   │   │   ├── redisson.module.ts
│   │   │   ├── redisson.service.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   └── rustfs/
│       ├── package.json
│       ├── src/
│       │   ├── index.ts
│       │   ├── rustfs.module-definition.ts
│       │   ├── rustfs.module.ts
│       │   ├── rustfs.service.ts
│       │   └── rustfs.types.ts
│       └── tsconfig.json
├── pnpm-workspace.yaml
├── tsconfig.build.json
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Build
dist/
build/
*.tsbuildinfo

# Environment
.env
.env.local
.env.*.local

# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Testing
coverage/
.nyc_output/
*.lcov

# Logs
logs/
*.log

# Database
*.sqlite
*.db

# OS
Thumbs.db


================================================
FILE: .npmrc
================================================
shamefully-hoist=true
strict-peer-dependencies=false


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2025 A3S Lab

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Nestify - Production-Ready NestJS Monorepo Template

A production-ready NestJS monorepo template with pnpm workspace, implementing Domain-Driven Design (DDD), Clean Architecture, and comprehensive infrastructure for enterprise applications.

## Features

### Core Architecture
- **Monorepo Architecture**: pnpm workspace for managing multiple packages and applications
- **Clean Architecture**: Clear separation of concerns with Domain, Application, Infrastructure, and Presentation layers
- **Domain-Driven Design**: Rich domain models with entities, value objects, aggregates, and domain events
- **CQRS Pattern**: Separate command and query handlers using @nestjs/cqrs
- **Event-Driven**: Domain events for decoupled communication

### Infrastructure Packages
- **Type-Safe SQL**: Kysely query builder with full TypeScript support
- **Distributed Caching**: Redis with Redisson for locks, caching, and rate limiting
- **Structured Logging**: Pino-based JSON logging with request tracing
- **Message Queue**: BullMQ for distributed task processing
- **Event Streaming**: NATS with JetStream support
- **Object Storage**: S3-compatible RustFS storage
- **Distributed Config**: etcd for configuration management with hot-reload

### Application Features
- **Authentication**: JWT with access/refresh tokens, RBAC permission system
- **API Metrics**: Prometheus metrics with request tracking
- **Circuit Breaker**: Fault tolerance with automatic failover
- **Retry Logic**: Exponential backoff with jitter
- **Rate Limiting**: Redis-based sliding window rate limiting
- **Multi-tenancy**: Tenant isolation support
- **Audit Logging**: Comprehensive audit trail
- **Feature Flags**: Rollout management
- **API Versioning**: Header-based API versioning
- **File Upload**: Multipart file handling

### Quality Assurance
- **Type Safety**: Full TypeScript with strict mode
- **API Documentation**: Swagger/OpenAPI integration
- **Validation**: class-validator with custom decorators
- **Testing**: Unit, integration, and E2E test setup
- **Code Quality**: Biome linting and formatting

## Architecture Overview

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                              API Application                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│  Presentation    │  Application    │  Domain      │  Infrastructure         │
│  - Controllers   │  - Commands     │  - Entities  │  - Kysely (PostgreSQL) │
│  - DTOs          │  - Queries      │  - Value Obj │  - Redisson (Redis)    │
│  - Guards        │  - Event Hand. │  - Aggreg.   │  - BullMQ (Tasks)       │
│  - Interceptors  │  - DTOs        │  - Events    │  - NATS (Messaging)     │
│                  │                 │  - Services   │  - RustFS (Storage)     │
│                  │                 │              │  - etcd (Config)        │
├─────────────────────────────────────────────────────────────────────────────┤
│                           Shared Infrastructure                               │
│  Auth │ Metrics │ Cache │ CircuitBreaker │ Retry │ RateLimit │ Health    │
└─────────────────────────────────────────────────────────────────────────────┘
```

## Monorepo Structure

```
nestify/
├── pnpm-workspace.yaml              # Workspace configuration
├── package.json                     # Root package.json with workspace scripts
├── tsconfig.json                   # Base TypeScript configuration
├── biome.json                      # Biome linting/formatting config
├── apps/
│   └── api/                       # Main NestJS API application
│       ├── src/
│       │   ├── app.module.ts      # Root application module
│       │   ├── main.ts            # Application entry point
│       │   ├── modules/          # Business modules (DDD)
│       │   └── shared/            # Shared infrastructure modules
│       └── package.json
└── packages/
    ├── kysely/                    # @a3s-lab/kysely - Type-safe SQL
    ├── redisson/                  # @a3s-lab/redisson - Redis client
    ├── logger/                    # @a3s-lab/logger - Structured logging
    ├── bullmq/                    # @a3s-lab/bullmq - Task queue
    ├── nats/                      # @a3s-lab/nats - Message broker
    ├── rustfs/                    # @a3s-lab/rustfs - S3 storage
    └── etcd/                      # @a3s-lab/etcd - Config center
```

## Packages

### @a3s-lab/kysely

Type-safe SQL query builder module for NestJS.

```typescript
KyselyModule.register({
  config: {
    dialect: new PostgresDialect({ pool: new Pool({ connectionString }) }),
  },
})
```

### @a3s-lab/redisson

Redis distributed locks, caching, and rate limiting.

```typescript
// Distributed lock
await redisson.withLock('resource-key', async () => {
  // Critical section
});

// Cache with TTL
await redisson.setJSON('cache-key', data, 3600);

// Rate limiting
const limited = await rateLimiter.tryAcquire('endpoint-limit');
```

### @a3s-lab/logger

Structured JSON logging with request tracing.

```typescript
LoggerModule.register({
  level: 'info',
  name: 'api',
  json: true,  // JSON format for K8s
});

// In services
logger.logRequest({ method, url, statusCode, responseTime });
```

### @a3s-lab/bullmq

Distributed task queue with retry and delayed jobs.

```typescript
// Add job
await bullmq.addJob('notifications', 'send-email', { to: 'user@example.com' });

// Create worker
bullmq.createWorker('notifications', async (job) => {
  await sendEmail(job.data);
  return { success: true };
});
```

### @a3s-lab/nats

High-performance message broker with JetStream.

```typescript
// Publish
await nats.publish({ subject: 'orders.created', data: orderEvent });

// Subscribe
await nats.subscribe$('orders.created', async (data) => {
  await handleOrderCreated(data);
});

// JetStream
await nats.jsPublish({ stream: 'ORDERS', subject: 'created', data });
```

### @a3s-lab/rustfs

S3-compatible object storage.

```typescript
// Upload file
const result = await rustfs.putObject('bucket', {
  key: 'uploads/file.pdf',
  body: fileBuffer,
  contentType: 'application/pdf',
});

// Get presigned URL
const url = await rustfs.getPresignedUrl('bucket', {
  key: 'uploads/file.pdf',
  expiresIn: 3600,
});
```

### @a3s-lab/etcd

Distributed configuration with hot-reload.

```typescript
// Get config
const value = await etcd.get('config/feature-flags');

// Watch for changes
etcd.watch('config/feature-flags', (event) => {
  if (event.value) reloadFeatures(event.value);
});
```

## Shared Modules

### Authentication (auth)

JWT-based authentication with RBAC.

```typescript
// JWT Token Generation
const tokens = jwtService.generateTokenPair({ sub: userId, roles: ['admin'] });

// Protect Routes
@UseGuards(JwtAuthGuard)

// Role-based Access
@Roles('admin')
@UseGuards(RolesGuard)

// Permission Check
@Permissions('users', 'create')
@UseGuards(PermissionsGuard)
```

### Metrics (metrics)

Prometheus metrics collection.

```typescript
// Automatic HTTP metrics
GET /metrics  // Prometheus format

// Custom metrics
metricsService.incGauge('active_users');
metricsService.observeHistogram('request_duration', duration);
```

### Circuit Breaker (circuit-breaker)

Fault tolerance pattern.

```typescript
@CircuitBreaker({ timeout: 5000, maxFailures: 5 })
async callExternalService() {
  return await externalService.get();
}
```

### Retry (retry)

Automatic retry with exponential backoff.

```typescript
const result = await retryService.execute(fn, {
  maxAttempts: 3,
  initialDelay: 100,
  backoffMultiplier: 2,
  retryableErrors: [NetworkError, TimeoutError],
});
```

### Rate Limiting (rate-limiting)

Redis-based sliding window rate limiting.

```typescript
@RateLimit({ limit: 100, window: '1m' })
async endpoint() { }
```

### Health (health)

Health check endpoints.

```typescript
GET /health      // Full health check
GET /health/live // Liveness probe
GET /health/ready // Readiness probe
```

### Validation (validation)

Custom validators beyond class-validator.

```typescript
@IsPassword()              // Strong password
@IsStrongPassword()        // Very strong password
@IsUsername()              // Alphanumeric with underscores
@IsSlug()                  // URL-safe slug
@IsFutureDate()           // Future date only
@IsInRange(0, 100)        // Number in range
```

### Serialization (serialization)

class-transformer integration with groups.

```typescript
class UserEntity { }
class UserDto { }

@Serialize(UserDto, { groups: ['user:read'] })
getUser(): UserEntity { }
```

## Project Structure

```
apps/api/src/
├── app.module.ts                    # Root module
├── main.ts                         # Bootstrap
├── modules/                         # Business modules
│   └── order/                      # Order bounded context
│       ├── domain/
│       │   ├── entities/          # Order, OrderItem
│       │   ├── value-objects/    # Money, Quantity
│       │   ├── events/           # OrderCreated, OrderConfirmed
│       │   ├── repositories/     # IOrderRepository
│       │   └── exceptions/      # Domain exceptions
│       ├── application/
│       │   ├── commands/        # CreateOrder, CancelOrder
│       │   ├── queries/        # GetOrder, ListOrders
│       │   └── event-handlers/ # HandleOrderCreated
│       ├── infrastructure/
│       │   └── persistence/     # KyselyOrderRepository
│       └── presentation/
│           └── order.controller.ts
└── shared/                         # Shared kernel
    ├── auth/                      # JWT, RBAC, Guards
    ├── metrics/                   # Prometheus
    ├── cache/                     # Caching
    ├── circuit-breaker/           # Fault tolerance
    ├── retry/                    # Retry logic
    ├── rate-limiting/            # Rate limit
    ├── health/                    # Health checks
    ├── validation/                # Custom validators
    ├── serialization/             # DTO transformation
    ├── base/                      # Base service, entity
    ├── domain/                    # Core DDD
    ├── errors/                    # Error handling
    ├── utils/                     # Utilities
    └── ...
```

## Getting Started

### Prerequisites

- Node.js 20+
- pnpm 8+
- Docker and Docker Compose
- PostgreSQL 15+
- Redis 7+

### Installation

```bash
# Clone and install
git clone https://github.com/A3S-Lab/nestify.git
cd nestify
pnpm install

# Start infrastructure
cd docker && docker-compose up -d

# Build
pnpm build

# Run
pnpm start:dev
```

Access:
- API: http://localhost:3000
- Swagger: http://localhost:3000/api/docs
- Metrics: http://localhost:3000/metrics

## Scripts

```bash
pnpm install              # Install dependencies
pnpm build               # Build all
pnpm start:dev           # Development mode
pnpm test                # Run tests
pnpm lint                # Lint code
pnpm format             # Format code
```

## Key Design Patterns

### Domain-Driven Design

```
Domain Layer (innermost, no dependencies)
    │
    ▼
Application Layer (depends on Domain)
    │
    ▼
Infrastructure Layer (implements interfaces)
    │
    ▼
Presentation Layer (depends on all)
```

### CQRS

- **Commands**: `CreateOrder`, `ConfirmOrder` - Write operations
- **Queries**: `GetOrder`, `ListOrders` - Read operations
- **Events**: `OrderCreated`, `OrderConfirmed` - Decoupled communication

### Fault Tolerance

```
Circuit Breaker States:
┌─────────┐     5 failures      ┌──────┐     timeout     ┌───────────┐
│ CLOSED │ ─────────────────▶  │ OPEN │ ──────────────▶ │ HALF_OPEN │
└─────────┘                     └──────┘                 └───────────┘
     ▲                                                        │
     │            success                                     │
     └────────────────────────────────────────────────────────┘
```

## Environment Variables

```env
# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/nestify

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379

# JWT
JWT_ACCESS_SECRET=your-access-secret
JWT_REFRESH_SECRET=your-refresh-secret
JWT_ACCESS_EXPIRY=15m
JWT_REFRESH_EXPIRY=7d

# NATS (optional)
NATS_SERVERS=nats://localhost:4222

# RustFS (optional)
RUSTFS_ENDPOINT=http://localhost:9000
RUSTFS_ACCESS_KEY=rustfsadmin
RUSTFS_SECRET_KEY=rustfsadmin
RUSTFS_BUCKET=nestify

# etcd (optional)
ETCD_ENDPOINTS=http://localhost:2379
```

## License

MIT


================================================
FILE: apps/api/DATABASE.md
================================================
# Database Setup

This project uses PostgreSQL with Kysely for type-safe SQL queries.

## Prerequisites

- PostgreSQL 14+
- pnpm 8+

## Setup

### 1. Start PostgreSQL

Using Docker:
```bash
cd docker
docker-compose up -d postgres
```

Or use your local PostgreSQL installation.

### 2. Create Database

```bash
createdb nestify
```

### 3. Run Migrations

```bash
psql -d nestify -f apps/api/migrations/001_create_orders_tables.sql
```

### 4. Configure Environment

Create `.env` file in the root:

```env
# Database
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE=nestify

# Application
NODE_ENV=development
PORT=3000
```

## Running the Application

```bash
# Install dependencies
pnpm install

# Build packages
pnpm build:packages

# Start development server
pnpm start:dev
```

The API will be available at http://localhost:3000/api

## API Endpoints

### Create Order
```bash
curl -X POST http://localhost:3000/api/orders \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "customer-123",
    "items": [
      {
        "productId": "product-456",
        "quantity": 2,
        "unitPrice": 10.99
      }
    ]
  }'
```

### Get Order
```bash
curl http://localhost:3000/api/orders/{orderId}
```

### List Orders by Customer
```bash
curl http://localhost:3000/api/orders?customerId=customer-123
```

### Confirm Order
```bash
curl -X POST http://localhost:3000/api/orders/{orderId}/confirm
```

### Cancel Order
```bash
curl -X POST http://localhost:3000/api/orders/{orderId}/cancel
```

## Database Schema

### orders table
- `id` (UUID, Primary Key)
- `customer_id` (VARCHAR)
- `status` (VARCHAR: 'pending', 'confirmed', 'cancelled')
- `total_amount` (DECIMAL)
- `created_at` (TIMESTAMP)
- `updated_at` (TIMESTAMP)

### order_items table
- `id` (UUID, Primary Key)
- `order_id` (UUID, Foreign Key)
- `product_id` (VARCHAR)
- `quantity` (INTEGER)
- `unit_price` (DECIMAL)
- `subtotal` (DECIMAL)
- `created_at` (TIMESTAMP)

## Using Kysely in Your Code

```typescript
import { Injectable } from '@nestjs/common';
import { KyselyService } from '@a3s-lab/kysely';
import { Database } from '@/shared/database/database.types';

@Injectable()
export class MyRepository {
  constructor(private readonly db: KyselyService<Database>) {}

  async findAll() {
    return this.db
      .selectFrom('orders')
      .selectAll()
      .execute();
  }

  async create(data: NewOrder) {
    return this.db
      .insertInto('orders')
      .values(data)
      .returningAll()
      .executeTakeFirstOrThrow();
  }

  async update(id: string, data: OrderUpdate) {
    return this.db
      .updateTable('orders')
      .set(data)
      .where('id', '=', id)
      .returningAll()
      .executeTakeFirstOrThrow();
  }

  async delete(id: string) {
    await this.db
      .deleteFrom('orders')
      .where('id', '=', id)
      .execute();
  }
}
```

## Transactions

```typescript
async saveWithTransaction(order: Order) {
  return await this.db.transaction().execute(async (trx) => {
    // Insert order
    await trx
      .insertInto('orders')
      .values(orderData)
      .execute();

    // Insert order items
    await trx
      .insertInto('order_items')
      .values(itemsData)
      .execute();

    return order;
  });
}
```

## Query Logging

Kysely logger is enabled in development mode and will show:
- SQL queries with syntax highlighting
- Query parameters
- Execution time with color coding:
  - Green: < 1ms (excellent)
  - Yellow: 1-100ms (acceptable)
  - Red: > 100ms (needs optimization)


================================================
FILE: apps/api/REDIS_USAGE.md
================================================
# Redis & Redisson Usage Guide

This guide demonstrates how to use the `@a3s-lab/redisson` package for caching, distributed locks, and other Redis operations in the NestJS application.

## Installation

The package is already installed as a workspace dependency:

```json
{
  "dependencies": {
    "@a3s-lab/redisson": "workspace:*"
  }
}
```

## Configuration

### Environment Variables

Add Redis configuration to your `.env` file:

```env
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
```

### Module Setup

The `RedisModule` is configured globally in `src/shared/redis/redis.module.ts`:

```typescript
import { Module, Global } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { RedissonModule } from '@a3s-lab/redisson';

@Global()
@Module({
    imports: [
        RedissonModule.registerAsync({
            imports: [ConfigModule],
            useFactory: (configService: ConfigService) => ({
                host: configService.get('REDIS_HOST', 'localhost'),
                port: configService.get('REDIS_PORT', 6379),
                password: configService.get('REDIS_PASSWORD'),
                db: configService.get('REDIS_DB', 0),
            }),
            inject: [ConfigService],
        }),
    ],
    exports: [RedissonModule],
})
export class RedisModule {}
```

## Basic Usage

### 1. Inject RedissonService

```typescript
import { Injectable } from '@nestjs/common';
import { RedissonService } from '@a3s-lab/redisson';

@Injectable()
export class MyService {
    constructor(private readonly redisson: RedissonService) {}

    async example() {
        // Your Redis operations here
    }
}
```

### 2. Simple Key-Value Operations

```typescript
// Set a value
await this.redisson.set('key', 'value');

// Set with TTL (time to live in seconds)
await this.redisson.set('key', 'value', 3600); // 1 hour

// Get a value
const value = await this.redisson.get('key');

// Delete a key
await this.redisson.delete('key');

// Check if key exists
const exists = await this.redisson.exists('key');
```

### 3. JSON Operations

```typescript
// Store JSON data
const user = { id: '123', name: 'John', email: 'john@example.com' };
await this.redisson.setJSON('user:123', user, 3600);

// Retrieve JSON data
const cachedUser = await this.redisson.getJSON<User>('user:123');

// Update JSON data
await this.redisson.setJSON('user:123', { ...user, name: 'Jane' });
```

### 4. Cache with Get-or-Set Pattern

```typescript
// Get from cache or execute factory function
const order = await this.redisson.getOrSet(
    'order:123',
    async () => {
        // This function only runs if cache miss
        return await this.orderRepository.findById('123');
    },
    3600, // TTL in seconds
);
```

### 5. Distributed Locks

Prevent race conditions with distributed locks:

```typescript
// Execute operation with lock
const result = await this.redisson.withLock(
    'lock:order:123',
    async () => {
        // Critical section - only one process can execute this at a time
        const order = await this.orderRepository.findById('123');
        order.confirm();
        return await this.orderRepository.save(order);
    },
    5000,  // Wait time (ms) - how long to wait for lock
    10000, // Lease time (ms) - how long to hold lock
);
```

### 6. Counters

```typescript
// Increment counter
const views = await this.redisson.increment('page:views');

// Increment by specific amount
const score = await this.redisson.increment('user:score', 10);

// Decrement counter
const remaining = await this.redisson.decrement('stock:product:123');
```

### 7. Hash Operations

```typescript
// Set hash field
await this.redisson.hset('user:123', 'name', 'John');
await this.redisson.hset('user:123', 'email', 'john@example.com');

// Get hash field
const name = await this.redisson.hget('user:123', 'name');

// Get all hash fields
const user = await this.redisson.hgetall('user:123');
// Returns: { name: 'John', email: 'john@example.com' }

// Delete hash field
await this.redisson.hdel('user:123', 'email');
```

### 8. Pattern-Based Deletion

```typescript
// Delete all keys matching pattern
const deletedCount = await this.redisson.deleteByPattern('cache:order:*');
console.log(`Deleted ${deletedCount} keys`);
```

### 9. Expiration

```typescript
// Set expiration on existing key
await this.redisson.expire('key', 3600); // 1 hour
```

## Real-World Example: Order Cache Service

See `src/modules/order/infrastructure/cache/order-cache.service.ts` for a complete implementation:

```typescript
import { Injectable, Logger } from '@nestjs/common';
import { RedissonService } from '@a3s-lab/redisson';

@Injectable()
export class OrderCacheService {
    private readonly logger = new Logger(OrderCacheService.name);

    constructor(private readonly redisson: RedissonService) {}

    // Cache an order
    async cacheOrder(order: Order, ttl: number = 3600): Promise<void> {
        const key = `order:${order.id}`;
        await this.redisson.setJSON(key, this.serializeOrder(order), ttl);
    }

    // Get cached order
    async getCachedOrder(orderId: string): Promise<SerializedOrder | null> {
        const key = `order:${orderId}`;
        return this.redisson.getJSON<SerializedOrder>(key);
    }

    // Get or fetch order with cache
    async getOrSetOrder(
        orderId: string,
        factory: () => Promise<Order | null>,
        ttl: number = 3600,
    ): Promise<SerializedOrder | null> {
        const key = `order:${orderId}`;
        return this.redisson.getOrSet<SerializedOrder | null>(
            key,
            async () => {
                const order = await factory();
                return order ? this.serializeOrder(order) : null;
            },
            ttl,
        );
    }

    // Execute with distributed lock
    async withOrderLock<T>(
        orderId: string,
        operation: () => Promise<T>,
    ): Promise<T> {
        const lockKey = `lock:order:${orderId}`;
        return this.redisson.withLock(lockKey, operation, 5000, 10000);
    }

    // Increment view count
    async incrementOrderViewCount(orderId: string): Promise<number> {
        const key = `order:views:${orderId}`;
        return this.redisson.increment(key);
    }
}
```

## Usage in Query Handlers

### Example: Get Order with Cache

```typescript
import { Injectable } from '@nestjs/common';
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { OrderCacheService } from '../../infrastructure/cache/order-cache.service';
import { OrderRepository } from '../../infrastructure/persistence/kysely-order.repository';

@QueryHandler(GetOrderQuery)
export class GetOrderHandler implements IQueryHandler<GetOrderQuery> {
    constructor(
        private readonly orderRepository: OrderRepository,
        private readonly cacheService: OrderCacheService,
    ) {}

    async execute(query: GetOrderQuery): Promise<OrderResponseDto> {
        // Try cache first
        const cached = await this.cacheService.getCachedOrder(query.orderId);
        if (cached) {
            return this.toDto(cached);
        }

        // Cache miss - fetch from database
        const order = await this.orderRepository.findById(query.orderId);
        if (!order) {
            throw new NotFoundException('Order not found');
        }

        // Cache for future requests
        await this.cacheService.cacheOrder(order);

        // Increment view count
        await this.cacheService.incrementOrderViewCount(query.orderId);

        return this.toDto(order);
    }
}
```

### Example: Update Order with Lock

```typescript
@CommandHandler(ConfirmOrderCommand)
export class ConfirmOrderHandler implements ICommandHandler<ConfirmOrderCommand> {
    constructor(
        private readonly orderRepository: OrderRepository,
        private readonly cacheService: OrderCacheService,
    ) {}

    async execute(command: ConfirmOrderCommand): Promise<void> {
        // Use distributed lock to prevent race conditions
        await this.cacheService.withOrderLock(command.orderId, async () => {
            const order = await this.orderRepository.findById(command.orderId);
            if (!order) {
                throw new NotFoundException('Order not found');
            }

            order.confirm();
            await this.orderRepository.save(order);

            // Invalidate cache after update
            await this.cacheService.invalidateOrder(command.orderId);
            await this.cacheService.invalidateCustomerOrders(order.customerId);
        });
    }
}
```

## Advanced Features

### Lua Scripts

Execute Lua scripts for atomic operations:

```typescript
const script = `
    local current = redis.call('GET', KEYS[1])
    if current and tonumber(current) > tonumber(ARGV[1]) then
        return redis.call('DECRBY', KEYS[1], ARGV[1])
    else
        return -1
    end
`;

const result = await this.redisson.eval(script, ['stock:product:123'], ['5']);
```

### Script Caching

Load and cache scripts for better performance:

```typescript
// Load script once
const sha1 = await this.redisson.scriptLoad(script);

// Execute cached script multiple times
const result = await this.redisson.evalsha(sha1, ['key'], ['arg']);
```

## Best Practices

1. **Use Appropriate TTL**: Set reasonable expiration times to prevent stale data
2. **Cache Invalidation**: Always invalidate cache after updates
3. **Distributed Locks**: Use locks for critical sections to prevent race conditions
4. **Key Naming**: Use consistent, hierarchical key naming (e.g., `entity:id:field`)
5. **Error Handling**: Always handle Redis errors gracefully with fallbacks
6. **Monitoring**: Log cache hits/misses for performance monitoring

## Testing

### Start Redis with Docker

```bash
docker run -d -p 6379:6379 --name redis redis:7-alpine
```

### Test Connection

```bash
redis-cli ping
# Should return: PONG
```

## Troubleshooting

### Connection Issues

```typescript
// Check if Redis is connected
try {
    await this.redisson.redis.ping();
    console.log('Redis connected');
} catch (error) {
    console.error('Redis connection failed:', error);
}
```

### Memory Issues

```bash
# Check Redis memory usage
redis-cli INFO memory

# Clear all keys (development only!)
redis-cli FLUSHDB
```

## Performance Tips

1. **Batch Operations**: Use pipelines for multiple operations
2. **Compression**: Compress large JSON objects before caching
3. **Lazy Loading**: Only cache frequently accessed data
4. **TTL Strategy**: Use shorter TTL for frequently changing data
5. **Connection Pooling**: Configure appropriate pool size in production

## References

- [Redisson Documentation](https://github.com/redisson/redisson)
- [ioredis Documentation](https://github.com/redis/ioredis)
- [Redis Commands](https://redis.io/commands)


================================================
FILE: apps/api/migrations/001_create_orders_tables.sql
================================================
-- Create orders table
CREATE TABLE IF NOT EXISTS orders (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    customer_id VARCHAR(255) NOT NULL,
    status VARCHAR(50) NOT NULL DEFAULT 'pending',
    total_amount DECIMAL(10, 2) NOT NULL DEFAULT 0,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Create order_items table
CREATE TABLE IF NOT EXISTS order_items (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
    product_id VARCHAR(255) NOT NULL,
    quantity INTEGER NOT NULL,
    unit_price DECIMAL(10, 2) NOT NULL,
    subtotal DECIMAL(10, 2) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Create indexes
CREATE INDEX IF NOT EXISTS idx_orders_customer_id ON orders(customer_id);
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items(order_id);


================================================
FILE: apps/api/nest-cli.json
================================================
{
    "$schema": "https://json.schemastore.org/nest-cli",
    "collection": "@nestjs/schematics",
    "sourceRoot": "src",
    "compilerOptions": {
        "builder": "swc",
        "typeCheck": true,
        "deleteOutDir": true,
        "tsConfigPath": "tsconfig.build.json"
    }
}


================================================
FILE: apps/api/package.json
================================================
{
    "name": "@a3s-lab/api",
    "version": "1.0.0",
    "description": "Production-ready NestJS API with Domain-Driven Design and Clean Architecture",
    "author": "",
    "private": true,
    "license": "MIT",
    "scripts": {
        "build": "nest build",
        "format": "biome format --write .",
        "format:check": "biome format .",
        "lint": "biome lint --write .",
        "lint:check": "biome lint .",
        "start": "nest start",
        "start:dev": "nest start --watch",
        "start:debug": "nest start --debug --watch",
        "start:prod": "node dist/main",
        "test": "jest",
        "test:watch": "jest --watch",
        "test:cov": "jest --coverage",
        "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
        "test:e2e": "jest --config ./test/jest-e2e.json"
    },
    "dependencies": {
        "@a3s-lab/kysely": "workspace:*",
        "@a3s-lab/redisson": "workspace:*",
        "@a3s-lab/logger": "workspace:*",
        "@a3s-lab/bullmq": "workspace:*",
        "@a3s-lab/nats": "workspace:*",
        "@a3s-lab/rustfs": "workspace:*",
        "@a3s-lab/etcd": "workspace:*",
        "@nestjs/common": "^10.0.0",
        "@nestjs/config": "^3.0.0",
        "@nestjs/core": "^10.0.0",
        "@nestjs/cqrs": "^10.0.0",
        "@nestjs/platform-express": "^10.0.0",
        "@nestjs/swagger": "^7.0.0",
        "@nestjs/terminus": "^10.0.0",
        "class-transformer": "^0.5.1",
        "class-validator": "^0.14.0",
        "kysely": "^0.28.11",
        "pg": "^8.18.0",
        "pino": "^9.0.0",
        "pino-http": "^10.0.0",
        "reflect-metadata": "^0.1.13",
        "rxjs": "^7.8.1",
        "uuid": "^9.0.0"
    },
    "devDependencies": {
        "@nestjs/cli": "^10.0.0",
        "@nestjs/schematics": "^10.0.0",
        "@nestjs/testing": "^10.0.0",
        "@swc/cli": "^0.7.10",
        "@swc/core": "^1.15.11",
        "@types/express": "^4.17.17",
        "@types/jest": "^29.5.0",
        "@types/node": "^20.0.0",
        "@types/pg": "^8.16.0",
        "@types/uuid": "^9.0.0",
        "jest": "^29.5.0",
        "source-map-support": "^0.5.21",
        "supertest": "^6.3.3",
        "ts-jest": "^29.1.0",
        "ts-loader": "^9.4.3",
        "ts-node": "^10.9.1",
        "tsconfig-paths": "^4.2.0",
        "typescript": "^5.1.3"
    },
    "jest": {
        "moduleFileExtensions": [
            "js",
            "json",
            "ts"
        ],
        "rootDir": "src",
        "testRegex": ".*\\.spec\\.ts$",
        "transform": {
            "^.+\\.(t|j)s$": "ts-jest"
        },
        "collectCoverageFrom": [
            "**/*.(t|j)s"
        ],
        "coverageDirectory": "../coverage",
        "testEnvironment": "node",
        "moduleNameMapper": {
            "^@/(.*)$": "<rootDir>/$1"
        }
    }
}


================================================
FILE: apps/api/src/app.module.ts
================================================
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { OrderModule } from './modules/order/order.module';
import { DatabaseModule } from './shared/database';
import { RedisModule } from './shared/redis';

// Shared infrastructure modules
import { LoggerModule } from '@a3s-lab/logger';
import { AuthModule } from './shared/auth';
import { MetricsModule } from './shared/metrics';
import { CacheModule } from './shared/cache';
import { CircuitBreakerModule } from './shared/circuit-breaker';
import { RetryModule } from './shared/retry';
import { HealthModule } from './shared/health';
import { ValidationModule } from './shared/validation';
import { SerializationModule } from './shared/serialization';
import { RateLimitingModule } from './shared/rate-limiting';
import { TenantModule } from './shared/tenant';
import { AuditModule } from './shared/audit';
import { ApiResponseModule } from './shared/api-response';
import { ApiVersioningModule } from './shared/api-versioning';
import { FeatureFlagsModule } from './shared/feature-flags';
import { FileUploadModule } from './shared/file-upload';
import { TransformModule } from './shared/transform';
import { ErrorsModule } from './shared/errors';
import { OpenAPIModule } from './shared/openapi';
import { TrackingModule } from './shared/tracking';

@Module({
    imports: [
        // NestJS Config
        ConfigModule.forRoot({
            isGlobal: true,
            envFilePath: '.env',
        }),

        // Logger (global JSON logging with request tracing)
        LoggerModule.register({
            level: process.env.LOG_LEVEL as any || 'info',
            name: 'nestify-api',
            json: true,
        }),

        // Database (Kysely + PostgreSQL)
        DatabaseModule,

        // Redis (Redisson)
        RedisModule,

        // Auth (JWT + RBAC)
        AuthModule,

        // Metrics (Prometheus)
        MetricsModule,

        // Cache
        CacheModule,

        // Circuit Breaker
        CircuitBreakerModule,

        // Retry with exponential backoff
        RetryModule,

        // Health checks
        HealthModule,

        // Validation
        ValidationModule,

        // Serialization (class-transformer)
        SerializationModule,

        // Rate limiting
        RateLimitingModule,

        // Tenant isolation
        TenantModule,

        // Audit logging
        AuditModule,

        // API response wrapper
        ApiResponseModule,

        // API versioning
        ApiVersioningModule,

        // Feature flags
        FeatureFlagsModule,

        // File upload
        FileUploadModule,

        // Transform interceptor
        TransformModule,

        // Error handling
        ErrorsModule,

        // OpenAPI decorators
        OpenAPIModule,

        // Request tracking
        TrackingModule,

        // Business modules
        OrderModule,
    ],
})
export class AppModule {}


================================================
FILE: apps/api/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './shared/presentation/filters/http-exception.filter';
import { DomainExceptionFilter } from './shared/presentation/filters/domain-exception.filter';
import { LoggingInterceptor } from './shared/presentation/interceptors/logging.interceptor';

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    app.setGlobalPrefix('api');

    app.useGlobalPipes(
        new ValidationPipe({
            whitelist: true,
            forbidNonWhitelisted: true,
            transform: true,
        }),
    );

    app.useGlobalFilters(new HttpExceptionFilter(), new DomainExceptionFilter());

    app.useGlobalInterceptors(new LoggingInterceptor());

    const config = new DocumentBuilder()
        .setTitle('NestJS DDD Template')
        .setDescription('Production-ready NestJS template with Domain-Driven Design')
        .setVersion('1.0')
        .addTag('orders')
        .build();

    const document = SwaggerModule.createDocument(app, config);
    SwaggerModule.setup('api/docs', app, document);

    const port = process.env.APP_PORT || 3000;
    await app.listen(port);

    console.log(`Application is running on: http://localhost:${port}`);
    console.log(`Swagger documentation: http://localhost:${port}/api/docs`);
}

bootstrap();


================================================
FILE: apps/api/src/modules/order/application/commands/cancel-order/cancel-order.command.ts
================================================
export class CancelOrderCommand {
    constructor(public readonly orderId: string) {}
}


================================================
FILE: apps/api/src/modules/order/application/commands/cancel-order/cancel-order.dto.ts
================================================
import { IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class CancelOrderDto {
    @ApiProperty({ example: 'order-id-123' })
    @IsString()
    orderId: string;
}


================================================
FILE: apps/api/src/modules/order/application/commands/cancel-order/cancel-order.handler.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { CancelOrderHandler } from './cancel-order.handler';
import { CancelOrderCommand } from './cancel-order.command';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { IEventBus, EVENT_BUS } from '@/shared/infrastructure/messaging/event-bus.interface';
import { Order } from '../../../domain/entities/order.entity';
import { OrderItem } from '../../../domain/entities/order-item.entity';
import { Money } from '../../../domain/value-objects/money.vo';
import { Quantity } from '../../../domain/value-objects/quantity.vo';
import { OrderNotFoundException } from '../../../domain/exceptions/order-not-found.exception';
import { InvalidOrderStateException } from '../../../domain/exceptions/invalid-order-state.exception';

describe('CancelOrderHandler', () => {
    let handler: CancelOrderHandler;
    let orderRepository: jest.Mocked<IOrderRepository>;
    let eventBus: jest.Mocked<IEventBus>;

    beforeEach(async () => {
        const mockOrderRepository: Partial<IOrderRepository> = {
            save: jest.fn(),
            findById: jest.fn(),
            findByCustomerId: jest.fn(),
            delete: jest.fn(),
        };

        const mockEventBus: Partial<IEventBus> = {
            publish: jest.fn(),
            publishAll: jest.fn(),
        };

        const module: TestingModule = await Test.createTestingModule({
            providers: [
                CancelOrderHandler,
                {
                    provide: ORDER_REPOSITORY,
                    useValue: mockOrderRepository,
                },
                {
                    provide: EVENT_BUS,
                    useValue: mockEventBus,
                },
            ],
        }).compile();

        handler = module.get<CancelOrderHandler>(CancelOrderHandler);
        orderRepository = module.get(ORDER_REPOSITORY);
        eventBus = module.get(EVENT_BUS);
    });

    it('should be defined', () => {
        expect(handler).toBeDefined();
    });

    describe('execute', () => {
        it('should cancel existing pending order', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            const command = new CancelOrderCommand(order.id);

            orderRepository.findById.mockResolvedValue(order);
            orderRepository.save.mockImplementation(async (o: Order) => o);

            await handler.execute(command);

            expect(orderRepository.findById).toHaveBeenCalledWith(order.id);
            expect(order.status.isCancelled()).toBe(true);
            expect(orderRepository.save).toHaveBeenCalledWith(order);
        });

        it('should cancel confirmed order', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            order.confirm();
            order.clearEvents();
            const command = new CancelOrderCommand(order.id);

            orderRepository.findById.mockResolvedValue(order);
            orderRepository.save.mockImplementation(async (o: Order) => o);

            await handler.execute(command);

            expect(order.status.isCancelled()).toBe(true);
        });

        it('should publish OrderCancelledEvent', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            const command = new CancelOrderCommand(order.id);

            orderRepository.findById.mockResolvedValue(order);
            orderRepository.save.mockImplementation(async (o: Order) => o);

            await handler.execute(command);

            expect(eventBus.publishAll).toHaveBeenCalledTimes(1);
        });

        it('should throw OrderNotFoundException when order not found', async () => {
            const command = new CancelOrderCommand('non-existent-id');

            orderRepository.findById.mockResolvedValue(null);

            await expect(handler.execute(command)).rejects.toThrow(OrderNotFoundException);
        });

        it('should throw InvalidOrderStateException when order is already cancelled', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            order.cancel(); // Already cancelled
            const command = new CancelOrderCommand(order.id);

            orderRepository.findById.mockResolvedValue(order);

            await expect(handler.execute(command)).rejects.toThrow(InvalidOrderStateException);
        });
    });
});


================================================
FILE: apps/api/src/modules/order/application/commands/cancel-order/cancel-order.handler.ts
================================================
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { Inject } from '@nestjs/common';
import { CancelOrderCommand } from './cancel-order.command';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { OrderNotFoundException } from '../../../domain/exceptions/order-not-found.exception';
import { EVENT_BUS, IEventBus } from '@/shared/infrastructure/messaging/event-bus.interface';

@CommandHandler(CancelOrderCommand)
export class CancelOrderHandler implements ICommandHandler<CancelOrderCommand> {
    constructor(
        @Inject(ORDER_REPOSITORY)
        private readonly orderRepository: IOrderRepository,
        @Inject(EVENT_BUS)
        private readonly eventBus: IEventBus,
    ) {}

    async execute(command: CancelOrderCommand): Promise<void> {
        const order = await this.orderRepository.findById(command.orderId);

        if (!order) {
            throw new OrderNotFoundException(command.orderId);
        }

        order.cancel();

        await this.orderRepository.save(order);

        await this.eventBus.publishAll(order.domainEvents);
        order.clearEvents();
    }
}


================================================
FILE: apps/api/src/modules/order/application/commands/confirm-order/confirm-order.command.ts
================================================
export class ConfirmOrderCommand {
    constructor(public readonly orderId: string) {}
}


================================================
FILE: apps/api/src/modules/order/application/commands/confirm-order/confirm-order.dto.ts
================================================
import { IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class ConfirmOrderDto {
    @ApiProperty({ example: 'order-id-123' })
    @IsString()
    orderId: string;
}


================================================
FILE: apps/api/src/modules/order/application/commands/confirm-order/confirm-order.handler.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { ConfirmOrderHandler } from './confirm-order.handler';
import { ConfirmOrderCommand } from './confirm-order.command';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { IEventBus, EVENT_BUS } from '@/shared/infrastructure/messaging/event-bus.interface';
import { Order } from '../../../domain/entities/order.entity';
import { OrderItem } from '../../../domain/entities/order-item.entity';
import { Money } from '../../../domain/value-objects/money.vo';
import { Quantity } from '../../../domain/value-objects/quantity.vo';
import { OrderNotFoundException } from '../../../domain/exceptions/order-not-found.exception';
import { InvalidOrderStateException } from '../../../domain/exceptions/invalid-order-state.exception';

describe('ConfirmOrderHandler', () => {
    let handler: ConfirmOrderHandler;
    let orderRepository: jest.Mocked<IOrderRepository>;
    let eventBus: jest.Mocked<IEventBus>;

    beforeEach(async () => {
        const mockOrderRepository: Partial<IOrderRepository> = {
            save: jest.fn(),
            findById: jest.fn(),
            findByCustomerId: jest.fn(),
            delete: jest.fn(),
        };

        const mockEventBus: Partial<IEventBus> = {
            publish: jest.fn(),
            publishAll: jest.fn(),
        };

        const module: TestingModule = await Test.createTestingModule({
            providers: [
                ConfirmOrderHandler,
                {
                    provide: ORDER_REPOSITORY,
                    useValue: mockOrderRepository,
                },
                {
                    provide: EVENT_BUS,
                    useValue: mockEventBus,
                },
            ],
        }).compile();

        handler = module.get<ConfirmOrderHandler>(ConfirmOrderHandler);
        orderRepository = module.get(ORDER_REPOSITORY);
        eventBus = module.get(EVENT_BUS);
    });

    it('should be defined', () => {
        expect(handler).toBeDefined();
    });

    describe('execute', () => {
        it('should confirm existing pending order', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            const command = new ConfirmOrderCommand(order.id);

            orderRepository.findById.mockResolvedValue(order);
            orderRepository.save.mockImplementation(async (o: Order) => o);

            await handler.execute(command);

            expect(orderRepository.findById).toHaveBeenCalledWith(order.id);
            expect(order.status.isConfirmed()).toBe(true);
            expect(orderRepository.save).toHaveBeenCalledWith(order);
        });

        it('should publish OrderConfirmedEvent', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            const command = new ConfirmOrderCommand(order.id);

            orderRepository.findById.mockResolvedValue(order);
            orderRepository.save.mockImplementation(async (o: Order) => o);

            await handler.execute(command);

            expect(eventBus.publishAll).toHaveBeenCalledTimes(1);
            const publishedEvents = eventBus.publishAll.mock.calls[0][0];
            expect(publishedEvents.length).toBeGreaterThan(0);
        });

        it('should throw OrderNotFoundException when order not found', async () => {
            const command = new ConfirmOrderCommand('non-existent-id');

            orderRepository.findById.mockResolvedValue(null);

            await expect(handler.execute(command)).rejects.toThrow(OrderNotFoundException);
            await expect(handler.execute(command)).rejects.toThrow('non-existent-id');
        });

        it('should throw InvalidOrderStateException when order is not pending', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            order.confirm(); // Already confirmed
            const command = new ConfirmOrderCommand(order.id);

            orderRepository.findById.mockResolvedValue(order);

            await expect(handler.execute(command)).rejects.toThrow(InvalidOrderStateException);
        });

        it('should not save order if confirmation fails', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            order.confirm(); // Already confirmed
            const command = new ConfirmOrderCommand(order.id);

            orderRepository.findById.mockResolvedValue(order);

            await expect(handler.execute(command)).rejects.toThrow();
            expect(orderRepository.save).not.toHaveBeenCalled();
        });
    });
});


================================================
FILE: apps/api/src/modules/order/application/commands/confirm-order/confirm-order.handler.ts
================================================
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { Inject } from '@nestjs/common';
import { ConfirmOrderCommand } from './confirm-order.command';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { OrderNotFoundException } from '../../../domain/exceptions/order-not-found.exception';
import { EVENT_BUS, IEventBus } from '@/shared/infrastructure/messaging/event-bus.interface';

@CommandHandler(ConfirmOrderCommand)
export class ConfirmOrderHandler implements ICommandHandler<ConfirmOrderCommand> {
    constructor(
        @Inject(ORDER_REPOSITORY)
        private readonly orderRepository: IOrderRepository,
        @Inject(EVENT_BUS)
        private readonly eventBus: IEventBus,
    ) {}

    async execute(command: ConfirmOrderCommand): Promise<void> {
        const order = await this.orderRepository.findById(command.orderId);

        if (!order) {
            throw new OrderNotFoundException(command.orderId);
        }

        order.confirm();

        await this.orderRepository.save(order);

        await this.eventBus.publishAll(order.domainEvents);
        order.clearEvents();
    }
}


================================================
FILE: apps/api/src/modules/order/application/commands/create-order/create-order.command.ts
================================================
export class CreateOrderCommand {
    constructor(
        public readonly customerId: string,
        public readonly items: Array<{
            productId: string;
            quantity: number;
            unitPrice: number;
        }>,
    ) {}
}


================================================
FILE: apps/api/src/modules/order/application/commands/create-order/create-order.dto.ts
================================================
import { IsString, IsArray, ValidateNested, IsNumber, Min } from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty } from '@nestjs/swagger';

export class CreateOrderItemDto {
    @ApiProperty({ example: 'product-123' })
    @IsString()
    productId: string;

    @ApiProperty({ example: 2 })
    @IsNumber()
    @Min(1)
    quantity: number;

    @ApiProperty({ example: 10.99 })
    @IsNumber()
    @Min(0)
    unitPrice: number;
}

export class CreateOrderDto {
    @ApiProperty({ example: 'customer-456' })
    @IsString()
    customerId: string;

    @ApiProperty({ type: [CreateOrderItemDto] })
    @IsArray()
    @ValidateNested({ each: true })
    @Type(() => CreateOrderItemDto)
    items: CreateOrderItemDto[];
}


================================================
FILE: apps/api/src/modules/order/application/commands/create-order/create-order.handler.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { CreateOrderHandler } from './create-order.handler';
import { CreateOrderCommand } from './create-order.command';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { IEventBus, EVENT_BUS } from '@/shared/infrastructure/messaging/event-bus.interface';
import { Order } from '../../../domain/entities/order.entity';

describe('CreateOrderHandler', () => {
    let handler: CreateOrderHandler;
    let orderRepository: jest.Mocked<IOrderRepository>;
    let eventBus: jest.Mocked<IEventBus>;

    beforeEach(async () => {
        const mockOrderRepository: Partial<IOrderRepository> = {
            save: jest.fn(),
            findById: jest.fn(),
            findByCustomerId: jest.fn(),
            delete: jest.fn(),
        };

        const mockEventBus: Partial<IEventBus> = {
            publish: jest.fn(),
            publishAll: jest.fn(),
        };

        const module: TestingModule = await Test.createTestingModule({
            providers: [
                CreateOrderHandler,
                {
                    provide: ORDER_REPOSITORY,
                    useValue: mockOrderRepository,
                },
                {
                    provide: EVENT_BUS,
                    useValue: mockEventBus,
                },
            ],
        }).compile();

        handler = module.get<CreateOrderHandler>(CreateOrderHandler);
        orderRepository = module.get(ORDER_REPOSITORY);
        eventBus = module.get(EVENT_BUS);
    });

    it('should be defined', () => {
        expect(handler).toBeDefined();
    });

    describe('execute', () => {
        it('should create order with valid command', async () => {
            const command = new CreateOrderCommand('customer-1', [
                { productId: 'product-1', quantity: 2, unitPrice: 10 },
                { productId: 'product-2', quantity: 1, unitPrice: 20 },
            ]);

            orderRepository.save.mockImplementation(async (order: Order) => order);

            const orderId = await handler.execute(command);

            expect(orderId).toBeDefined();
            expect(typeof orderId).toBe('string');
            expect(orderRepository.save).toHaveBeenCalledTimes(1);
        });

        it('should save order with correct properties', async () => {
            const command = new CreateOrderCommand('customer-1', [
                { productId: 'product-1', quantity: 2, unitPrice: 10 },
            ]);

            orderRepository.save.mockImplementation(async (order: Order) => order);

            await handler.execute(command);

            expect(orderRepository.save).toHaveBeenCalledWith(
                expect.objectContaining({
                    customerId: 'customer-1',
                }),
            );

            const savedOrder = orderRepository.save.mock.calls[0][0];
            expect(savedOrder.items.length).toBe(1);
            expect(savedOrder.items[0].productId).toBe('product-1');
            expect(savedOrder.items[0].quantity.value).toBe(2);
            expect(savedOrder.items[0].unitPrice.amount).toBe(10);
        });

        it('should publish domain events after saving', async () => {
            const command = new CreateOrderCommand('customer-1', [
                { productId: 'product-1', quantity: 1, unitPrice: 10 },
            ]);

            orderRepository.save.mockImplementation(async (order: Order) => order);

            await handler.execute(command);

            expect(eventBus.publishAll).toHaveBeenCalledTimes(1);
            expect(eventBus.publishAll).toHaveBeenCalledWith(
                expect.arrayContaining([
                    expect.objectContaining({
                        customerId: 'customer-1',
                    }),
                ]),
            );
        });

        it('should clear events after publishing', async () => {
            const command = new CreateOrderCommand('customer-1', [
                { productId: 'product-1', quantity: 1, unitPrice: 10 },
            ]);

            let savedOrder: Order;
            orderRepository.save.mockImplementation(async (order: Order) => {
                savedOrder = order;
                return order;
            });

            await handler.execute(command);

            // Events should be cleared after publishing
            expect(savedOrder!.domainEvents.length).toBe(0);
        });

        it('should create order with multiple items', async () => {
            const command = new CreateOrderCommand('customer-1', [
                { productId: 'product-1', quantity: 2, unitPrice: 10 },
                { productId: 'product-2', quantity: 1, unitPrice: 20 },
                { productId: 'product-3', quantity: 3, unitPrice: 5 },
            ]);

            orderRepository.save.mockImplementation(async (order: Order) => order);

            await handler.execute(command);

            const savedOrder = orderRepository.save.mock.calls[0][0];
            expect(savedOrder.items.length).toBe(3);
        });

        it('should handle repository errors', async () => {
            const command = new CreateOrderCommand('customer-1', [
                { productId: 'product-1', quantity: 1, unitPrice: 10 },
            ]);

            orderRepository.save.mockRejectedValue(new Error('Database error'));

            await expect(handler.execute(command)).rejects.toThrow('Database error');
        });
    });
});


================================================
FILE: apps/api/src/modules/order/application/commands/create-order/create-order.handler.ts
================================================
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { Inject } from '@nestjs/common';
import { CreateOrderCommand } from './create-order.command';
import { Order } from '../../../domain/entities/order.entity';
import { OrderItem } from '../../../domain/entities/order-item.entity';
import { Money } from '../../../domain/value-objects/money.vo';
import { Quantity } from '../../../domain/value-objects/quantity.vo';
import { OrderId } from '../../../domain/value-objects/order-id.vo';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { EVENT_BUS, IEventBus } from '@/shared/infrastructure/messaging/event-bus.interface';

@CommandHandler(CreateOrderCommand)
export class CreateOrderHandler implements ICommandHandler<CreateOrderCommand> {
    constructor(
        @Inject(ORDER_REPOSITORY)
        private readonly orderRepository: IOrderRepository,
        @Inject(EVENT_BUS)
        private readonly eventBus: IEventBus,
    ) {}

    async execute(command: CreateOrderCommand): Promise<string> {
        const orderItems = command.items.map(item =>
            OrderItem.create({
                id: OrderId.create().value,
                productId: item.productId,
                quantity: Quantity.create(item.quantity),
                unitPrice: Money.create(item.unitPrice),
            }),
        );

        const order = Order.create(command.customerId, orderItems);

        await this.orderRepository.save(order);

        await this.eventBus.publishAll(order.domainEvents);
        order.clearEvents();

        return order.id;
    }
}


================================================
FILE: apps/api/src/modules/order/application/event-handlers/order-confirmed.handler.ts
================================================
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
import { Logger } from '@nestjs/common';
import { OrderConfirmedEvent } from '../../domain/events/order-confirmed.event';

@EventsHandler(OrderConfirmedEvent)
export class OrderConfirmedHandler implements IEventHandler<OrderConfirmedEvent> {
    private readonly logger = new Logger(OrderConfirmedHandler.name);

    async handle(event: OrderConfirmedEvent) {
        this.logger.log(`Order confirmed: ${event.orderId}`);
    }
}


================================================
FILE: apps/api/src/modules/order/application/event-handlers/order-created.handler.ts
================================================
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
import { Logger } from '@nestjs/common';
import { OrderCreatedEvent } from '../../domain/events/order-created.event';

@EventsHandler(OrderCreatedEvent)
export class OrderCreatedHandler implements IEventHandler<OrderCreatedEvent> {
    private readonly logger = new Logger(OrderCreatedHandler.name);

    async handle(event: OrderCreatedEvent) {
        this.logger.log(
            `Order created: ${event.orderId} for customer ${event.customerId} with total ${event.totalAmount.amount}`,
        );
    }
}


================================================
FILE: apps/api/src/modules/order/application/queries/get-order/get-order.handler.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { GetOrderHandler } from './get-order.handler';
import { GetOrderQuery } from './get-order.query';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { Order } from '../../../domain/entities/order.entity';
import { OrderItem } from '../../../domain/entities/order-item.entity';
import { Money } from '../../../domain/value-objects/money.vo';
import { Quantity } from '../../../domain/value-objects/quantity.vo';
import { OrderNotFoundException } from '../../../domain/exceptions/order-not-found.exception';

describe('GetOrderHandler', () => {
    let handler: GetOrderHandler;
    let orderRepository: jest.Mocked<IOrderRepository>;

    beforeEach(async () => {
        const mockOrderRepository: Partial<IOrderRepository> = {
            save: jest.fn(),
            findById: jest.fn(),
            findByCustomerId: jest.fn(),
            delete: jest.fn(),
        };

        const module: TestingModule = await Test.createTestingModule({
            providers: [
                GetOrderHandler,
                {
                    provide: ORDER_REPOSITORY,
                    useValue: mockOrderRepository,
                },
            ],
        }).compile();

        handler = module.get<GetOrderHandler>(GetOrderHandler);
        orderRepository = module.get(ORDER_REPOSITORY);
    });

    it('should be defined', () => {
        expect(handler).toBeDefined();
    });

    describe('execute', () => {
        it('should return order DTO for existing order', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(2),
                    unitPrice: Money.create(10),
                }),
                OrderItem.create({
                    id: 'item-2',
                    productId: 'product-2',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(20),
                }),
            ];
            const order = Order.create('customer-1', items);
            const query = new GetOrderQuery(order.id);

            orderRepository.findById.mockResolvedValue(order);

            const result = await handler.execute(query);

            expect(result).toBeDefined();
            expect(result.id).toBe(order.id);
            expect(result.customerId).toBe('customer-1');
            expect(result.items.length).toBe(2);
            expect(result.status).toBe('PENDING');
            expect(result.totalAmount).toBe(40);
        });

        it('should map order items correctly', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(3),
                    unitPrice: Money.create(15),
                }),
            ];
            const order = Order.create('customer-1', items);
            const query = new GetOrderQuery(order.id);

            orderRepository.findById.mockResolvedValue(order);

            const result = await handler.execute(query);

            expect(result.items[0].id).toBe('item-1');
            expect(result.items[0].productId).toBe('product-1');
            expect(result.items[0].quantity).toBe(3);
            expect(result.items[0].unitPrice).toBe(15);
            expect(result.items[0].totalPrice).toBe(45);
        });

        it('should include timestamps in response', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            const query = new GetOrderQuery(order.id);

            orderRepository.findById.mockResolvedValue(order);

            const result = await handler.execute(query);

            expect(result.createdAt).toBeInstanceOf(Date);
            expect(result.updatedAt).toBeInstanceOf(Date);
        });

        it('should throw OrderNotFoundException when order not found', async () => {
            const query = new GetOrderQuery('non-existent-id');

            orderRepository.findById.mockResolvedValue(null);

            await expect(handler.execute(query)).rejects.toThrow(OrderNotFoundException);
            await expect(handler.execute(query)).rejects.toThrow('non-existent-id');
        });

        it('should handle confirmed order status', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            order.confirm();
            const query = new GetOrderQuery(order.id);

            orderRepository.findById.mockResolvedValue(order);

            const result = await handler.execute(query);

            expect(result.status).toBe('CONFIRMED');
        });

        it('should handle cancelled order status', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const order = Order.create('customer-1', items);
            order.cancel();
            const query = new GetOrderQuery(order.id);

            orderRepository.findById.mockResolvedValue(order);

            const result = await handler.execute(query);

            expect(result.status).toBe('CANCELLED');
        });
    });
});


================================================
FILE: apps/api/src/modules/order/application/queries/get-order/get-order.handler.ts
================================================
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { Inject } from '@nestjs/common';
import { GetOrderQuery } from './get-order.query';
import { OrderResponseDto } from './order.response.dto';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { OrderNotFoundException } from '../../../domain/exceptions/order-not-found.exception';

@QueryHandler(GetOrderQuery)
export class GetOrderHandler implements IQueryHandler<GetOrderQuery> {
    constructor(
        @Inject(ORDER_REPOSITORY)
        private readonly orderRepository: IOrderRepository,
    ) {}

    async execute(query: GetOrderQuery): Promise<OrderResponseDto> {
        const order = await this.orderRepository.findById(query.orderId);

        if (!order) {
            throw new OrderNotFoundException(query.orderId);
        }

        return {
            id: order.id,
            customerId: order.customerId,
            items: order.items.map(item => ({
                id: item.id,
                productId: item.productId,
                quantity: item.quantity.value,
                unitPrice: item.unitPrice.amount,
                totalPrice: item.getTotalPrice().amount,
            })),
            status: order.status.value,
            totalAmount: order.getTotalAmount().amount,
            createdAt: order.createdAt,
            updatedAt: order.updatedAt,
        };
    }
}


================================================
FILE: apps/api/src/modules/order/application/queries/get-order/get-order.query.ts
================================================
export class GetOrderQuery {
    constructor(public readonly orderId: string) {}
}


================================================
FILE: apps/api/src/modules/order/application/queries/get-order/order.response.dto.ts
================================================
import { ApiProperty } from '@nestjs/swagger';

export class OrderItemResponseDto {
    @ApiProperty()
    id: string;

    @ApiProperty()
    productId: string;

    @ApiProperty()
    quantity: number;

    @ApiProperty()
    unitPrice: number;

    @ApiProperty()
    totalPrice: number;
}

export class OrderResponseDto {
    @ApiProperty()
    id: string;

    @ApiProperty()
    customerId: string;

    @ApiProperty({ type: [OrderItemResponseDto] })
    items: OrderItemResponseDto[];

    @ApiProperty()
    status: string;

    @ApiProperty()
    totalAmount: number;

    @ApiProperty()
    createdAt: Date;

    @ApiProperty()
    updatedAt: Date;
}


================================================
FILE: apps/api/src/modules/order/application/queries/list-orders/list-orders.handler.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { ListOrdersHandler } from './list-orders.handler';
import { ListOrdersQuery } from './list-orders.query';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';
import { Order } from '../../../domain/entities/order.entity';
import { OrderItem } from '../../../domain/entities/order-item.entity';
import { Money } from '../../../domain/value-objects/money.vo';
import { Quantity } from '../../../domain/value-objects/quantity.vo';

describe('ListOrdersHandler', () => {
    let handler: ListOrdersHandler;
    let orderRepository: jest.Mocked<IOrderRepository>;

    beforeEach(async () => {
        const mockOrderRepository: Partial<IOrderRepository> = {
            save: jest.fn(),
            findById: jest.fn(),
            findByCustomerId: jest.fn(),
            delete: jest.fn(),
        };

        const module: TestingModule = await Test.createTestingModule({
            providers: [
                ListOrdersHandler,
                {
                    provide: ORDER_REPOSITORY,
                    useValue: mockOrderRepository,
                },
            ],
        }).compile();

        handler = module.get<ListOrdersHandler>(ListOrdersHandler);
        orderRepository = module.get(ORDER_REPOSITORY);
    });

    it('should be defined', () => {
        expect(handler).toBeDefined();
    });

    describe('execute', () => {
        it('should return list of orders for customer', async () => {
            const items1 = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const items2 = [
                OrderItem.create({
                    id: 'item-2',
                    productId: 'product-2',
                    quantity: Quantity.create(2),
                    unitPrice: Money.create(20),
                }),
            ];

            const order1 = Order.create('customer-1', items1);
            const order2 = Order.create('customer-1', items2);

            const query = new ListOrdersQuery('customer-1');

            orderRepository.findByCustomerId.mockResolvedValue([order1, order2]);

            const result = await handler.execute(query);

            expect(result.orders.length).toBe(2);
            expect(result.total).toBe(2);
            expect(result.orders[0].customerId).toBe('customer-1');
            expect(result.orders[1].customerId).toBe('customer-1');
        });

        it('should return empty list when no orders found', async () => {
            const query = new ListOrdersQuery('customer-1');

            orderRepository.findByCustomerId.mockResolvedValue([]);

            const result = await handler.execute(query);

            expect(result.orders.length).toBe(0);
            expect(result.total).toBe(0);
        });

        it('should map all order properties correctly', async () => {
            const items = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(2),
                    unitPrice: Money.create(15),
                }),
            ];
            const order = Order.create('customer-1', items);
            const query = new ListOrdersQuery('customer-1');

            orderRepository.findByCustomerId.mockResolvedValue([order]);

            const result = await handler.execute(query);

            expect(result.orders[0].id).toBe(order.id);
            expect(result.orders[0].customerId).toBe('customer-1');
            expect(result.orders[0].items.length).toBe(1);
            expect(result.orders[0].status).toBe('PENDING');
            expect(result.orders[0].totalAmount).toBe(30);
        });

        it('should handle orders with different statuses', async () => {
            const items1 = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(10),
                }),
            ];
            const items2 = [
                OrderItem.create({
                    id: 'item-2',
                    productId: 'product-2',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(20),
                }),
            ];

            const order1 = Order.create('customer-1', items1);
            const order2 = Order.create('customer-1', items2);
            order2.confirm();

            const query = new ListOrdersQuery('customer-1');

            orderRepository.findByCustomerId.mockResolvedValue([order1, order2]);

            const result = await handler.execute(query);

            expect(result.orders[0].status).toBe('PENDING');
            expect(result.orders[1].status).toBe('CONFIRMED');
        });

        it('should return empty list when customerId is not provided', async () => {
            const query = new ListOrdersQuery();

            const result = await handler.execute(query);

            expect(result.orders.length).toBe(0);
            expect(result.total).toBe(0);
            expect(orderRepository.findByCustomerId).not.toHaveBeenCalled();
        });
    });
});


================================================
FILE: apps/api/src/modules/order/application/queries/list-orders/list-orders.handler.ts
================================================
import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { Inject } from '@nestjs/common';
import { ListOrdersQuery } from './list-orders.query';
import { OrderListResponseDto } from './order-list.response.dto';
import { IOrderRepository, ORDER_REPOSITORY } from '../../../domain/repositories/order.repository.interface';

@QueryHandler(ListOrdersQuery)
export class ListOrdersHandler implements IQueryHandler<ListOrdersQuery> {
    constructor(
        @Inject(ORDER_REPOSITORY)
        private readonly orderRepository: IOrderRepository,
    ) {}

    async execute(query: ListOrdersQuery): Promise<OrderListResponseDto> {
        const orders = query.customerId ? await this.orderRepository.findByCustomerId(query.customerId) : [];

        return {
            orders: orders.map(order => ({
                id: order.id,
                customerId: order.customerId,
                items: order.items.map(item => ({
                    id: item.id,
                    productId: item.productId,
                    quantity: item.quantity.value,
                    unitPrice: item.unitPrice.amount,
                    totalPrice: item.getTotalPrice().amount,
                })),
                status: order.status.value,
                totalAmount: order.getTotalAmount().amount,
                createdAt: order.createdAt,
                updatedAt: order.updatedAt,
            })),
            total: orders.length,
        };
    }
}


================================================
FILE: apps/api/src/modules/order/application/queries/list-orders/list-orders.query.ts
================================================
export class ListOrdersQuery {
    constructor(public readonly customerId?: string) {}
}


================================================
FILE: apps/api/src/modules/order/application/queries/list-orders/order-list.response.dto.ts
================================================
import { ApiProperty } from '@nestjs/swagger';
import { OrderResponseDto } from '../get-order/order.response.dto';

export class OrderListResponseDto {
    @ApiProperty({ type: [OrderResponseDto] })
    orders: OrderResponseDto[];

    @ApiProperty()
    total: number;
}


================================================
FILE: apps/api/src/modules/order/domain/entities/order-item.entity.spec.ts
================================================
import { OrderItem } from './order-item.entity';
import { Money } from '../value-objects/money.vo';
import { Quantity } from '../value-objects/quantity.vo';

describe('OrderItem Entity', () => {
    describe('create', () => {
        it('should create order item with valid properties', () => {
            const orderItem = OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10.99),
            });

            expect(orderItem.id).toBe('item-1');
            expect(orderItem.productId).toBe('product-1');
            expect(orderItem.quantity.value).toBe(2);
            expect(orderItem.unitPrice.amount).toBe(10.99);
        });
    });

    describe('getTotalPrice', () => {
        it('should calculate total price correctly', () => {
            const orderItem = OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(3),
                unitPrice: Money.create(10),
            });

            const totalPrice = orderItem.getTotalPrice();

            expect(totalPrice.amount).toBe(30);
        });

        it('should calculate total price for single item', () => {
            const orderItem = OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(1),
                unitPrice: Money.create(15.99),
            });

            const totalPrice = orderItem.getTotalPrice();

            expect(totalPrice.amount).toBe(15.99);
        });

        it('should calculate total price with decimal values', () => {
            const orderItem = OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10.99),
            });

            const totalPrice = orderItem.getTotalPrice();

            expect(totalPrice.amount).toBe(21.98);
        });
    });

    describe('updateQuantity', () => {
        it('should update quantity', () => {
            const orderItem = OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10),
            });

            orderItem.updateQuantity(Quantity.create(5));

            expect(orderItem.quantity.value).toBe(5);
        });

        it('should affect total price after quantity update', () => {
            const orderItem = OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10),
            });

            orderItem.updateQuantity(Quantity.create(3));

            expect(orderItem.getTotalPrice().amount).toBe(30);
        });
    });

    describe('equals', () => {
        it('should return true for same id', () => {
            const item1 = OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10),
            });

            const item2 = OrderItem.create({
                id: 'item-1',
                productId: 'product-2',
                quantity: Quantity.create(3),
                unitPrice: Money.create(20),
            });

            expect(item1.equals(item2)).toBe(true);
        });

        it('should return false for different ids', () => {
            const item1 = OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10),
            });

            const item2 = OrderItem.create({
                id: 'item-2',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10),
            });

            expect(item1.equals(item2)).toBe(false);
        });
    });
});


================================================
FILE: apps/api/src/modules/order/domain/entities/order-item.entity.ts
================================================
import { Entity } from '@/shared/domain/entity';
import { Money } from '../value-objects/money.vo';
import { Quantity } from '../value-objects/quantity.vo';

export interface OrderItemProps {
    id: string;
    productId: string;
    quantity: Quantity;
    unitPrice: Money;
}

export class OrderItem extends Entity<string> {
    private _productId: string;
    private _quantity: Quantity;
    private _unitPrice: Money;

    get productId(): string {
        return this._productId;
    }

    get quantity(): Quantity {
        return this._quantity;
    }

    get unitPrice(): Money {
        return this._unitPrice;
    }

    private constructor(props: OrderItemProps) {
        super(props.id);
        this._productId = props.productId;
        this._quantity = props.quantity;
        this._unitPrice = props.unitPrice;
    }

    public static create(props: OrderItemProps): OrderItem {
        return new OrderItem(props);
    }

    public getTotalPrice(): Money {
        return this._unitPrice.multiply(this._quantity.value);
    }

    public updateQuantity(quantity: Quantity): void {
        this._quantity = quantity;
    }
}


================================================
FILE: apps/api/src/modules/order/domain/entities/order.entity.spec.ts
================================================
import { Order } from './order.entity';
import { OrderItem } from './order-item.entity';
import { Money } from '../value-objects/money.vo';
import { Quantity } from '../value-objects/quantity.vo';
import { OrderId } from '../value-objects/order-id.vo';
import { OrderStatus } from '../value-objects/order-status.vo';
import { OrderCreatedEvent } from '../events/order-created.event';
import { OrderConfirmedEvent } from '../events/order-confirmed.event';
import { OrderCancelledEvent } from '../events/order-cancelled.event';
import { InvalidOrderStateException } from '../exceptions/invalid-order-state.exception';

describe('Order Aggregate Root', () => {
    let orderItems: OrderItem[];

    beforeEach(() => {
        orderItems = [
            OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10),
            }),
            OrderItem.create({
                id: 'item-2',
                productId: 'product-2',
                quantity: Quantity.create(1),
                unitPrice: Money.create(20),
            }),
        ];
    });

    describe('create', () => {
        it('should create order with valid properties', () => {
            const order = Order.create('customer-1', orderItems);

            expect(order.id).toBeDefined();
            expect(order.customerId).toBe('customer-1');
            expect(order.items.length).toBe(2);
            expect(order.status.isPending()).toBe(true);
            expect(order.createdAt).toBeInstanceOf(Date);
            expect(order.updatedAt).toBeInstanceOf(Date);
        });

        it('should create order with provided id', () => {
            const orderId = OrderId.create('custom-order-id');
            const order = Order.create('customer-1', orderItems, orderId);

            expect(order.id).toBe('custom-order-id');
        });

        it('should raise OrderCreatedEvent', () => {
            const order = Order.create('customer-1', orderItems);

            expect(order.domainEvents.length).toBe(1);
            expect(order.domainEvents[0]).toBeInstanceOf(OrderCreatedEvent);
            expect((order.domainEvents[0] as OrderCreatedEvent).orderId).toBe(order.id);
            expect((order.domainEvents[0] as OrderCreatedEvent).customerId).toBe('customer-1');
        });

        it('should create order with empty items', () => {
            const order = Order.create('customer-1', []);

            expect(order.items.length).toBe(0);
        });
    });

    describe('getTotalAmount', () => {
        it('should calculate total amount correctly', () => {
            const order = Order.create('customer-1', orderItems);

            const total = order.getTotalAmount();

            expect(total.amount).toBe(40); // (2 * 10) + (1 * 20)
        });

        it('should return zero for empty order', () => {
            const order = Order.create('customer-1', []);

            const total = order.getTotalAmount();

            expect(total.amount).toBe(0);
        });

        it('should calculate total for single item', () => {
            const singleItem = [
                OrderItem.create({
                    id: 'item-1',
                    productId: 'product-1',
                    quantity: Quantity.create(3),
                    unitPrice: Money.create(15),
                }),
            ];

            const order = Order.create('customer-1', singleItem);

            expect(order.getTotalAmount().amount).toBe(45);
        });
    });

    describe('confirm', () => {
        it('should confirm pending order', () => {
            const order = Order.create('customer-1', orderItems);

            order.confirm();

            expect(order.status.isConfirmed()).toBe(true);
        });

        it('should raise OrderConfirmedEvent', () => {
            const order = Order.create('customer-1', orderItems);
            order.clearEvents(); // Clear creation event

            order.confirm();

            expect(order.domainEvents.length).toBe(1);
            expect(order.domainEvents[0]).toBeInstanceOf(OrderConfirmedEvent);
            expect((order.domainEvents[0] as OrderConfirmedEvent).orderId).toBe(order.id);
        });

        it('should update updatedAt timestamp', () => {
            const order = Order.create('customer-1', orderItems);
            const originalUpdatedAt = order.updatedAt;

            // Wait a bit to ensure timestamp difference
            setTimeout(() => {
                order.confirm();
                expect(order.updatedAt.getTime()).toBeGreaterThanOrEqual(originalUpdatedAt.getTime());
            }, 10);
        });

        it('should throw error when confirming non-pending order', () => {
            const order = Order.create('customer-1', orderItems);
            order.confirm();

            expect(() => order.confirm()).toThrow(InvalidOrderStateException);
            expect(() => order.confirm()).toThrow('Cannot confirm order');
        });

        it('should throw error when confirming cancelled order', () => {
            const order = Order.create('customer-1', orderItems);
            order.cancel();

            expect(() => order.confirm()).toThrow(InvalidOrderStateException);
        });
    });

    describe('cancel', () => {
        it('should cancel pending order', () => {
            const order = Order.create('customer-1', orderItems);

            order.cancel();

            expect(order.status.isCancelled()).toBe(true);
        });

        it('should cancel confirmed order', () => {
            const order = Order.create('customer-1', orderItems);
            order.confirm();

            order.cancel();

            expect(order.status.isCancelled()).toBe(true);
        });

        it('should raise OrderCancelledEvent', () => {
            const order = Order.create('customer-1', orderItems);
            order.clearEvents();

            order.cancel();

            expect(order.domainEvents.length).toBe(1);
            expect(order.domainEvents[0]).toBeInstanceOf(OrderCancelledEvent);
        });

        it('should throw error when cancelling already cancelled order', () => {
            const order = Order.create('customer-1', orderItems);
            order.cancel();

            expect(() => order.cancel()).toThrow(InvalidOrderStateException);
            expect(() => order.cancel()).toThrow('Cannot cancel order');
        });
    });

    describe('addItem', () => {
        it('should add item to pending order', () => {
            const order = Order.create('customer-1', orderItems);
            const newItem = OrderItem.create({
                id: 'item-3',
                productId: 'product-3',
                quantity: Quantity.create(1),
                unitPrice: Money.create(30),
            });

            order.addItem(newItem);

            expect(order.items.length).toBe(3);
            expect(order.items[2].id).toBe('item-3');
        });

        it('should update total amount after adding item', () => {
            const order = Order.create('customer-1', orderItems);
            const newItem = OrderItem.create({
                id: 'item-3',
                productId: 'product-3',
                quantity: Quantity.create(1),
                unitPrice: Money.create(30),
            });

            order.addItem(newItem);

            expect(order.getTotalAmount().amount).toBe(70); // 40 + 30
        });

        it('should throw error when adding item to non-pending order', () => {
            const order = Order.create('customer-1', orderItems);
            order.confirm();

            const newItem = OrderItem.create({
                id: 'item-3',
                productId: 'product-3',
                quantity: Quantity.create(1),
                unitPrice: Money.create(30),
            });

            expect(() => order.addItem(newItem)).toThrow(InvalidOrderStateException);
            expect(() => order.addItem(newItem)).toThrow('Cannot add items to a non-pending order');
        });
    });

    describe('removeItem', () => {
        it('should remove item from pending order', () => {
            const order = Order.create('customer-1', orderItems);

            order.removeItem('item-1');

            expect(order.items.length).toBe(1);
            expect(order.items[0].id).toBe('item-2');
        });

        it('should update total amount after removing item', () => {
            const order = Order.create('customer-1', orderItems);

            order.removeItem('item-1'); // Remove item worth 20

            expect(order.getTotalAmount().amount).toBe(20);
        });

        it('should throw error when removing item from non-pending order', () => {
            const order = Order.create('customer-1', orderItems);
            order.confirm();

            expect(() => order.removeItem('item-1')).toThrow(InvalidOrderStateException);
            expect(() => order.removeItem('item-1')).toThrow('Cannot remove items from a non-pending order');
        });

        it('should handle removing non-existent item', () => {
            const order = Order.create('customer-1', orderItems);

            order.removeItem('non-existent-id');

            expect(order.items.length).toBe(2); // No change
        });
    });

    describe('clearEvents', () => {
        it('should clear all domain events', () => {
            const order = Order.create('customer-1', orderItems);

            expect(order.domainEvents.length).toBeGreaterThan(0);

            order.clearEvents();

            expect(order.domainEvents.length).toBe(0);
        });
    });

    describe('reconstitute', () => {
        it('should reconstitute order from props without raising events', () => {
            const orderId = OrderId.create('existing-order-id');
            const order = Order.reconstitute({
                id: orderId,
                customerId: 'customer-1',
                items: orderItems,
                status: OrderStatus.confirmed(),
                createdAt: new Date('2024-01-01'),
                updatedAt: new Date('2024-01-02'),
            });

            expect(order.id).toBe('existing-order-id');
            expect(order.status.isConfirmed()).toBe(true);
            expect(order.domainEvents.length).toBe(0); // No events raised
        });
    });

    describe('items immutability', () => {
        it('should return copy of items array', () => {
            const order = Order.create('customer-1', orderItems);

            const items1 = order.items;
            const items2 = order.items;

            expect(items1).not.toBe(items2); // Different array instances
            expect(items1).toEqual(items2); // Same content
        });

        it('should not allow external modification of items', () => {
            const order = Order.create('customer-1', orderItems);

            const items = order.items;
            items.push(
                OrderItem.create({
                    id: 'item-3',
                    productId: 'product-3',
                    quantity: Quantity.create(1),
                    unitPrice: Money.create(30),
                }),
            );

            expect(order.items.length).toBe(2); // Original unchanged
        });
    });
});


================================================
FILE: apps/api/src/modules/order/domain/entities/order.entity.ts
================================================
import { AggregateRoot } from '@/shared/domain/aggregate-root';
import { OrderId } from '../value-objects/order-id.vo';
import { OrderStatus } from '../value-objects/order-status.vo';
import { Money } from '../value-objects/money.vo';
import { OrderItem } from './order-item.entity';
import { OrderCreatedEvent } from '../events/order-created.event';
import { OrderConfirmedEvent } from '../events/order-confirmed.event';
import { OrderCancelledEvent } from '../events/order-cancelled.event';
import { InvalidOrderStateException } from '../exceptions/invalid-order-state.exception';

export interface OrderProps {
    id: OrderId;
    customerId: string;
    items: OrderItem[];
    status: OrderStatus;
    createdAt: Date;
    updatedAt: Date;
}

export class Order extends AggregateRoot<string> {
    private _customerId: string;
    private _items: OrderItem[];
    private _status: OrderStatus;
    private _createdAt: Date;
    private _updatedAt: Date;

    get customerId(): string {
        return this._customerId;
    }

    get items(): OrderItem[] {
        return [...this._items];
    }

    get status(): OrderStatus {
        return this._status;
    }

    get createdAt(): Date {
        return this._createdAt;
    }

    get updatedAt(): Date {
        return this._updatedAt;
    }

    private constructor(props: OrderProps) {
        super(props.id.value);
        this._customerId = props.customerId;
        this._items = props.items;
        this._status = props.status;
        this._createdAt = props.createdAt;
        this._updatedAt = props.updatedAt;
    }

    public static create(customerId: string, items: OrderItem[], id?: OrderId): Order {
        const orderId = id || OrderId.create();
        const now = new Date();

        const order = new Order({
            id: orderId,
            customerId,
            items,
            status: OrderStatus.pending(),
            createdAt: now,
            updatedAt: now,
        });

        order.addDomainEvent(new OrderCreatedEvent(orderId.value, customerId, order.getTotalAmount()));

        return order;
    }

    public static reconstitute(props: OrderProps): Order {
        return new Order(props);
    }

    public getTotalAmount(): Money {
        if (this._items.length === 0) {
            return Money.create(0);
        }

        return this._items.reduce((total, item) => total.add(item.getTotalPrice()), Money.create(0));
    }

    public confirm(): void {
        if (!this._status.isPending()) {
            throw new InvalidOrderStateException(`Cannot confirm order. Current status: ${this._status.value}`);
        }

        this._status = OrderStatus.confirmed();
        this._updatedAt = new Date();

        this.addDomainEvent(new OrderConfirmedEvent(this.id));
    }

    public cancel(): void {
        if (this._status.isCancelled() || this._status.isCompleted()) {
            throw new InvalidOrderStateException(`Cannot cancel order. Current status: ${this._status.value}`);
        }

        this._status = OrderStatus.cancelled();
        this._updatedAt = new Date();

        this.addDomainEvent(new OrderCancelledEvent(this.id));
    }

    public addItem(item: OrderItem): void {
        if (!this._status.isPending()) {
            throw new InvalidOrderStateException('Cannot add items to a non-pending order');
        }

        this._items.push(item);
        this._updatedAt = new Date();
    }

    public removeItem(itemId: string): void {
        if (!this._status.isPending()) {
            throw new InvalidOrderStateException('Cannot remove items from a non-pending order');
        }

        this._items = this._items.filter(item => item.id !== itemId);
        this._updatedAt = new Date();
    }
}


================================================
FILE: apps/api/src/modules/order/domain/events/order-cancelled.event.ts
================================================
import { DomainEvent } from '@/shared/domain/domain-event';

export class OrderCancelledEvent extends DomainEvent {
    constructor(public readonly orderId: string) {
        super();
    }

    getAggregateId(): string {
        return this.orderId;
    }
}


================================================
FILE: apps/api/src/modules/order/domain/events/order-confirmed.event.ts
================================================
import { DomainEvent } from '@/shared/domain/domain-event';

export class OrderConfirmedEvent extends DomainEvent {
    constructor(public readonly orderId: string) {
        super();
    }

    getAggregateId(): string {
        return this.orderId;
    }
}


================================================
FILE: apps/api/src/modules/order/domain/events/order-created.event.ts
================================================
import { DomainEvent } from '@/shared/domain/domain-event';
import { Money } from '../value-objects/money.vo';

export class OrderCreatedEvent extends DomainEvent {
    constructor(
        public readonly orderId: string,
        public readonly customerId: string,
        public readonly totalAmount: Money,
    ) {
        super();
    }

    getAggregateId(): string {
        return this.orderId;
    }
}


================================================
FILE: apps/api/src/modules/order/domain/exceptions/invalid-order-state.exception.ts
================================================
import { DomainException } from '@/shared/presentation/filters/domain-exception.filter';

export class InvalidOrderStateException extends DomainException {
    constructor(message: string) {
        super(message);
    }
}


================================================
FILE: apps/api/src/modules/order/domain/exceptions/order-not-found.exception.ts
================================================
import { DomainException } from '@/shared/presentation/filters/domain-exception.filter';

export class OrderNotFoundException extends DomainException {
    constructor(orderId: string) {
        super(`Order with id ${orderId} not found`);
    }
}


================================================
FILE: apps/api/src/modules/order/domain/repositories/order.repository.interface.ts
================================================
import { Order } from '../entities/order.entity';

export interface IOrderRepository {
    findById(id: string): Promise<Order | null>;
    findByCustomerId(customerId: string): Promise<Order[]>;
    save(order: Order): Promise<Order>;
    delete(id: string): Promise<void>;
}

export const ORDER_REPOSITORY = Symbol('ORDER_REPOSITORY');


================================================
FILE: apps/api/src/modules/order/domain/services/order-pricing.service.spec.ts
================================================
import { OrderPricingService } from './order-pricing.service';
import { Order } from '../entities/order.entity';
import { OrderItem } from '../entities/order-item.entity';
import { Money } from '../value-objects/money.vo';
import { Quantity } from '../value-objects/quantity.vo';

describe('OrderPricingService', () => {
    let service: OrderPricingService;
    let order: Order;

    beforeEach(() => {
        service = new OrderPricingService();

        const items = [
            OrderItem.create({
                id: 'item-1',
                productId: 'product-1',
                quantity: Quantity.create(2),
                unitPrice: Money.create(10),
            }),
            OrderItem.create({
                id: 'item-2',
                productId: 'product-2',
                quantity: Quantity.create(1),
                unitPrice: Money.create(20),
            }),
        ];

        order = Order.create('customer-1', items);
    });

    describe('calculateTotal', () => {
        it('should calculate total amount for order', () => {
            const total = service.calculateTotal(order);

            expect(total.amount).toBe(40); // (2 * 10) + (1 * 20)
        });

        it('should return zero for empty order', () => {
            const emptyOrder = Order.create('customer-1', []);

            const total = service.calculateTotal(emptyOrder);

            expect(total.amount).toBe(0);
        });
    });

    describe('applyDiscount', () => {
        it('should apply discount percentage correctly', () => {
            const total = Money.create(100);

            const discounted = service.applyDiscount(total, 10);

            expect(discounted.amount).toBe(90);
        });

        it('should apply 50% discount', () => {
            const total = Money.create(100);

            const discounted = service.applyDiscount(total, 50);

            expect(discounted.amount).toBe(50);
        });

        it('should apply 100% discount', () => {
            const total = Money.create(100);

            const discounted = service.applyDiscount(total, 100);

            expect(discounted.amount).toBe(0);
        });

        it('should apply 0% discount', () => {
            const total = Money.create(100);

            const discounted = service.applyDiscount(total, 0);

            expect(discounted.amount).toBe(100);
        });

        it('should throw error for negative discount', () => {
            const total = Money.create(100);

            expect(() => service.applyDiscount(total, -10)).toThrow('Discount percent must be between 0 and 100');
        });

        it('should throw error for discount over 100%', () => {
            const total = Money.create(100);

            expect(() => service.applyDiscount(total, 101)).toThrow('Discount percent must be between 0 and 100');
        });

        it('should preserve currency after discount', () => {
            const total = Money.create(100, 'EUR');

            const discounted = service.applyDiscount(total, 10);

            expect(discounted.currency).toBe('EUR');
        });

        it('should handle decimal discount percentages', () => {
            const total = Money.create(100);

            const discounted = service.applyDiscount(total, 12.5);

            expect(discounted.amount).toBe(87.5);
        });
    });
});


================================================
FILE: apps/api/src/modules/order/domain/services/order-pricing.service.ts
================================================
import { Injectable } from '@nestjs/common';
import { Order } from '../entities/order.entity';
import { Money } from '../value-objects/money.vo';

@Injectable()
export class OrderPricingService {
    calculateTotal(order: Order): Money {
        return order.getTotalAmount();
    }

    applyDiscount(total: Money, discountPercent: number): Money {
        if (discountPercent < 0 || discountPercent > 100) {
            throw new Error('Discount percent must be between 0 and 100');
        }

        const discountMultiplier = 1 - discountPercent / 100;
        return Money.create(total.amount * discountMultiplier, total.currency);
    }
}


================================================
FILE: apps/api/src/modules/order/domain/value-objects/money.vo.spec.ts
================================================
import { Money } from './money.vo';

describe('Money Value Object', () => {
    describe('create', () => {
        it('should create money with valid amount', () => {
            const money = Money.create(10.99, 'USD');

            expect(money.amount).toBe(10.99);
            expect(money.currency).toBe('USD');
        });

        it('should create money with default currency', () => {
            const money = Money.create(10.99);

            expect(money.amount).toBe(10.99);
            expect(money.currency).toBe('USD');
        });

        it('should throw error for negative amount', () => {
            expect(() => Money.create(-10)).toThrow('Money amount cannot be negative');
        });

        it('should allow zero amount', () => {
            const money = Money.create(0);

            expect(money.amount).toBe(0);
        });
    });

    describe('add', () => {
        it('should add money with same currency', () => {
            const money1 = Money.create(10, 'USD');
            const money2 = Money.create(5, 'USD');

            const result = money1.add(money2);

            expect(result.amount).toBe(15);
            expect(result.currency).toBe('USD');
        });

        it('should throw error when adding different currencies', () => {
            const money1 = Money.create(10, 'USD');
            const money2 = Money.create(5, 'EUR');

            expect(() => money1.add(money2)).toThrow('Cannot add money with different currencies');
        });

        it('should not mutate original money objects', () => {
            const money1 = Money.create(10, 'USD');
            const money2 = Money.create(5, 'USD');

            money1.add(money2);

            expect(money1.amount).toBe(10);
            expect(money2.amount).toBe(5);
        });
    });

    describe('multiply', () => {
        it('should multiply money by positive number', () => {
            const money = Money.create(10, 'USD');

            const result = money.multiply(2);

            expect(result.amount).toBe(20);
            expect(result.currency).toBe('USD');
        });

        it('should multiply money by decimal', () => {
            const money = Money.create(10, 'USD');

            const result = money.multiply(1.5);

            expect(result.amount).toBe(15);
        });

        it('should multiply money by zero', () => {
            const money = Money.create(10, 'USD');

            const result = money.multiply(0);

            expect(result.amount).toBe(0);
        });

        it('should not mutate original money object', () => {
            const money = Money.create(10, 'USD');

            money.multiply(2);

            expect(money.amount).toBe(10);
        });
    });

    describe('equals', () => {
        it('should return true for equal money objects', () => {
            const money1 = Money.create(10, 'USD');
            const money2 = Money.create(10, 'USD');

            expect(money1.equals(money2)).toBe(true);
        });

        it('should return false for different amounts', () => {
            const money1 = Money.create(10, 'USD');
            const money2 = Money.create(20, 'USD');

            expect(money1.equals(money2)).toBe(false);
        });

        it('should return false for different currencies', () => {
            const money1 = Money.create(10, 'USD');
            const money2 = Money.create(10, 'EUR');

            expect(money1.equals(money2)).toBe(false);
        });

        it('should return false for null', () => {
            const money = Money.create(10, 'USD');

            expect(money.equals(null as any)).toBe(false);
        });

        it('should return false for undefined', () => {
            const money = Money.create(10, 'USD');

            expect(money.equals(undefined as any)).toBe(false);
        });
    });
});


================================================
FILE: apps/api/src/modules/order/domain/value-objects/money.vo.ts
================================================
import { ValueObject } from '@/shared/domain/value-object';
import { Guard } from '@/shared/utils/guard';

interface MoneyProps {
    amount: number;
    currency: string;
}

export class Money extends ValueObject<MoneyProps> {
    get amount(): number {
        return this.props.amount;
    }

    get currency(): string {
        return this.props.currency;
    }

    private constructor(props: MoneyProps) {
        super(props);
    }

    public static create(amount: number, currency: string = 'USD'): Money {
        const guardResult = Guard.againstNullOrUndefined(amount, 'amount');
        if (!guardResult.succeeded) {
            throw new Error(guardResult.message);
        }

        if (amount < 0) {
            throw new Error('Money amount cannot be negative');
        }

        return new Money({ amount, currency });
    }

    public add(money: Money): Money {
        if (this.currency !== money.currency) {
            throw new Error('Cannot add money with different currencies');
        }
        return Money.create(this.amount + money.amount, this.currency);
    }

    public multiply(multiplier: number): Money {
        return Money.create(this.amount * multiplier, this.currency);
    }
}


================================================
FILE: apps/api/src/modules/order/domain/value-objects/order-id.vo.spec.ts
================================================
import { OrderId } from './order-id.vo';

describe('OrderId Value Object', () => {
    describe('create', () => {
        it('should create order id with provided value', () => {
            const id = 'test-order-id';
            const orderId = OrderId.create(id);

            expect(orderId.value).toBe(id);
        });

        it('should generate UUID when no value provided', () => {
            const orderId = OrderId.create();

            expect(orderId.value).toBeDefined();
            expect(orderId.value.length).toBeGreaterThan(0);
            expect(orderId.value).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
        });

        it('should generate different UUIDs for each call', () => {
            const orderId1 = OrderId.create();
            const orderId2 = OrderId.create();

            expect(orderId1.value).not.toBe(orderId2.value);
        });
    });

    describe('toString', () => {
        it('should return string representation', () => {
            const id = 'test-order-id';
            const orderId = OrderId.create(id);

            expect(orderId.toString()).toBe(id);
        });
    });

    describe('equals', () => {
        it('should return true for equal order ids', () => {
            const id = 'test-order-id';
            const orderId1 = OrderId.create(id);
            const orderId2 = OrderId.create(id);

            expect(orderId1.equals(orderId2)).toBe(true);
        });

        it('should return false for different order ids', () => {
            const orderId1 = OrderId.create('id-1');
            const orderId2 = OrderId.create('id-2');

            expect(orderId1.equals(orderId2)).toBe(false);
        });
    });
});


================================================
FILE: apps/api/src/modules/order/domain/value-objects/order-id.vo.ts
================================================
import { ValueObject } from '@/shared/domain/value-object';
import { v4 as uuidv4 } from 'uuid';

interface OrderIdProps {
    value: string;
}

export class OrderId extends ValueObject<OrderIdProps> {
    get value(): string {
        return this.props.value;
    }

    private constructor(props: OrderIdProps) {
        super(props);
    }

    public static create(id?: string): OrderId {
        return new OrderId({ value: id || uuidv4() });
    }

    public toString(): string {
        return this.value;
    }
}


================================================
FILE: apps/api/src/modules/order/domain/value-objects/order-status.vo.spec.ts
================================================
import { OrderStatus, OrderStatusEnum } from './order-status.vo';

describe('OrderStatus Value Object', () => {
    describe('create', () => {
        it('should create order status with valid enum value', () => {
            const status = OrderStatus.create(OrderStatusEnum.PENDING);

            expect(status.value).toBe(OrderStatusEnum.PENDING);
        });
    });

    describe('factory methods', () => {
        it('should create pending status', () => {
            const status = OrderStatus.pending();

            expect(status.value).toBe(OrderStatusEnum.PENDING);
            expect(status.isPending()).toBe(true);
        });

        it('should create confirmed status', () => {
            const status = OrderStatus.confirmed();

            expect(status.value).toBe(OrderStatusEnum.CONFIRMED);
            expect(status.isConfirmed()).toBe(true);
        });

        it('should create cancelled status', () => {
            const status = OrderStatus.cancelled();

            expect(status.value).toBe(OrderStatusEnum.CANCELLED);
            expect(status.isCancelled()).toBe(true);
        });

        it('should create completed status', () => {
            const status = OrderStatus.completed();

            expect(status.value).toBe(OrderStatusEnum.COMPLETED);
            expect(status.isCompleted()).toBe(true);
        });
    });

    describe('status checks', () => {
        it('isPending should return false for non-pending status', () => {
            const status = OrderStatus.confirmed();

            expect(status.isPending()).toBe(false);
        });

        it('isConfirmed should return false for non-confirmed status', () => {
            const status = OrderStatus.pending();

            expect(status.isConfirmed()).toBe(false);
        });

        it('isCancelled should return false for non-cancelled status', () => {
            const status = OrderStatus.pending();

            expect(status.isCancelled()).toBe(false);
        });

        it('isCompleted should return false for non-completed status', () => {
            const status = OrderStatus.pending();

            expect(status.isCompleted()).toBe(false);
        });
    });

    describe('equals', () => {
        it('should return true for equal statuses', () => {
            const status1 = OrderStatus.pending();
            const status2 = OrderStatus.pending();

            expect(status1.equals(status2)).toBe(true);
        });

        it('should return false for different statuses', () => {
            const status1 = OrderStatus.pending();
            const status2 = OrderStatus.confirmed();

            expect(status1.equals(status2)).toBe(false);
        });
    });
});


================================================
FILE: apps/api/src/modules/order/domain/value-objects/order-status.vo.ts
================================================
import { ValueObject } from '@/shared/domain/value-object';

export enum OrderStatusEnum {
    PENDING = 'PENDING',
    CONFIRMED = 'CONFIRMED',
    CANCELLED = 'CANCELLED',
    COMPLETED = 'COMPLETED',
}

interface OrderStatusProps {
    value: OrderStatusEnum;
}

export class OrderStatus extends ValueObject<OrderStatusProps> {
    get value(): OrderStatusEnum {
        return this.props.value;
    }

    private constructor(props: OrderStatusProps) {
        super(props);
    }

    public static create(status: OrderStatusEnum): OrderStatus {
        return new OrderStatus({ value: status });
    }

    public static pending(): OrderStatus {
        return new OrderStatus({ value: OrderStatusEnum.PENDING });
    }

    public static confirmed(): OrderStatus {
        return new OrderStatus({ value: OrderStatusEnum.CONFIRMED });
    }

    public static cancelled(): OrderStatus {
        return new OrderStatus({ value: OrderStatusEnum.CANCELLED });
    }

    public static completed(): OrderStatus {
        return new OrderStatus({ value: OrderStatusEnum.COMPLETED });
    }

    public isPending(): boolean {
        return this.value === OrderStatusEnum.PENDING;
    }

    public isConfirmed(): boolean {
        return this.value === OrderStatusEnum.CONFIRMED;
    }

    public isCancelled(): boolean {
        return this.value === OrderStatusEnum.CANCELLED;
    }

    public isCompleted(): boolean {
        return this.value === OrderStatusEnum.COMPLETED;
    }
}


================================================
FILE: apps/api/src/modules/order/domain/value-objects/quantity.vo.spec.ts
================================================
import { Quantity } from './quantity.vo';

describe('Quantity Value Object', () => {
    describe('create', () => {
        it('should create quantity with valid value', () => {
            const quantity = Quantity.create(5);

            expect(quantity.value).toBe(5);
        });

        it('should throw error for zero quantity', () => {
            expect(() => Quantity.create(0)).toThrow('Quantity must be at least 1');
        });

        it('should throw error for negative quantity', () => {
            expect(() => Quantity.create(-5)).toThrow('Quantity must be at least 1');
        });

        it('should throw error for non-integer quantity', () => {
            expect(() => Quantity.create(5.5)).toThrow('Quantity must be an integer');
        });

        it('should allow large quantities', () => {
            const quantity = Quantity.create(1000);

            expect(quantity.value).toBe(1000);
        });
    });

    describe('equals', () => {
        it('should return true for equal quantities', () => {
            const quantity1 = Quantity.create(5);
            const quantity2 = Quantity.create(5);

            expect(quantity1.equals(quantity2)).toBe(true);
        });

        it('should return false for different quantities', () => {
            const quantity1 = Quantity.create(5);
            const quantity2 = Quantity.create(10);

            expect(quantity1.equals(quantity2)).toBe(false);
        });

        it('should return false for null', () => {
            const quantity = Quantity.create(5);

            expect(quantity.equals(null as any)).toBe(false);
        });
    });
});


================================================
FILE: apps/api/src/modules/order/domain/value-objects/quantity.vo.ts
================================================
import { ValueObject } from '@/shared/domain/value-object';

interface QuantityProps {
    value: number;
}

export class Quantity extends ValueObject<QuantityProps> {
    get value(): number {
        return this.props.value;
    }

    private constructor(props: QuantityProps) {
        super(props);
    }

    public static create(value: number): Quantity {
        if (value < 1) {
            throw new Error('Quantity must be at least 1');
        }

        if (!Number.isInteger(value)) {
            throw new Error('Quantity must be an integer');
        }

        return new Quantity({ value });
    }
}


================================================
FILE: apps/api/src/modules/order/infrastructure/cache/order-cache.service.ts
================================================
import { Injectable, Logger } from '@nestjs/common';
import { RedissonService } from '@a3s-lab/redisson';
import { Order } from '../../domain/entities/order.entity';

const ORDER_CACHE_PREFIX = 'order:';
const ORDER_LIST_CACHE_PREFIX = 'orders:customer:';
const DEFAULT_TTL = 3600; // 1 hour

@Injectable()
export class OrderCacheService {
    private readonly logger = new Logger(OrderCacheService.name);

    constructor(private readonly redisson: RedissonService) {}

    /**
     * Cache an order by ID
     */
    async cacheOrder(order: Order, ttl: number = DEFAULT_TTL): Promise<void> {
        const key = `${ORDER_CACHE_PREFIX}${order.id}`;
        const data = this.serializeOrder(order);
        await this.redisson.setJSON(key, data, ttl);
        this.logger.debug(`Cached order: ${order.id}`);
    }

    /**
     * Get cached order by ID
     */
    async getCachedOrder(orderId: string): Promise<SerializedOrder | null> {
        const key = `${ORDER_CACHE_PREFIX}${orderId}`;
        return this.redisson.getJSON<SerializedOrder>(key);
    }

    /**
     * Invalidate order cache
     */
    async invalidateOrder(orderId: string): Promise<void> {
        const key = `${ORDER_CACHE_PREFIX}${orderId}`;
        await this.redisson.delete(key);
        this.logger.debug(`Invalidated order cache: ${orderId}`);
    }

    /**
     * Cache order list for a customer
     */
    async cacheCustomerOrders(
        customerId: string,
        orders: Order[],
        ttl: number = DEFAULT_TTL,
    ): Promise<void> {
        const key = `${ORDER_LIST_CACHE_PREFIX}${customerId}`;
        const data = orders.map((order) => this.serializeOrder(order));
        await this.redisson.setJSON(key, data, ttl);
        this.logger.debug(`Cached ${orders.length} orders for customer: ${customerId}`);
    }

    /**
     * Get cached orders for a customer
     */
    async getCachedCustomerOrders(customerId: string): Promise<SerializedOrder[] | null> {
        const key = `${ORDER_LIST_CACHE_PREFIX}${customerId}`;
        return this.redisson.getJSON<SerializedOrder[]>(key);
    }

    /**
     * Invalidate customer orders cache
     */
    async invalidateCustomerOrders(customerId: string): Promise<void> {
        const key = `${ORDER_LIST_CACHE_PREFIX}${customerId}`;
        await this.redisson.delete(key);
        this.logger.debug(`Invalidated customer orders cache: ${customerId}`);
    }

    /**
     * Get or set order with cache
     */
    async getOrSetOrder(
        orderId: string,
        factory: () => Promise<Order | null>,
        ttl: number = DEFAULT_TTL,
    ): Promise<SerializedOrder | null> {
        const key = `${ORDER_CACHE_PREFIX}${orderId}`;

        return this.redisson.getOrSet<SerializedOrder | null>(
            key,
            async () => {
                const order = await factory();
                return order ? this.serializeOrder(order) : null;
            },
            ttl,
        );
    }

    /**
     * Execute operation with distributed lock
     * Prevents race conditions when updating orders
     */
    async withOrderLock<T>(
        orderId: string,
        operation: () => Promise<T>,
        waitTime: number = 5000,
        leaseTime: number = 10000,
    ): Promise<T> {
        const lockKey = `lock:order:${orderId}`;
        return this.redisson.withLock(lockKey, operation, waitTime, leaseTime);
    }

    /**
     * Increment order view count (for analytics)
     */
    async incrementOrderViewCount(orderId: string): Promise<number> {
        const key = `order:views:${orderId}`;
        return this.redisson.increment(key);
    }

    /**
     * Get order view count
     */
    async getOrderViewCount(orderId: string): Promise<number> {
        const key = `order:views:${orderId}`;
        const count = await this.redisson.get(key);
        return count ? parseInt(count, 10) : 0;
    }

    private serializeOrder(order: Order): SerializedOrder {
        return {
            id: order.id,
            customerId: order.customerId,
            status: order.status.value,
            totalAmount: order.getTotalAmount().amount,
            items: order.items.map((item) => ({
                id: item.id,
                productId: item.productId,
                quantity: item.quantity.value,
                unitPrice: item.unitPrice.amount,
                subtotal: item.getTotalPrice().amount,
            })),
            createdAt: order.createdAt.toISOString(),
            updatedAt: order.updatedAt.toISOString(),
        };
    }
}

export interface SerializedOrder {
    id: string;
    customerId: string;
    status: string;
    totalAmount: number;
    items: SerializedOrderItem[];
    createdAt: string;
    updatedAt: string;
}

export interface SerializedOrderItem {
    id: string;
    productId: string;
    quantity: number;
    unitPrice: number;
    subtotal: number;
}


================================================
FILE: apps/api/src/modules/order/infrastructure/persistence/kysely-order.repository.ts
================================================
import { Injectable } from '@nestjs/common';
import { KyselyService } from '@a3s-lab/kysely';
import { Database, NewOrder, NewOrderItem } from '@/shared/database/database.types';
import { IOrderRepository } from '../../domain/repositories/order.repository.interface';
import { Order } from '../../domain/entities/order.entity';
import { OrderItem } from '../../domain/entities/order-item.entity';
import { OrderId } from '../../domain/value-objects/order-id.vo';
import { OrderStatus, OrderStatusEnum } from '../../domain/value-objects/order-status.vo';
import { Money } from '../../domain/value-objects/money.vo';
import { Quantity } from '../../domain/value-objects/quantity.vo';

@Injectable()
export class OrderRepository implements IOrderRepository {
    constructor(private readonly db: KyselyService<Database>) {}

    async findById(id: string): Promise<Order | null> {
        const orderRow = await this.db
            .selectFrom('orders')
            .where('id', '=', id)
            .selectAll()
            .executeTakeFirst();

        if (!orderRow) {
            return null;
        }

        const itemRows = await this.db
            .selectFrom('order_items')
            .where('order_id', '=', id)
            .selectAll()
            .execute();

        return this.toDomain(orderRow, itemRows);
    }

    async findByCustomerId(customerId: string): Promise<Order[]> {
        const orderRows = await this.db
            .selectFrom('orders')
            .where('customer_id', '=', customerId)
            .selectAll()
            .execute();

        const orders: Order[] = [];

        for (const orderRow of orderRows) {
            const itemRows = await this.db
                .selectFrom('order_items')
                .where('order_id', '=', orderRow.id)
                .selectAll()
                .execute();

            orders.push(this.toDomain(orderRow, itemRows));
        }

        return orders;
    }

    async save(order: Order): Promise<Order> {
        return await this.db.transaction().execute(async (trx) => {
            // Check if order exists
            const existingOrder = await trx
                .selectFrom('orders')
                .where('id', '=', order.id)
                .select('id')
                .executeTakeFirst();

            const statusValue = this.mapStatusToDb(order.status.value);

            const orderData: NewOrder = {
                id: order.id,
                customer_id: order.customerId,
                status: statusValue,
                total_amount: order.getTotalAmount().amount,
                created_at: order.createdAt.toISOString(),
                updated_at: order.updatedAt.toISOString(),
            };

            if (existingOrder) {
                // Update existing order
                await trx
                    .updateTable('orders')
                    .set({
                        status: orderData.status,
                        total_amount: orderData.total_amount,
                        updated_at: orderData.updated_at,
                    })
                    .where('id', '=', order.id)
                    .execute();

                // Delete existing items
                await trx
                    .deleteFrom('order_items')
                    .where('order_id', '=', order.id)
                    .execute();
            } else {
                // Insert new order
                await trx
                    .insertInto('orders')
                    .values(orderData)
                    .execute();
            }

            // Insert order items
            if (order.items.length > 0) {
                const itemsData: NewOrderItem[] = order.items.map((item) => ({
                    id: item.id,
                    order_id: order.id,
                    product_id: item.productId,
                    quantity: item.quantity.value,
                    unit_price: item.unitPrice.amount,
                    subtotal: item.getTotalPrice().amount,
                    created_at: new Date().toISOString(),
                }));

                await trx
                    .insertInto('order_items')
                    .values(itemsData)
                    .execute();
            }

            return order;
        });
    }

    async delete(id: string): Promise<void> {
        await this.db.transaction().execute(async (trx) => {
            await trx
                .deleteFrom('order_items')
                .where('order_id', '=', id)
                .execute();

            await trx
                .deleteFrom('orders')
                .where('id', '=', id)
                .execute();
        });
    }

    private mapStatusToDb(status: OrderStatusEnum): 'pending' | 'confirmed' | 'cancelled' {
        switch (status) {
            case OrderStatusEnum.PENDING:
                return 'pending';
            case OrderStatusEnum.CONFIRMED:
                return 'confirmed';
            case OrderStatusEnum.CANCELLED:
                return 'cancelled';
            default:
                return 'pending';
        }
    }

    private mapStatusFromDb(status: string): OrderStatusEnum {
        switch (status.toLowerCase()) {
            case 'pending':
                return OrderStatusEnum.PENDING;
            case 'confirmed':
                return OrderStatusEnum.CONFIRMED;
            case 'cancelled':
                return OrderStatusEnum.CANCELLED;
            default:
                return OrderStatusEnum.PENDING;
        }
    }

    private toDomain(
        orderRow: {
            id: string;
            customer_id: string;
            status: string;
            total_amount: number;
            created_at: Date;
            updated_at: Date;
        },
        itemRows: Array<{
            id: string;
            order_id: string;
            product_id: string;
            quantity: number;
            unit_price: number;
            subtotal: number;
            created_at: Date;
        }>,
    ): Order {
        const items = itemRows.map((itemRow) =>
            OrderItem.create({
                id: itemRow.id,
                productId: itemRow.product_id,
                quantity: Quantity.create(itemRow.quantity),
                unitPrice: Money.create(itemRow.unit_price),
            }),
        );

        return Order.reconstitute({
            id: OrderId.create(orderRow.id),
            customerId: orderRow.customer_id,
            items,
            status: OrderStatus.create(this.mapStatusFromDb(orderRow.status)),
            createdAt: orderRow.created_at,
            updatedAt: orderRow.updated_at,
        });
    }
}


================================================
FILE: apps/api/src/modules/order/order.module.ts
================================================
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { OrderController } from './presentation/order.controller';
import { OrderRepository } from './infrastructure/persistence/kysely-order.repository';
import { OrderCacheService } from './infrastructure/cache/order-cache.service';
import { ORDER_REPOSITORY } from './domain/repositories/order.repository.interface';
import { CreateOrderHandler } from './application/commands/create-order/create-order.handler';
import { ConfirmOrderHandler } from './application/commands/confirm-order/confirm-order.handler';
import { CancelOrderHandler } from './application/commands/cancel-order/cancel-order.handler';
import { GetOrderHandler } from './application/queries/get-order/get-order.handler';
import { ListOrdersHandler } from './application/queries/list-orders/list-orders.handler';
import { OrderCreatedHandler } from './application/event-handlers/order-created.handler';
import { OrderConfirmedHandler } from './application/event-handlers/order-confirmed.handler';
import { OrderPricingService } from './domain/services/order-pricing.service';
import { EventBusService } from '@/shared/infrastructure/messaging/event-bus.service';
import { EVENT_BUS } from '@/shared/infrastructure/messaging/event-bus.interface';

const CommandHandlers = [CreateOrderHandler, ConfirmOrderHandler, CancelOrderHandler];
const QueryHandlers = [GetOrderHandler, ListOrdersHandler];
const EventHandlers = [OrderCreatedHandler, OrderConfirmedHandler];

@Module({
    imports: [CqrsModule],
    controllers: [OrderController],
    providers: [
        ...CommandHandlers,
        ...QueryHandlers,
        ...EventHandlers,
        OrderPricingService,
        OrderCacheService,
        {
            provide: ORDER_REPOSITORY,
            useClass: OrderRepository,
        },
        {
            provide: EVENT_BUS,
            useClass: EventBusService,
        },
    ],
    exports: [OrderCacheService],
})
export class OrderModule {}


================================================
FILE: apps/api/src/modules/order/presentation/order.controller.ts
================================================
import { Controller, Get, Post, Body, Param, Query, HttpCode, HttpStatus } from '@nestjs/common';
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { CreateOrderDto } from '../application/commands/create-order/create-order.dto';
import { CreateOrderCommand } from '../application/commands/create-order/create-order.command';
import { ConfirmOrderCommand } from '../application/commands/confirm-order/confirm-order.command';
import { CancelOrderCommand } from '../application/commands/cancel-order/cancel-order.command';
import { GetOrderQuery } from '../application/queries/get-order/get-order.query';
import { ListOrdersQuery } from '../application/queries/list-orders/list-orders.query';
import { OrderResponseDto } from '../application/queries/get-order/order.response.dto';
import { OrderListResponseDto } from '../application/queries/list-orders/order-list.response.dto';

@ApiTags('orders')
@Controller('orders')
export class OrderController {
    constructor(
        private readonly commandBus: CommandBus,
        private readonly queryBus: QueryBus,
    ) {}

    @Post()
    @ApiOperation({ summary: 'Create a new order' })
    @ApiResponse({ status: 201, description: 'Order created successfully' })
    async createOrder(@Body() dto: CreateOrderDto): Promise<{ orderId: string }> {
        const command = new CreateOrderCommand(dto.customerId, dto.items);
        const orderId = await this.commandBus.execute(command);
        return { orderId };
    }

    @Get(':id')
    @ApiOperation({ summary: 'Get order by ID' })
    @ApiResponse({ status: 200, type: OrderResponseDto })
    async getOrder(@Param('id') id: string): Promise<OrderResponseDto> {
        const query = new GetOrderQuery(id);
        return this.queryBus.execute(query);
    }

    @Get()
    @ApiOperation({ summary: 'List orders' })
    @ApiResponse({ status: 200, type: OrderListResponseDto })
    async listOrders(@Query('customerId') customerId?: string): Promise<OrderListResponseDto> {
        const query = new ListOrdersQuery(customerId);
        return this.queryBus.execute(query);
    }

    @Post(':id/confirm')
    @HttpCode(HttpStatus.NO_CONTENT)
    @ApiOperation({ summary: 'Confirm an order' })
    @ApiResponse({ status: 204, description: 'Order confirmed successfully' })
    async confirmOrder(@Param('id') id: string): Promise<void> {
        const command = new ConfirmOrderCommand(id);
        await this.commandBus.execute(command);
    }

    @Post(':id/cancel')
    @HttpCode(HttpStatus.NO_CONTENT)
    @ApiOperation({ summary: 'Cancel an order' })
    @ApiResponse({ status: 204, description: 'Order cancelled successfully' })
    async cancelOrder(@Param('id') id: string): Promise<void> {
        const command = new CancelOrderCommand(id);
        await this.commandBus.execute(command);
    }
}


================================================
FILE: apps/api/src/shared/api-response/api-response.dto.ts
================================================
// ============================================================================
// API Response DTO - Standardized API response wrapper
// ============================================================================

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

export class ApiResponseDto<T> {
    @ApiProperty({ description: 'Response code', example: 200 })
    code: number;

    @ApiProperty({ description: 'Response message', example: 'Success' })
    message: string;

    @ApiPropertyOptional({ description: 'Response data' })
    data?: T;

    @ApiPropertyOptional({ description: 'Request ID for tracing' })
    requestId?: string;

    @ApiProperty({ description: 'Timestamp' })
    timestamp: string;

    constructor(partial: Partial<ApiResponseDto<T>>) {
        Object.assign(this, partial);
        this.timestamp = this.timestamp || new Date().toISOString();
    }
}

export class PaginatedResponseDto<T> {
    @ApiProperty({ description: 'Items array', type: () => Array })
    items: T[];

    @ApiProperty({ description: 'Total count' })
    total: number;

    @ApiProperty({ description: 'Current page' })
    page: number;

    @ApiProperty({ description: 'Page size' })
    pageSize: number;

    @ApiProperty({ description: 'Total pages' })
    totalPages: number;

    @ApiProperty({ description: 'Has next page' })
    hasNext: boolean;

    @ApiProperty({ description: 'Has previous page' })
    hasPrevious: boolean;

    constructor(partial: Partial<PaginatedResponseDto<T>>) {
        Object.assign(this, partial);
    }
}

export class ApiErrorResponseDto {
    @ApiProperty({ description: 'Error code', example: 'NOT_FOUND' })
    code: string;

    @ApiProperty({ description: 'Error message', example: 'Resource not found' })
    message: string;

    @ApiPropertyOptional({ description: 'Detailed error info' })
    details?: Record<string, unknown>;

    @ApiProperty({ description: 'Timestamp' })
    timestamp: string;

    @ApiPropertyOptional({ description: 'Request ID for tracing' })
    requestId?: string;

    constructor(partial: Partial<ApiErrorResponseDto>) {
        Object.assign(this, partial);
        this.timestamp = this.timestamp || new Date().toISOString();
    }
}


================================================
FILE: apps/api/src/shared/api-response/api-response.interceptor.ts
================================================
// ============================================================================
// API Response Interceptor - Wrap all responses in ApiResponseDto
// ============================================================================

import {
    Injectable,
    NestInterceptor,
    ExecutionContext,
    CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Request } from 'express';
import { ApiResponseDto } from './api-response.dto';

@Injectable()
export class ApiResponseInterceptor implements NestInterceptor {
    intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
        const request = context.switchToHttp().getRequest<Request>();
        const requestId =
            (request.headers['x-request-id'] as string) ||
            (request.headers['x-correlation-id'] as string);

        return next.handle().pipe(
            map((data) => {
                // If already wrapped in ApiResponseDto, return as is
                if (data instanceof ApiResponseDto) {
                    return data;
                }

                // Return wrapped response
                return new ApiResponseDto({
                    code: 200,
                    message: 'Success',
                    data,
                    requestId,
                    timestamp: new Date().toISOString(),
                });
            }),
        );
    }
}


================================================
FILE: apps/api/src/shared/api-response/api-response.service.ts
================================================
// ============================================================================
// API Response Service - Factory for creating standardized responses
// ============================================================================

import { Injectable, Logger } from '@nestjs/common';
import { Request } from 'express';
import {
    ApiResponseDto,
    PaginatedResponseDto,
    ApiErrorResponseDto,
} from './api-response.dto';

@Injectable()
export class ApiResponseService {
    private readonly logger = new Logger(ApiResponseService.name);

    /**
     * Create a success response
     */
    success<T>(data?: T, message = 'Success', requestId?: string): ApiResponseDto<T> {
        return new ApiResponseDto<T>({
            code: 200,
            message,
            data,
            requestId,
        });
    }

    /**
     * Create a created response (201)
     */
    created<T>(data?: T, message = 'Created', requestId?: string): ApiResponseDto<T> {
        return new ApiResponseDto<T>({
            code: 201,
            message,
            data,
            requestId,
        });
    }

    /**
     * Create an accepted response (202)
     */
    accepted<T>(data?: T, message = 'Accepted', requestId?: string): ApiResponseDto<T> {
        return new ApiResponseDto<T>({
            code: 202,
            message,
            data,
            requestId,
        });
    }

    /**
     * Create a no content response (204)
     */
    noContent(requestId?: string): ApiResponseDto<null> {
        return new ApiResponseDto<null>({
            code: 204,
            message: 'No Content',
            requestId,
        });
    }

    /**
     * Create a paginated response
     */
    paginated<T>(
        items: T[],
        total: number,
        page: number,
        pageSize: number,
        message = 'Success',
        requestId?: string,
    ): PaginatedResponseDto<T> {
        const totalPages = Math.ceil(total / pageSize);

        return new PaginatedResponseDto<T>({
            items,
            total,
            page,
            pageSize,
            totalPages,
            hasNext: page < totalPages,
            hasPrevious: page > 1,
        });
    }

    /**
     * Create an error response
     */
    error(
        code: string,
        message: string,
        details?: Record<string, unknown>,
        requestId?: string,
    ): ApiErrorResponseDto {
        return new ApiErrorResponseDto({
            code,
            message,
            details,
            requestId,
        });
    }

    /**
     * Get request ID from request object
     */
    getRequestId(request: Request): string | undefined {
        return (
            (request.headers['x-request-id'] as string) ||
            (request.headers['x-correlation-id'] as string) ||
            undefined
        );
    }
}


================================================
FILE: apps/api/src/shared/api-response/index.ts
================================================
export * from './api-response.dto';
export * from './api-response.service';
export * from './api-response.interceptor';


================================================
FILE: apps/api/src/shared/api-versioning/api-versioning.decorator.ts
================================================
// ============================================================================
// API Versioning Decorators
// ============================================================================

import { SetMetadata } from '@nestjs/common';

export const API_VERSION_KEY = 'api_version';

/**
 * Set API version for a controller or route
 */
export const ApiVersion = (version: string) => SetMetadata(API_VERSION_KEY, version);

/**
 * Mark endpoint as deprecated
 */
export const Deprecated = () => SetMetadata('isDeprecated', true);

/**
 * Set sunset date for endpoint
 */
export const Sunset = (date: Date) => SetMetadata('sunsetDate', date);


================================================
FILE: apps/api/src/shared/api-versioning/api-versioning.interceptor.ts
================================================
// ============================================================================
// API Versioning - URL and Header based versioning
// ============================================================================

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Request } from 'express';

export interface ApiVersionOptions {
    /** Current API version */
    version: string;
    /** Deprecation info */
    deprecated?: boolean;
    /** Sunset date */
    sunsetDate?: Date;
}

/**
 * Default API version
 */
export const DEFAULT_API_VERSION = '1';

/**
 * Extract API version from URL path
 * Supports /api/v1, /api/v2 patterns
 */
export function extractVersionFromUrl(url: string): string | null {
    const match = url.match(/\/api\/v(\d+)/);
    return match ? match[1] : null;
}

/**
 * Extract API version from Accept-Header
 * Supports: application/vnd.api.v1+json
 */
export function extractVersionFromHeader(header: string | string[] | undefined): string | null {
    if (!header) return null;

    const headerValue = Array.isArray(header) ? header[0] : header;
    const match = headerValue.match(/v(\d+)/);
    return match ? match[1] : null;
}

/**
 * Extract API version from custom header
 * Supports: X-API-Version: 1
 */
export function extractVersionFromCustomHeader(
    header: string | string[] | undefined,
): string | null {
    if (!header) return null;
    const value = Array.isArray(header) ? header[0] : header;
    return value || null;
}

@Injectable()
export class ApiVersioningInterceptor implements NestInterceptor {
    intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        const request = context.switchToHttp().getRequest<Request>();

        // Try URL first (e.g., /api/v1/users)
        let version = extractVersionFromUrl(request.url);

        // Fallback to Accept header
        if (!version) {
            version = extractVersionFromHeader(request.headers.accept);
        }

        // Fallback to custom header
        if (!version) {
            version = extractVersionFromCustomHeader(
                request.headers['x-api-version'],
            );
        }

        // Default to v1 if no version specified
        if (!version) {
            version = DEFAULT_API_VERSION;
        }

        // Attach version to request
        (request as any).apiVersion = version;

        return next.handle();
    }
}


================================================
FILE: apps/api/src/shared/api-versioning/index.ts
================================================
// ============================================================================
// API Versioning Module
// ============================================================================

export { ApiVersioningInterceptor } from './api-versioning.interceptor';
export { ApiVersion, Deprecated, Sunset } from './api-versioning.decorator';


================================================
FILE: apps/api/src/shared/application/dto.base.ts
================================================
export abstract class BaseDto {
    constructor(partial?: Partial<any>) {
        if (partial) {
            Object.assign(this, partial);
        }
    }
}


================================================
FILE: apps/api/src/shared/application/query.interface.ts
================================================
export interface IQuery<IResponse> {
    execute(): Promise<IResponse>;
}


================================================
FILE: apps/api/src/shared/application/use-case.interface.ts
================================================
export interface IUseCase<IRequest, IResponse> {
    execute(request: IRequest): Promise<IResponse>;
}


================================================
FILE: apps/api/src/shared/audit/audit.decorator.ts
================================================
// ============================================================================
// Audit Decorators
// ============================================================================

import { SetMetadata } from '@nestjs/common';

export const AUDIT_KEY = 'audit';

/**
 * Mark endpoint to be audited
 */
export const Audited = (resource?: string) => SetMetadata(AUDIT_KEY, { resource });


================================================
FILE: apps/api/src/shared/audit/audit.interceptor.ts
================================================
// ============================================================================
// Audit Interceptor - Automatically logs operations
// ============================================================================

import {
    Injectable,
    NestInterceptor,
    ExecutionContext,
    CallHandler,
    Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
import { AuditService } from './audit.service';
import { Request } from 'express';

export const AUDIT_ACTION_KEY = 'audit_action';
export const AUDIT_RESOURCE_KEY = 'audit_resource';

/**
 * Decorator to mark endpoint for audit logging
 */
export function AuditedAction(action: string) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        Reflect.defineMetadata(AUDIT_ACTION_KEY, action, descriptor.value);
        return descriptor;
    };
}

/**
 * Decorator to specify audit resource
 */
export function AuditedResource(resource: string) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        Reflect.defineMetadata(AUDIT_RESOURCE_KEY, resource, descriptor.value);
        return descriptor;
    };
}

@Injectable()
export class AuditInterceptor implements NestInterceptor {
    private readonly logger = new Logger(AuditInterceptor.name);

    constructor(private readonly auditService: AuditService) {}

    intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        const request = context.switchToHttp().getRequest<Request>();
        const user = (request as any).user;
        const action = this.getAction(context);
        const resource = this.getResource(context);

        if (!user?.sub || !action || !resource) {
            return next.handle();
        }

        const startTime = Date.now();
        const ipAddress = this.getClientIp(request);
        const userAgent = request.headers['user-agent'];

        return next.handle().pipe(
            tap(async (response) => {
                const duration = Date.now() - startTime;
                await this.auditService.log({
                    userId: user.sub,
                    organizationId: user.organizationId,
                    action,
                    resource,
                    resourceId: response?.id ?? request.params?.id,
                    metadata: {
                        method: request.method,
                        path: request.path,
                        duration,
                    },
                    ipAddress,
                    userAgent,
                    status: 'success',
                });
            }),
            catchError(async (error) => {
                const duration = Date.now() - startTime;
                await this.auditService.log({
                    userId: user.sub,
                    organizationId: user.organizationId,
                    action,
                    resource,
                    resourceId: request.params?.id,
                    metadata: {
                        method: request.method,
                        path: request.path,
                        duration,
                    },
                    ipAddress,
                    userAgent,
                    status: 'error',
                    errorMessage: error.message,
                });

                throw error;
            }),
        );
    }

    private getAction(context: ExecutionContext): string | undefined {
        return (
            Reflect.getMetadata(AUDIT_ACTION_KEY, context.getHandler()) ||
            this.inferAction(context)
        );
    }

    private getResource(context: ExecutionContext): string | undefined {
        return (
            Reflect.getMetadata(AUDIT_RESOURCE_KEY, context.getHandler()) ||
            this.inferResource(context)
        );
    }

    private inferAction(context: ExecutionContext): string {
        const method = context.getHandler().name;
        const httpMethod = context.switchToHttp().getRequest().method;

        // Map HTTP methods to CRUD actions
        const actionMap: Record<string, string> = {
            GET: 'read',
            POST: 'create',
            PUT: 'update',
            PATCH: 'update',
            DELETE: 'delete',
        };

        return actionMap[httpMethod] ?? method;
    }

    private inferResource(context: ExecutionContext): string {
        const controller = context.getClass();
        return controller.name.replace('Controller', '').toLowerCase();
    }

    private getClientIp(request: Request): string {
        const forwarded = request.headers['x-forwarded-for'];
        if (forwarded) {
            const ips = Array.isArray(forwarded) ? forwarded[0] : forwarded.split(',')[0];
            return ips.trim();
        }
        return request.ip ?? request.socket.remoteAddress ?? 'unknown';
    }
}


================================================
FILE: apps/api/src/shared/audit/audit.service.ts
================================================
// ============================================================================
// Audit Service - Operation logging for compliance
// ============================================================================

import { Injectable, Logger } from '@nestjs/common';
import { KyselyService } from '@a3s-lab/kysely';
import { v4 as uuidv4 } from 'uuid';

export interface AuditLogEntry {
    id?: string;
    timestamp?: Date;
    userId: string;
    organizationId: string;
    action: string;
    resource: string;
    resourceId?: string;
    changes?: Record<string, { before: unknown; after: unknown }>;
    metadata?: Record<string, unknown>;
    ipAddress?: string;
    userAgent?: string;
    status: 'success' | 'failure' | 'error';
    errorMessage?: string;
}

export interface AuditQueryOptions {
    userId?: string;
    organizationId: string;
    resource?: string;
    action?: string;
    startDate?: Date;
    endDate?: Date;
    page?: number;
    pageSize?: number;
}

export interface PaginatedAuditResult {
    items: AuditLogEntry[];
    total: number;
    page: number;
    pageSize: number;
}

/**
 * Audit Service - logs all operations for compliance
 */
@Injectable()
export class AuditService {
    private readonly logger = new Logger(AuditService.name);
    private readonly tableName = 'audit_logs';

    constructor(private readonly kysely: KyselyService<any>) {}

    /**
     * Log an audit entry
     */
    async log(entry: AuditLogEntry): Promise<void> {
        const auditEntry: Record<string, unknown> = {
            id: entry.id ?? uuidv4(),
            timestamp: entry.timestamp ?? new Date(),
            user_id: entry.userId,
            organization_id: entry.organizationId,
            action: entry.action,
            resource: entry.resource,
            resource_id: entry.resourceId,
            changes: entry.changes ? JSON.stringify(entry.changes) : null,
            metadata: entry.metadata ? JSON.stringify(entry.metadata) : null,
            ip_address: entry.ipAddress,
            user_agent: entry.userAgent,
            status: entry.status,
            error_message: entry.errorMessage,
        };

        try {
            await this.kysely.insertInto(this.tableName).values(auditEntry).executeTakeFirst();
            this.logger.debug(`Audit log created: ${entry.action} on ${entry.resource}`);
        } catch (error) {
            // Don't fail the operation if audit logging fails
            this.logger.error(`Failed to write audit log: ${error}`);
        }
    }

    /**
     * Log entity creation
     */
    async logCreate(
        userId: string,
        organizationId: string,
        resource: string,
        resourceId: string,
        data: Record<string, unknown>,
        metadata?: Record<string, unknown>,
    ): Promise<void> {
        await this.log({
            userId,
            organizationId,
            action: 'create',
            resource,
            resourceId,
            changes: Object.fromEntries(
                Object.entries(data).map(([key, value]) => [key, { before: null, after: value }]),
            ),
            metadata,
            status: 'success',
        });
    }

    /**
     * Log entity update
     */
    async logUpdate(
        userId: string,
        organizationId: string,
        resource: string,
        resourceId: string,
        before: Record<string, unknown>,
        after: Record<string, unknown>,
        metadata?: Record<string, unknown>,
    ): Promise<void> {
        const changes: Record<string, { before: unknown; after: unknown }> = {};

        for (const key of Object.keys(after)) {
            if (JSON.stringify(before[key]) !== JSON.stringify(after[key])) {
                changes[key] = { before: before[key], after: after[key] };
            }
        }

        if (Object.keys(changes).length > 0) {
            await this.log({
                userId,
                organizationId,
                action: 'update',
                resource,
                resourceId,
                changes,
                metadata,
                status: 'success',
            });
        }
    }

    /**
     * Log entity deletion
     */
    async logDelete(
        userId: string,
        organizationId: string,
        resource: string,
        resourceId: string,
        data: Record<string, unknown>,
        metadata?: Record<string, unknown>,
    ): Promise<void> {
        await this.log({
            userId,
            organizationId,
            action: 'delete',
            resource,
            resourceId,
            changes: Object.fromEntries(
                Object.entries(data).map(([key, value]) => [key, { before: value, after: null }]),
            ),
            metadata,
            status: 'success',
        });
    }

    /**
     * Log failed operation
     */
    async logFailure(
        userId: string,
        organizationId: string,
        action: string,
        resource: string,
        errorMessage: string,
        metadata?: Record<string, unknown>,
    ): Promise<void> {
        await this.log({
            userId,
            organizationId,
            action,
            resource,
            metadata,
            status: 'failure',
            errorMessage,
        });
    }

    /**
     * Query audit logs
     */
    async query(options: AuditQueryOptions): Promise<PaginatedAuditResult> {
        const page = options.page ?? 1;
        const pageSize = options.pageSize ?? 20;
        const offset = (page - 1) * pageSize;

        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        let query = (this.kysely as any).selectFrom(this.tableName).where(
            'organization_id',
            '=',
            options.organizationId,
        );

        if (options.userId) {
            query = query.where('user_id', '=', options.userId);
        }

        if (options.resource) {
            query = query.where('resource', '=', options.resource);
        }

        if (options.action) {
            query = query.where('action', '=', options.action);
        }

        if (options.startDate) {
            query = query.where('timestamp', '>=', options.startDate);
        }

        if (options.endDate) {
            query = query.where('timestamp', '<=', options.endDate);
        }

        const countResult = await query
            .select((eb: any) => eb.fn.countAll().as('count'))
            .executeTakeFirst();

        const total = Number((countResult as { count?: number })?.count ?? 0);

        const items = await query
            .orderBy('timestamp', 'desc')
            .limit(pageSize)
            .offset(offset)
            .execute();

        return {
            items: items as AuditLogEntry[],
            total,
            page,
            pageSize,
        };
    }

    /**
     * Get audit log by ID
     */
    async getById(id: string, organizationId: string): Promise<AuditLogEntry | null> {
        const row = await this.kysely
            .selectFrom(this.tableName)
            .where('id', '=', id)
            .where('organization_id', '=', organizationId)
            .executeTakeFirst();

        return (row as AuditLogEntry) || null;
    }
}


================================================
FILE: apps/api/src/shared/audit/index.ts
================================================
// ============================================================================
// Audit Module
// ============================================================================

export * from './audit.service';
export * from './audit.interceptor';
export * from './audit.decorator';


================================================
FILE: apps/api/src/shared/auth/auth.module.ts
================================================
// ============================================================================
// Auth Module - Authentication and Authorization
// ============================================================================

import { Module, Global } from '@nestjs/common';
import { JwtService } from './jwt/jwt.service';
import { RbacService } from './rbac/rbac.service';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { RolesGuard } from './guards/roles.guard';
import { PermissionsGuard } from './guards/permissions.guard';

@Global()
@Module({
    providers: [
        JwtService,
        RbacService,
        JwtAuthGuard,
        RolesGuard,
        PermissionsGuard,
    ],
    exports: [
        JwtService,
        RbacService,
        JwtAuthGuard,
        RolesGuard,
        PermissionsGuard,
    ],
})
export class AuthModule {}


================================================
FILE: apps/api/src/shared/auth/decorators/current-user.decorator.ts
================================================
// ============================================================================
// Auth Decorators - Convenience decorators for extracting user info
// ============================================================================

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { JwtPayload } from '../jwt/jwt.types';

/**
 * Get current user from request
 */
export const CurrentUser = createParamDecorator(
    (data: keyof JwtPayload | undefined, ctx: ExecutionContext) => {
        const request = ctx.switchToHttp().getRequest();
        const user = request.user as JwtPayload;

        if (!user) {
            return null;
        }

        return data ? user[data] : user;
    },
);

/**
 * Get current user ID
 */
export const CurrentUserId = createParamDecorator(
    (_data: unknown, ctx: ExecutionContext) => {
        const request = ctx.switchToHttp().getRequest();
        const user = request.user as JwtPayload;
        return user?.sub;
    },
);

/**
 * Get current organization ID
 */
export const CurrentOrganization = createParamDecorator(
    (_data: unknown, ctx: ExecutionContext) => {
        const request = ctx.switchToHttp().getRequest();
        const user = request.user as JwtPayload;
        return user?.organizationId;
    },
);

/**
 * Get current user roles
 */
export const CurrentRoles = createParamDecorator(
    (_data: unknown, ctx: ExecutionContext) => {
        const request = ctx.switchToHttp().getRequest();
        const user = request.user as JwtPayload;
        return user?.roles ?? [];
    },
);


================================================
FILE: apps/api/src/shared/auth/decorators/index.ts
================================================
// ============================================================================
// Auth Decorators
// ============================================================================

export * from './current-user.decorator';


================================================
FILE: apps/api/src/shared/auth/dto/auth.dto.ts
================================================
// ============================================================================
// Auth DTOs - Data Transfer Objects for authentication
// ============================================================================

import { IsEmail, IsString, MinLength, MaxLength, IsOptional } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

/**
 * Login DTO
 */
export class LoginDto {
    @IsEmail()
    @ApiProperty({ description: 'User email', example: 'user@example.com' })
    email: string;

    @IsString()
    @MinLength(8)
    @ApiProperty({ description: 'User password', minLength: 8 })
    password: string;
}

/**
 * Register DTO
 */
export class RegisterDto {
    @IsEmail()
    @ApiProperty({ description: 'User email', example: 'user@example.com' })
    email: string;

    @IsString()
    @MinLength(3)
    @MaxLength(30)
    @ApiProperty({ description: 'Username', minLength: 3, maxLength: 30 })
    username: string;

    @IsString()
    @MinLength(8)
    @ApiProperty({ description: 'Password', minLength: 8 })
    password: string;

    @IsOptional()
    @IsString()
    @ApiPropertyOptional({ description: 'Display name' })
    displayName?: string;

    @IsOptional()
    @IsString()
    @ApiPropertyOptional({ description: 'Organization name (for first user)' })
    organizationName?: string;
}

/**
 * Refresh Token DTO
 */
export class RefreshTokenDto {
    @IsString()
    @ApiProperty({ description: 'Refresh token' })
    refreshToken: string;
}

/**
 * Change Password DTO
 */
export class ChangePasswordDto {
    @IsString()
    @ApiProperty({ description: 'Current password' })
    currentPassword: string;

    @IsString()
    @MinLength(8)
    @ApiProperty({ description: 'New password', minLength: 8 })
    newPassword: string;
}

/**
 * Forgot Password DTO
 */
export class ForgotPasswordDto {
    @IsEmail()
    @ApiProperty({ description: 'User email' })
    email: string;
}

/**
 * Reset Password DTO
 */
export class ResetPasswordDto {
    @IsString()
    @ApiProperty({ description: 'Reset token' })
    resetToken: string;

    @IsString()
    @MinLength(8)
    @ApiProperty({ description: 'New password', minLength: 8 })
    newPassword: string;
}

/**
 * Verify Email DTO
 */
export class VerifyEmailDto {
    @IsString()
    @ApiProperty({ description: 'Verification token' })
    verifyToken: string;
}

/**
 * Token Response DTO
 */
export class TokenResponseDto {
    @ApiProperty({ description: 'Access token' })
    accessToken: string;

    @ApiProperty({ description: 'Refresh token' })
    refreshToken: string;

    @ApiProperty({ description: 'Token type', default: 'Bearer' })
    tokenType: string;

    @ApiProperty({ description: 'Expires in seconds' })
    expiresIn: number;
}

/**
 * User Response DTO (public info)
 */
export class UserResponseDto {
    @ApiProperty({ description: 'User ID' })
    id: string;

    @ApiProperty({ description: 'Email' })
    email: string;

    @ApiProperty({ description: 'Username' })
    username: string;

    @ApiPropertyOptional({ description: 'Display name' })
    displayName?: string;

    @ApiProperty({ description: 'Roles' })
    roles: string[];
}


================================================
FILE: apps/api/src/shared/auth/dto/index.ts
================================================
// ============================================================================
// Auth DTOs
// ============================================================================

export * from './auth.dto';


================================================
FILE: apps/api/src/shared/auth/guards/index.ts
================================================
// ============================================================================
// Auth Guards
// ============================================================================

export * from './jwt-auth.guard';
export * from './roles.guard';
export * from './permissions.guard';


================================================
FILE: apps/api/src/shared/auth/guards/jwt-auth.guard.ts
================================================
// ============================================================================
// JWT Auth Guard - Validates JWT tokens
// ============================================================================

import {
    Injectable,
    CanActivate,
    ExecutionContext,
    UnauthorizedException,
    SetMetadata,
} from '@nestjs/common';
import { JwtService } from '../jwt/jwt.service';
import { Request } from 'express';
import { JwtPayload } from '../jwt/jwt.types';

/**
 * Metadata key for public routes (skip auth)
 */
export const IS_PUBLIC_KEY = 'isPublic';

/**
 * Mark a route as public (skip JWT validation)
 */
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

@Injectable()
export class JwtAuthGuard implements CanActivate {
    constructor(private readonly jwtService: JwtService) {}

    async canActivate(context: ExecutionContext): Promise<boolean> {
        const request = context.switchToHttp().getRequest<Request>();
        const token = this.extractTokenFromHeader(request);

        if (!token) {
            throw new UnauthorizedException('No token provided');
        }

        try {
            const payload = this.jwtService.verifyAccessToken(token);
            // Attach user to request
            (request as any).user = payload;
            (request as any).userId = payload.sub;
            (request as any).organizationId = payload.organizationId;
        } catch (error) {
            throw new UnauthorizedException('Invalid or expired token');
        }

        return true;
    }

    private extractTokenFromHeader(request: Request): string | undefined {
        const authHeader = request.headers.authorization;
        if (!authHeader) {
            return undefined;
        }

        const [type, token] = authHeader.split(' ');
        return type === 'Bearer' ? token : undefined;
    }
}


================================================
FILE: apps/api/src/shared/auth/guards/permissions.guard.ts
================================================
// ============================================================================
// Permissions Guard - Checks user permissions (RBAC)
// ============================================================================

import {
    Injectable,
    CanActivate,
    ExecutionContext,
    ForbiddenException,
    SetMetadata,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { RbacService } from '../rbac/rbac.service';
import { JwtPayload } from '../jwt/jwt.types';

/**
 * Metadata key for required permissions
 */
export const PERMISSIONS_KEY = 'permissions';

/**
 * Require specific permissions to access route
 * Format: 'resource:action' e.g., 'users:read', 'workflows:delete'
 */
export const Permissions = (...permissions: string[]) =>
    SetMetadata(PERMISSIONS_KEY, permissions);

/**
 * Permissions Guard - checks if user has required permissions
 */
@Injectable()
export class PermissionsGuard implements CanActivate {
    constructor(
        private readonly reflector: Reflector,
        private readonly rbacService: RbacService,
    ) {}

    canActivate(context: ExecutionContext): boolean {
        const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
            PERMISSIONS_KEY,
            [context.getHandler(), context.getClass()],
        );

        if (!requiredPermissions || requiredPermissions.length === 0) {
            return true;
        }

        const request = context.switchToHttp().getRequest();
        const user = request.user as JwtPayload;

        if (!user || !user.roles) {
            throw new ForbiddenException('Access denied: No permissions assigned');
        }

        for (const permission of requiredPermissions) {
            const [resource, action] = permission.split(':');
            const hasPermission = this.rbacService.hasAnyPermission(
                user.roles,
                resource,
                action,
            );

            if (!hasPermission) {
                throw new ForbiddenException(
                    `Access denied: Missing permission '${permission}'`,
                );
            }
        }

        return true;
    }
}


================================================
FILE: apps/api/src/shared/auth/guards/roles.guard.ts
================================================
// ============================================================================
// Roles Guard - Checks user roles
// ============================================================================

import {
    Injectable,
    CanActivate,
    ExecutionContext,
    ForbiddenException,
    SetMetadata,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { RbacService } from '../rbac/rbac.service';
import { JwtPayload } from '../jwt/jwt.types';

/**
 * Metadata key for required roles
 */
export const ROLES_KEY = 'roles';

/**
 * Require specific roles to access route
 */
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);

/**
 * Roles Guard - checks if user has required roles
 */
@Injectable()
export class RolesGuard implements CanActivate {
    constructor(
        private readonly reflector: Reflector,
        private readonly rbacService: RbacService,
    ) {}

    canActivate(context: ExecutionContext): boolean {
        const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
            context.getHandler(),
            context.getClass(),
        ]);

        if (!requiredRoles || requiredRoles.length === 0) {
            return true;
        }

        const request = context.switchToHttp().getRequest();
        const user = request.user as JwtPayload;

        if (!user || !user.roles) {
            throw new ForbiddenException('Access denied: No roles assigned');
        }

        const hasRole = this.rbacService.hasAnyRole(user.roles, requiredRoles);
        if (!hasRole) {
            throw new ForbiddenException(
                `Access denied: Required role(s): ${requiredRoles.join(', ')}`,
            );
        }

        return true;
    }
}


================================================
FILE: apps/api/src/shared/auth/index.ts
================================================
// ============================================================================
// Auth Module - Authentication and Authorization
// ============================================================================

export * from './jwt';
export * from './rbac';
export * from './guards';
export * from './decorators';
export * from './dto';
export * from './auth.module';


================================================
FILE: apps/api/src/shared/auth/jwt/index.ts
================================================
// ============================================================================
// JWT Module
// ============================================================================

export * from './jwt.service';
export * from './jwt.types';


================================================
FILE: apps/api/src/shared/auth/jwt/jwt.service.ts
================================================
// ============================================================================
// JWT Service - Token generation and verification
// ============================================================================

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as jwt from 'jsonwebtoken';
import { JwtPayload, TokenPair } from './jwt.types';

@Injectable()
export class JwtService {
    private readonly accessTokenSecret: string;
    private readonly refreshTokenSecret: string;
    private readonly accessTokenExpiry: string;
    private readonly refreshTokenExpiry: string;

    constructor(private readonly configService: ConfigService) {
        this.accessTokenSecret = this.configService.get<string>('JWT_ACCESS_SECRET') || 'default-access-secret';
        this.refreshTokenSecret = this.configService.get<string>('JWT_REFRESH_SECRET') || 'default-refresh-secret';
        this.accessTokenExpiry = this.configService.get<string>('JWT_ACCESS_EXPIRY') || '15m';
        this.refreshTokenExpiry = this.configService.get<string>('JWT_REFRESH_EXPIRY') || '7d';
    }

    /**
     * Generate access token
     */
    generateAccessToken(payload: JwtPayload): string {
        return jwt.sign(payload as object, this.accessTokenSecret, {
            expiresIn: this.accessTokenExpiry as jwt.SignOptions['expiresIn'],
        });
    }

    /**
     * Generate refresh token
     */
    generateRefreshToken(payload: JwtPayload): string {
        return jwt.sign(payload as object, this.refreshTokenSecret, {
            expiresIn: this.refreshTokenExpiry as jwt.SignOptions['expiresIn'],
        });
    }

    /**
     * Generate both access and refresh tokens
     */
    generateTokenPair(payload: JwtPayload): TokenPair {
        return {
            accessToken: this.generateAccessToken(payload),
            refreshToken: this.generateRefreshToken(payload),
        };
    }

    /**
     * Verify access token
     */
    verifyAccessToken(token: string): JwtPayload {
        try {
            return jwt.verify(token, this.accessTokenSecret) as JwtPayload;
        } catch (error) {
            throw new UnauthorizedException('Invalid or expired access token');
        }
    }

    /**
     * Verify refresh token
     */
    verifyRefreshToken(token: string): JwtPayload {
        try {
            return jwt.verify(token, this.refreshTokenSecret) as JwtPayload;
        } catch (error) {
            throw new UnauthorizedException('Invalid or expired refresh token');
        }
    }

    /**
     * Decode token without verification (for debugging)
     */
    decodeToken(token: string): JwtPayload | null {
        try {
            return jwt.decode(token) as JwtPayload;
        } catch {
            return null;
        }
    }

    /**
     * Check if token is about to expire (within 5 minutes)
     */
    isTokenExpiringSoon(token: string): boolean {
        const decoded = this.decodeToken(token);
        if (!decoded || !decoded.exp) {
            return true;
        }

        const fiveMinutesFromNow = Math.floor(Date.now() / 1000) + 300;
        return decoded.exp < fiveMinutesFromNow;
    }
}


================================================
FILE: apps/api/src/shared/auth/jwt/jwt.types.ts
================================================
// ============================================================================
// JWT Types
// ============================================================================

/**
 * JWT Payload - contains user information stored in the token
 */
export interface JwtPayload {
    /** User ID */
    sub: string;
    /** User email */
    email: string;
    /** Organization/Tenant ID */
    organizationId: string;
    /** User roles */
    roles: string[];
    /** Permissions (optional, can be computed from roles) */
    permissions?: string[];
    /** Token type: access or refresh */
    type: 'access' | 'refresh';
    /** Issued at timestamp */
    iat?: number;
    /** Expiration timestamp */
    exp?: number;
}

/**
 * Access Token
 */
export interface AccessToken {
    token: string;
    expiresIn: string;
}

/**
 * Refresh Token
 */
export interface RefreshToken {
    token: string;
    expiresIn: string;
}

/**
 * Token Pair - both access and refresh tokens
 */
export interface TokenPair {
    accessToken: string;
    refreshToken: string;
}

/**
 * Token Response - returned to client after login
 */
export interface TokenResponse {
    accessToken: string;
    refreshToken: string;
    tokenType: 'Bearer';
    expiresIn: number;
}


================================================
FILE: apps/api/src/shared/auth/rbac/index.ts
================================================
// ============================================================================
// RBAC Module
// ============================================================================

export * from './rbac.service';


================================================
FILE: apps/api/src/shared/auth/rbac/rbac.service.ts
================================================
// ============================================================================
// RBAC - Role-Based Access Control
// ============================================================================

import { Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

/**
 * Permission definition
 */
export interface Permission {
    resource: string;
    actions: string[]; // e.g., ['create', 'read', 'update', 'delete']
}

/**
 * Role definition
 */
export interface Role {
    name: string;
    permissions: Permission[];
}

/**
 * Default roles and permissions
 */
export const DEFAULT_PERMISSIONS: Permission[] = [
    { resource: 'users', actions: ['create', 'read', 'update', 'delete'] },
    { resource: 'organizations', actions: ['create', 'read', 'update', 'delete'] },
    { resource: 'agents', actions: ['create', 'read', 'update', 'delete'] },
    { resource: 'workflows', actions: ['create', 'read', 'update', 'delete'] },
    { resource: 'knowledge', actions: ['create', 'read', 'update', 'delete'] },
    { resource: 'repositories', actions: ['create', 'read', 'update', 'delete'] },
    { resource: 'pipeline', actions: ['create', 'read', 'update', 'delete'] },
    { resource: 'audit', actions: ['read'] },
];

export const DEFAULT_ROLES: Role[] = [
    {
        name: 'owner',
        permissions: DEFAULT_PERMISSIONS,
    },
    {
        name: 'admin',
        permissions: DEFAULT_PERMISSIONS.filter(p => p.resource !== 'organizations'),
    },
    {
        name: 'member',
        permissions: [
            { resource: 'users', actions: ['read', 'update'] },
            { resource: 'agents', actions: ['create', 'read', 'update'] },
            { resource: 'workflows', actions: ['create', 'read', 'update'] },
            { resource: 'knowledge', actions: ['create', 'read', 'update'] },
            { resource: 'repositories', actions: ['create', 'read', 'update'] },
            { resource: 'pipeline', actions: ['read'] },
        ],
    },
    {
        name: 'viewer',
        permissions: [
            { resource: 'users', actions: ['read'] },
            { resource: 'agents', actions: ['read'] },
            { resource: 'workflows', actions: ['read'] },
            { resource: 'knowledge', actions: ['read'] },
            { resource: 'repositories', actions: ['read'] },
            { resource: 'pipeline', actions: ['read'] },
        ],
    },
];

/**
 * RBAC Service - handles permission checking
 */
@Injectable()
export class RbacService {
    private readonly roles: Map<string, Role> = new Map();

    constructor() {
        // Initialize default roles
        for (const role of DEFAULT_ROLES) {
            this.roles.set(role.name, role);
        }
    }

    /**
     * Get role by name
     */
    getRole(roleName: string): Role | undefined {
        return this.roles.get(roleName);
    }

    /**
     * Check if a role has a specific permission
     */
    hasPermission(roleName: string, resource: string, action: string): boolean {
        const role = this.getRole(roleName);
        if (!role) {
            return false;
        }

        const permission = role.permissions.find(p => p.resource === resource);
        if (!permission) {
            return false;
        }

        return permission.actions.includes(action) || permission.actions.includes('*');
    }

    /**
     * Check if any of the roles has a specific permission
     */
    hasAnyPermission(roleNames: string[], resource: string, action: string): boolean {
        return roleNames.some(roleName => this.hasPermission(roleName, resource, action));
    }

    /**
     * Check if all roles have a specific permission
     */
    hasAllPermissions(roleNames: string[], resource: string, action: string): boolean {
        return roleNames.every(roleName => this.hasPermission(roleName, resource, action));
    }

    /**
     * Get all permissions for a role
     */
    getPermissions(roleName: string): Permission[] {
        const role = this.getRole(roleName);
        return role?.permissions ?? [];
    }

    /**
     * Register a custom role
     */
    registerRole(role: Role): void {
        this.roles.set(role.name, role);
    }

    /**
     * Check if user has role
     */
    hasRole(userRoles: string[], requiredRole: string): boolean {
        return userRoles.includes(requiredRole);
    }

    /**
     * Check if user has any of the roles
     */
    hasAnyRole(userRoles: string[], requiredRoles: string[]): boolean {
        return userRoles.some(role => requiredRoles.includes(role));
    }
}


================================================
FILE: apps/api/src/shared/base/base.entity.ts
================================================
// ============================================================================
// Base Entity Interface - Common properties for all entities
// ============================================================================

export interface BaseEntity {
    id: string;
    createdAt: Date;
    updatedAt: Date;
}

export interface TenantEntity extends BaseEntity {
    organizationId: string;
}

export interface SoftDeleteEntity extends BaseEntity {
    deletedAt?: Date;
    deletedBy?: string;
}

export interface VersionedEntity extends BaseEntity {
    version: number;
}


================================================
FILE: apps/api/src/shared/base/base.service.ts
================================================
// ============================================================================
// Base Service - Generic CRUD operations with Kysely
// ============================================================================

import { KyselyService } from '@a3s-lab/kysely';
import { NotFoundException } from '@nestjs/common';
import { parsePaginationOptions, PaginationOptions, PaginationQueryDto } from './pagination.dto';

export interface FindOptions<FilterDto, SortDto> {
    filter?: FilterDto;
    sort?: SortDto;
    pagination?: PaginationQueryDto;
}

export interface PaginatedResult<T> {
    items: T[];
    total: number;
    page: number;
    pageSize: number;
    totalPages: number;
}

export abstract class BaseService<
    Entity extends { id: string },
    CreateDto,
    UpdateDto,
    FilterDto = never,
    SortDto = never,
> {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    constructor(
        protected readonly kysely: KyselyService<any>,
        protected readonly tableName: string,
    ) {}

    /**
     * Create a new entity
     */
    async create(dto: CreateDto, additionalData?: Partial<Entity>): Promise<Entity> {
        const now = new Date();
        const entity: Partial<Entity> = {
            ...dto,
            ...additionalData,
            id: crypto.randomUUID(),
            createdAt: now,
            updatedAt: now,
        } as Partial<Entity>;

        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        await (this.kysely as any)
            .insertInto(this.tableName)
            .values(entity as Record<string, unknown>)
            .executeTakeFirstOrThrow();

        return entity as Entity;
    }

    /**
     * Find entity by ID
     */
    async findById(id: string): Promise<Entity | null> {
        const row = await (this.kysely as any)
            .selectFrom(this.tableName)
            .where('id', '=', id)
            .executeTakeFirst();

        return (row as Entity) || null;
    }

    /**
     * Find entity by ID or throw NotFoundException
     */
    async findByIdOrThrow(id: string): Promise<Entity> {
        const entity = await this.findById(id);
        if (!entity) {
            throw new NotFoundException(`${this.tableName} with id '${id}' not found`);
        }
        return entity;
    }

    /**
     * Find all entities with pagination (optimized - uses SQL COUNT)
     */
    async findAll(options?: FindOptions<FilterDto, SortDto>): Promise<PaginatedResult<Entity>> {
        const paginationOptions = options?.pagination
            ? parsePaginationOptions(options.pagination)
            : { page: 1, pageSize: 20, limit: 20, offset: 0 };

        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        let baseQuery = (this.kysely as any).selectFrom(this.tableName);

        // Apply filters if FilterDto is provided
        if (options?.filter) {
            baseQuery = this.applyFilters(baseQuery, options.filter);
        }

        // Get total count using efficient SQL COUNT
        const countResult = await baseQuery
            .select((eb: any) => eb.fn.countAll().as('count'))
            .executeTakeFirst();

        const total = Number(countResult?.count ?? 0);

        // Apply pagination and sorting to a separate query
        let dataQuery = baseQuery.limit(paginationOptions.limit).offset(paginationOptions.offset);

        if (options?.sort) {
            dataQuery = this.applySort(dataQuery, options.sort);
        }

        const items = await dataQuery.execute();

        return {
            items: items as Entity[],
            total,
            page: paginationOptions.page,
            pageSize: paginationOptions.pageSize,
            totalPages: Math.ceil(total / paginationOptions.pageSize),
        };
    }

    /**
     * Find entities with pagination using cursor-based approach (for large datasets)
     */
    async findAllCursor(
        cursor: string | null,
        limit: number,
        options?: FindOptions<FilterDto, SortDto>,
    ): Promise<{ items: Entity[]; nextCursor: string | null }> {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        let query = (this.kysely as any).selectFrom(this.tableName);

        if (options?.filter) {
            query = this.applyFilters(query, options.filter);
        }

        // Cursor-based pagination using id > cursor
        if (cursor) {
            query = query.where('id', '>', cursor);
        }

        query = query.orderBy('id', 'asc').limit(limit + 1); // Fetch one extra to check if there's more

        const items = await query.execute();
        const hasMore = items.length > limit;
        const result = hasMore ? items.slice(0, -1) : items;

        return {
            items: result as Entity[],
            nextCursor: hasMore ? (result[result.length - 1] as Entity).id : null,
        };
    }

    /**
     * Update entity by ID
     */
    async update(id: string, dto: UpdateDto): Promise<Entity> {
        const existing = await this.findByIdOrThrow(id);

        const updated: Partial<Entity> = {
            ...existing,
            ...dto,
            id: existing.id,
            createdAt: (existing as any).createdAt,
            updatedAt: new Date(),
        } as Partial<Entity>;

        await (this.kysely as any)
            .updateTable(this.tableName)
            .set(updated as Record<string, unknown>)
            .where('id', '=', id)
            .executeTakeFirst();

        return updated as Entity;
    }

    /**
     * Update entities by filter (batch update)
     */
    async updateMany(filter: FilterDto, dto: Partial<UpdateDto>): Promise<number> {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        let query = (this.kysely as any).updateTable(this.tableName).set({
            ...dto,
            updatedAt: new Date(),
        } as Record<string, unknown>);

        query = this.applyFilters(query, filter);

        const result = await query.execute();
        return result.numUpdatedRows ?? 0;
    }

    /**
     * Delete entity by ID (hard delete)
     */
    async delete(id: string): Promise<void> {
        const existing = await this.findByIdOrThrow(id);

        await (this.kysely as any)
            .deleteFrom(this.tableName)
            .where('id', '=', id)
            .executeTakeFirst();
    }

    /**
     * Soft delete entity by ID
     */
    async softDelete(id: string, deletedBy?: string): Promise<void> {
        await this.findByIdOrThrow(id);

        await (this.kysely as any)
            .updateTable(this.tableName)
            .set({
                deletedAt: new Date(),
                deletedBy,
                updatedAt: new Date(),
            } as Record<string, unknown>)
            .where('id', '=', id)
            .executeTakeFirst();
    }

    /**
     * Soft delete entities by filter (batch)
     */
    async softDeleteMany(filter: FilterDto, deletedBy?: string): Promise<number> {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        let query = (this.kysely as any).updateTable(this.tableName).set({
            deletedAt: new Date(),
            deletedBy,
            updatedAt: new Date(),
        } as Record<string, unknown>);

        query = this.applyFilters(query, filter);

        const result = await query.execute();
        return result.numUpdatedRows ?? 0;
    }

    /**
     * Check if entity exists
     */
    async exists(id: string): Promise<boolean> {
        const entity = await (this.kysely as any)
       
Download .txt
gitextract_v0ktoj1e/

├── .gitignore
├── .npmrc
├── LICENSE
├── README.md
├── apps/
│   └── api/
│       ├── DATABASE.md
│       ├── REDIS_USAGE.md
│       ├── migrations/
│       │   └── 001_create_orders_tables.sql
│       ├── nest-cli.json
│       ├── package.json
│       ├── src/
│       │   ├── app.module.ts
│       │   ├── main.ts
│       │   ├── modules/
│       │   │   └── order/
│       │   │       ├── application/
│       │   │       │   ├── commands/
│       │   │       │   │   ├── cancel-order/
│       │   │       │   │   │   ├── cancel-order.command.ts
│       │   │       │   │   │   ├── cancel-order.dto.ts
│       │   │       │   │   │   ├── cancel-order.handler.spec.ts
│       │   │       │   │   │   └── cancel-order.handler.ts
│       │   │       │   │   ├── confirm-order/
│       │   │       │   │   │   ├── confirm-order.command.ts
│       │   │       │   │   │   ├── confirm-order.dto.ts
│       │   │       │   │   │   ├── confirm-order.handler.spec.ts
│       │   │       │   │   │   └── confirm-order.handler.ts
│       │   │       │   │   └── create-order/
│       │   │       │   │       ├── create-order.command.ts
│       │   │       │   │       ├── create-order.dto.ts
│       │   │       │   │       ├── create-order.handler.spec.ts
│       │   │       │   │       └── create-order.handler.ts
│       │   │       │   ├── event-handlers/
│       │   │       │   │   ├── order-confirmed.handler.ts
│       │   │       │   │   └── order-created.handler.ts
│       │   │       │   └── queries/
│       │   │       │       ├── get-order/
│       │   │       │       │   ├── get-order.handler.spec.ts
│       │   │       │       │   ├── get-order.handler.ts
│       │   │       │       │   ├── get-order.query.ts
│       │   │       │       │   └── order.response.dto.ts
│       │   │       │       └── list-orders/
│       │   │       │           ├── list-orders.handler.spec.ts
│       │   │       │           ├── list-orders.handler.ts
│       │   │       │           ├── list-orders.query.ts
│       │   │       │           └── order-list.response.dto.ts
│       │   │       ├── domain/
│       │   │       │   ├── entities/
│       │   │       │   │   ├── order-item.entity.spec.ts
│       │   │       │   │   ├── order-item.entity.ts
│       │   │       │   │   ├── order.entity.spec.ts
│       │   │       │   │   └── order.entity.ts
│       │   │       │   ├── events/
│       │   │       │   │   ├── order-cancelled.event.ts
│       │   │       │   │   ├── order-confirmed.event.ts
│       │   │       │   │   └── order-created.event.ts
│       │   │       │   ├── exceptions/
│       │   │       │   │   ├── invalid-order-state.exception.ts
│       │   │       │   │   └── order-not-found.exception.ts
│       │   │       │   ├── repositories/
│       │   │       │   │   └── order.repository.interface.ts
│       │   │       │   ├── services/
│       │   │       │   │   ├── order-pricing.service.spec.ts
│       │   │       │   │   └── order-pricing.service.ts
│       │   │       │   └── value-objects/
│       │   │       │       ├── money.vo.spec.ts
│       │   │       │       ├── money.vo.ts
│       │   │       │       ├── order-id.vo.spec.ts
│       │   │       │       ├── order-id.vo.ts
│       │   │       │       ├── order-status.vo.spec.ts
│       │   │       │       ├── order-status.vo.ts
│       │   │       │       ├── quantity.vo.spec.ts
│       │   │       │       └── quantity.vo.ts
│       │   │       ├── infrastructure/
│       │   │       │   ├── cache/
│       │   │       │   │   └── order-cache.service.ts
│       │   │       │   └── persistence/
│       │   │       │       └── kysely-order.repository.ts
│       │   │       ├── order.module.ts
│       │   │       └── presentation/
│       │   │           └── order.controller.ts
│       │   └── shared/
│       │       ├── api-response/
│       │       │   ├── api-response.dto.ts
│       │       │   ├── api-response.interceptor.ts
│       │       │   ├── api-response.service.ts
│       │       │   └── index.ts
│       │       ├── api-versioning/
│       │       │   ├── api-versioning.decorator.ts
│       │       │   ├── api-versioning.interceptor.ts
│       │       │   └── index.ts
│       │       ├── application/
│       │       │   ├── dto.base.ts
│       │       │   ├── query.interface.ts
│       │       │   └── use-case.interface.ts
│       │       ├── audit/
│       │       │   ├── audit.decorator.ts
│       │       │   ├── audit.interceptor.ts
│       │       │   ├── audit.service.ts
│       │       │   └── index.ts
│       │       ├── auth/
│       │       │   ├── auth.module.ts
│       │       │   ├── decorators/
│       │       │   │   ├── current-user.decorator.ts
│       │       │   │   └── index.ts
│       │       │   ├── dto/
│       │       │   │   ├── auth.dto.ts
│       │       │   │   └── index.ts
│       │       │   ├── guards/
│       │       │   │   ├── index.ts
│       │       │   │   ├── jwt-auth.guard.ts
│       │       │   │   ├── permissions.guard.ts
│       │       │   │   └── roles.guard.ts
│       │       │   ├── index.ts
│       │       │   ├── jwt/
│       │       │   │   ├── index.ts
│       │       │   │   ├── jwt.service.ts
│       │       │   │   └── jwt.types.ts
│       │       │   └── rbac/
│       │       │       ├── index.ts
│       │       │       └── rbac.service.ts
│       │       ├── base/
│       │       │   ├── base.entity.ts
│       │       │   ├── base.service.ts
│       │       │   ├── index.ts
│       │       │   └── pagination.dto.ts
│       │       ├── cache/
│       │       │   ├── cache.decorator.ts
│       │       │   ├── cache.interceptor.ts
│       │       │   ├── cache.service.ts
│       │       │   └── index.ts
│       │       ├── circuit-breaker/
│       │       │   ├── circuit-breaker.decorator.ts
│       │       │   ├── circuit-breaker.module.ts
│       │       │   ├── circuit-breaker.service.ts
│       │       │   └── index.ts
│       │       ├── database/
│       │       │   ├── database.module.ts
│       │       │   ├── database.types.ts
│       │       │   └── index.ts
│       │       ├── domain/
│       │       │   ├── aggregate-root.ts
│       │       │   ├── domain-event-publisher.ts
│       │       │   ├── domain-event.ts
│       │       │   ├── entity.ts
│       │       │   ├── index.ts
│       │       │   └── value-object.ts
│       │       ├── errors/
│       │       │   ├── business.exception.ts
│       │       │   ├── error-codes.ts
│       │       │   ├── error.filter.ts
│       │       │   └── index.ts
│       │       ├── feature-flags/
│       │       │   ├── feature-flags.decorator.ts
│       │       │   ├── feature-flags.guard.ts
│       │       │   ├── feature-flags.service.ts
│       │       │   └── index.ts
│       │       ├── file-upload/
│       │       │   ├── file-upload.decorator.ts
│       │       │   ├── file-upload.interceptor.ts
│       │       │   ├── file-upload.service.ts
│       │       │   └── index.ts
│       │       ├── health/
│       │       │   ├── health.controller.ts
│       │       │   ├── health.module.ts
│       │       │   ├── index.ts
│       │       │   └── indicators/
│       │       │       ├── database.indicator.ts
│       │       │       ├── nats.indicator.ts
│       │       │       ├── redis.indicator.ts
│       │       │       └── rustfs.indicator.ts
│       │       ├── infrastructure/
│       │       │   ├── messaging/
│       │       │   │   ├── event-bus.interface.ts
│       │       │   │   ├── event-bus.service.ts
│       │       │   │   └── messaging.interface.ts
│       │       │   ├── persistence/
│       │       │   │   ├── repository.interface.ts
│       │       │   │   └── unit-of-work.interface.ts
│       │       │   └── storage/
│       │       │       └── storage.interface.ts
│       │       ├── metrics/
│       │       │   ├── index.ts
│       │       │   ├── metrics.controller.ts
│       │       │   ├── metrics.interceptor.ts
│       │       │   └── metrics.service.ts
│       │       ├── openapi/
│       │       │   ├── index.ts
│       │       │   ├── openapi-common.dto.ts
│       │       │   └── openapi-decorators.ts
│       │       ├── presentation/
│       │       │   ├── filters/
│       │       │   │   ├── domain-exception.filter.ts
│       │       │   │   └── http-exception.filter.ts
│       │       │   └── interceptors/
│       │       │       └── logging.interceptor.ts
│       │       ├── rate-limiting/
│       │       │   ├── index.ts
│       │       │   ├── rate-limiting.decorator.ts
│       │       │   ├── rate-limiting.guard.ts
│       │       │   └── rate-limiting.service.ts
│       │       ├── redis/
│       │       │   ├── index.ts
│       │       │   └── redis.module.ts
│       │       ├── retry/
│       │       │   ├── index.ts
│       │       │   ├── retry.module.ts
│       │       │   └── retry.service.ts
│       │       ├── serialization/
│       │       │   ├── example.ts
│       │       │   ├── index.ts
│       │       │   └── serializer.ts
│       │       ├── tenant/
│       │       │   ├── index.ts
│       │       │   ├── tenant.decorator.ts
│       │       │   ├── tenant.guard.ts
│       │       │   ├── tenant.interceptor.ts
│       │       │   └── tenant.service.ts
│       │       ├── testing/
│       │       │   ├── index.ts
│       │       │   └── testing.utils.ts
│       │       ├── tracking/
│       │       │   ├── index.ts
│       │       │   └── tracking.interceptor.ts
│       │       ├── transform/
│       │       │   ├── index.ts
│       │       │   └── transform.interceptor.ts
│       │       ├── utils/
│       │       │   ├── guard.ts
│       │       │   └── result.ts
│       │       └── validation/
│       │           ├── index.ts
│       │           ├── validation-options.ts
│       │           └── validation.pipe.ts
│       ├── test/
│       │   └── jest-e2e.json
│       ├── tsconfig.build.json
│       └── tsconfig.json
├── biome.json
├── docker/
│   ├── Dockerfile
│   ├── Dockerfile.dev
│   ├── docker-compose.prod.yml
│   └── docker-compose.yml
├── docs/
│   ├── architecture.md
│   └── ddd-patterns.md
├── nest-cli.json
├── package.json
├── packages/
│   ├── bullmq/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── bullmq.module-definition.ts
│   │   │   ├── bullmq.module.ts
│   │   │   ├── bullmq.service.ts
│   │   │   ├── bullmq.types.ts
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── etcd/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── config.service.ts
│   │   │   ├── etcd.module.ts
│   │   │   ├── etcd.service.ts
│   │   │   ├── etcd.types.ts
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── kysely/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   ├── kysely.logger.spec.ts
│   │   │   │   └── kysely.module.spec.ts
│   │   │   ├── index.ts
│   │   │   ├── kysely-module-options.interface.ts
│   │   │   ├── kysely.logger.ts
│   │   │   ├── kysely.module-definition.ts
│   │   │   ├── kysely.module.ts
│   │   │   └── kysely.service.ts
│   │   └── tsconfig.json
│   ├── logger/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── logger.module-definition.ts
│   │   │   ├── logger.module.ts
│   │   │   ├── logger.service.ts
│   │   │   ├── logger.types.ts
│   │   │   └── logging.interceptor.ts
│   │   └── tsconfig.json
│   ├── nats/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── nats.module-definition.ts
│   │   │   ├── nats.module.ts
│   │   │   ├── nats.service.ts
│   │   │   └── nats.types.ts
│   │   └── tsconfig.json
│   ├── redisson/
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   ├── redisson.module.spec.ts
│   │   │   │   └── types.spec.ts
│   │   │   ├── index.ts
│   │   │   ├── redisson-module-options.interface.ts
│   │   │   ├── redisson.module-definition.ts
│   │   │   ├── redisson.module.ts
│   │   │   ├── redisson.service.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   └── rustfs/
│       ├── package.json
│       ├── src/
│       │   ├── index.ts
│       │   ├── rustfs.module-definition.ts
│       │   ├── rustfs.module.ts
│       │   ├── rustfs.service.ts
│       │   └── rustfs.types.ts
│       └── tsconfig.json
├── pnpm-workspace.yaml
├── tsconfig.build.json
└── tsconfig.json
Download .txt
SYMBOL INDEX (995 symbols across 144 files)

FILE: apps/api/migrations/001_create_orders_tables.sql
  type orders (line 2) | CREATE TABLE IF NOT EXISTS orders (
  type order_items (line 12) | CREATE TABLE IF NOT EXISTS order_items (
  type idx_orders_customer_id (line 23) | CREATE INDEX IF NOT EXISTS idx_orders_customer_id ON orders(customer_id)
  type idx_orders_status (line 24) | CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status)
  type idx_order_items_order_id (line 25) | CREATE INDEX IF NOT EXISTS idx_order_items_order_id ON order_items(order...

FILE: apps/api/src/app.module.ts
  class AppModule (line 111) | class AppModule {}

FILE: apps/api/src/main.ts
  function bootstrap (line 9) | async function bootstrap() {

FILE: apps/api/src/modules/order/application/commands/cancel-order/cancel-order.command.ts
  class CancelOrderCommand (line 1) | class CancelOrderCommand {
    method constructor (line 2) | constructor(public readonly orderId: string) {}

FILE: apps/api/src/modules/order/application/commands/cancel-order/cancel-order.dto.ts
  class CancelOrderDto (line 4) | class CancelOrderDto {

FILE: apps/api/src/modules/order/application/commands/cancel-order/cancel-order.handler.ts
  class CancelOrderHandler (line 9) | class CancelOrderHandler implements ICommandHandler<CancelOrderCommand> {
    method constructor (line 10) | constructor(
    method execute (line 17) | async execute(command: CancelOrderCommand): Promise<void> {

FILE: apps/api/src/modules/order/application/commands/confirm-order/confirm-order.command.ts
  class ConfirmOrderCommand (line 1) | class ConfirmOrderCommand {
    method constructor (line 2) | constructor(public readonly orderId: string) {}

FILE: apps/api/src/modules/order/application/commands/confirm-order/confirm-order.dto.ts
  class ConfirmOrderDto (line 4) | class ConfirmOrderDto {

FILE: apps/api/src/modules/order/application/commands/confirm-order/confirm-order.handler.ts
  class ConfirmOrderHandler (line 9) | class ConfirmOrderHandler implements ICommandHandler<ConfirmOrderCommand> {
    method constructor (line 10) | constructor(
    method execute (line 17) | async execute(command: ConfirmOrderCommand): Promise<void> {

FILE: apps/api/src/modules/order/application/commands/create-order/create-order.command.ts
  class CreateOrderCommand (line 1) | class CreateOrderCommand {
    method constructor (line 2) | constructor(

FILE: apps/api/src/modules/order/application/commands/create-order/create-order.dto.ts
  class CreateOrderItemDto (line 5) | class CreateOrderItemDto {
  class CreateOrderDto (line 21) | class CreateOrderDto {

FILE: apps/api/src/modules/order/application/commands/create-order/create-order.handler.ts
  class CreateOrderHandler (line 13) | class CreateOrderHandler implements ICommandHandler<CreateOrderCommand> {
    method constructor (line 14) | constructor(
    method execute (line 21) | async execute(command: CreateOrderCommand): Promise<string> {

FILE: apps/api/src/modules/order/application/event-handlers/order-confirmed.handler.ts
  class OrderConfirmedHandler (line 6) | class OrderConfirmedHandler implements IEventHandler<OrderConfirmedEvent> {
    method handle (line 9) | async handle(event: OrderConfirmedEvent) {

FILE: apps/api/src/modules/order/application/event-handlers/order-created.handler.ts
  class OrderCreatedHandler (line 6) | class OrderCreatedHandler implements IEventHandler<OrderCreatedEvent> {
    method handle (line 9) | async handle(event: OrderCreatedEvent) {

FILE: apps/api/src/modules/order/application/queries/get-order/get-order.handler.ts
  class GetOrderHandler (line 9) | class GetOrderHandler implements IQueryHandler<GetOrderQuery> {
    method constructor (line 10) | constructor(
    method execute (line 15) | async execute(query: GetOrderQuery): Promise<OrderResponseDto> {

FILE: apps/api/src/modules/order/application/queries/get-order/get-order.query.ts
  class GetOrderQuery (line 1) | class GetOrderQuery {
    method constructor (line 2) | constructor(public readonly orderId: string) {}

FILE: apps/api/src/modules/order/application/queries/get-order/order.response.dto.ts
  class OrderItemResponseDto (line 3) | class OrderItemResponseDto {
  class OrderResponseDto (line 20) | class OrderResponseDto {

FILE: apps/api/src/modules/order/application/queries/list-orders/list-orders.handler.ts
  class ListOrdersHandler (line 8) | class ListOrdersHandler implements IQueryHandler<ListOrdersQuery> {
    method constructor (line 9) | constructor(
    method execute (line 14) | async execute(query: ListOrdersQuery): Promise<OrderListResponseDto> {

FILE: apps/api/src/modules/order/application/queries/list-orders/list-orders.query.ts
  class ListOrdersQuery (line 1) | class ListOrdersQuery {
    method constructor (line 2) | constructor(public readonly customerId?: string) {}

FILE: apps/api/src/modules/order/application/queries/list-orders/order-list.response.dto.ts
  class OrderListResponseDto (line 4) | class OrderListResponseDto {

FILE: apps/api/src/modules/order/domain/entities/order-item.entity.ts
  type OrderItemProps (line 5) | interface OrderItemProps {
  class OrderItem (line 12) | class OrderItem extends Entity<string> {
    method productId (line 17) | get productId(): string {
    method quantity (line 21) | get quantity(): Quantity {
    method unitPrice (line 25) | get unitPrice(): Money {
    method constructor (line 29) | private constructor(props: OrderItemProps) {
    method create (line 36) | public static create(props: OrderItemProps): OrderItem {
    method getTotalPrice (line 40) | public getTotalPrice(): Money {
    method updateQuantity (line 44) | public updateQuantity(quantity: Quantity): void {

FILE: apps/api/src/modules/order/domain/entities/order.entity.ts
  type OrderProps (line 11) | interface OrderProps {
  class Order (line 20) | class Order extends AggregateRoot<string> {
    method customerId (line 27) | get customerId(): string {
    method items (line 31) | get items(): OrderItem[] {
    method status (line 35) | get status(): OrderStatus {
    method createdAt (line 39) | get createdAt(): Date {
    method updatedAt (line 43) | get updatedAt(): Date {
    method constructor (line 47) | private constructor(props: OrderProps) {
    method create (line 56) | public static create(customerId: string, items: OrderItem[], id?: Orde...
    method reconstitute (line 74) | public static reconstitute(props: OrderProps): Order {
    method getTotalAmount (line 78) | public getTotalAmount(): Money {
    method confirm (line 86) | public confirm(): void {
    method cancel (line 97) | public cancel(): void {
    method addItem (line 108) | public addItem(item: OrderItem): void {
    method removeItem (line 117) | public removeItem(itemId: string): void {

FILE: apps/api/src/modules/order/domain/events/order-cancelled.event.ts
  class OrderCancelledEvent (line 3) | class OrderCancelledEvent extends DomainEvent {
    method constructor (line 4) | constructor(public readonly orderId: string) {
    method getAggregateId (line 8) | getAggregateId(): string {

FILE: apps/api/src/modules/order/domain/events/order-confirmed.event.ts
  class OrderConfirmedEvent (line 3) | class OrderConfirmedEvent extends DomainEvent {
    method constructor (line 4) | constructor(public readonly orderId: string) {
    method getAggregateId (line 8) | getAggregateId(): string {

FILE: apps/api/src/modules/order/domain/events/order-created.event.ts
  class OrderCreatedEvent (line 4) | class OrderCreatedEvent extends DomainEvent {
    method constructor (line 5) | constructor(
    method getAggregateId (line 13) | getAggregateId(): string {

FILE: apps/api/src/modules/order/domain/exceptions/invalid-order-state.exception.ts
  class InvalidOrderStateException (line 3) | class InvalidOrderStateException extends DomainException {
    method constructor (line 4) | constructor(message: string) {

FILE: apps/api/src/modules/order/domain/exceptions/order-not-found.exception.ts
  class OrderNotFoundException (line 3) | class OrderNotFoundException extends DomainException {
    method constructor (line 4) | constructor(orderId: string) {

FILE: apps/api/src/modules/order/domain/repositories/order.repository.interface.ts
  type IOrderRepository (line 3) | interface IOrderRepository {
  constant ORDER_REPOSITORY (line 10) | const ORDER_REPOSITORY = Symbol('ORDER_REPOSITORY');

FILE: apps/api/src/modules/order/domain/services/order-pricing.service.ts
  class OrderPricingService (line 6) | class OrderPricingService {
    method calculateTotal (line 7) | calculateTotal(order: Order): Money {
    method applyDiscount (line 11) | applyDiscount(total: Money, discountPercent: number): Money {

FILE: apps/api/src/modules/order/domain/value-objects/money.vo.ts
  type MoneyProps (line 4) | interface MoneyProps {
  class Money (line 9) | class Money extends ValueObject<MoneyProps> {
    method amount (line 10) | get amount(): number {
    method currency (line 14) | get currency(): string {
    method constructor (line 18) | private constructor(props: MoneyProps) {
    method create (line 22) | public static create(amount: number, currency: string = 'USD'): Money {
    method add (line 35) | public add(money: Money): Money {
    method multiply (line 42) | public multiply(multiplier: number): Money {

FILE: apps/api/src/modules/order/domain/value-objects/order-id.vo.ts
  type OrderIdProps (line 4) | interface OrderIdProps {
  class OrderId (line 8) | class OrderId extends ValueObject<OrderIdProps> {
    method value (line 9) | get value(): string {
    method constructor (line 13) | private constructor(props: OrderIdProps) {
    method create (line 17) | public static create(id?: string): OrderId {
    method toString (line 21) | public toString(): string {

FILE: apps/api/src/modules/order/domain/value-objects/order-status.vo.ts
  type OrderStatusEnum (line 3) | enum OrderStatusEnum {
  type OrderStatusProps (line 10) | interface OrderStatusProps {
  class OrderStatus (line 14) | class OrderStatus extends ValueObject<OrderStatusProps> {
    method value (line 15) | get value(): OrderStatusEnum {
    method constructor (line 19) | private constructor(props: OrderStatusProps) {
    method create (line 23) | public static create(status: OrderStatusEnum): OrderStatus {
    method pending (line 27) | public static pending(): OrderStatus {
    method confirmed (line 31) | public static confirmed(): OrderStatus {
    method cancelled (line 35) | public static cancelled(): OrderStatus {
    method completed (line 39) | public static completed(): OrderStatus {
    method isPending (line 43) | public isPending(): boolean {
    method isConfirmed (line 47) | public isConfirmed(): boolean {
    method isCancelled (line 51) | public isCancelled(): boolean {
    method isCompleted (line 55) | public isCompleted(): boolean {

FILE: apps/api/src/modules/order/domain/value-objects/quantity.vo.ts
  type QuantityProps (line 3) | interface QuantityProps {
  class Quantity (line 7) | class Quantity extends ValueObject<QuantityProps> {
    method value (line 8) | get value(): number {
    method constructor (line 12) | private constructor(props: QuantityProps) {
    method create (line 16) | public static create(value: number): Quantity {

FILE: apps/api/src/modules/order/infrastructure/cache/order-cache.service.ts
  constant ORDER_CACHE_PREFIX (line 5) | const ORDER_CACHE_PREFIX = 'order:';
  constant ORDER_LIST_CACHE_PREFIX (line 6) | const ORDER_LIST_CACHE_PREFIX = 'orders:customer:';
  constant DEFAULT_TTL (line 7) | const DEFAULT_TTL = 3600;
  class OrderCacheService (line 10) | class OrderCacheService {
    method constructor (line 13) | constructor(private readonly redisson: RedissonService) {}
    method cacheOrder (line 18) | async cacheOrder(order: Order, ttl: number = DEFAULT_TTL): Promise<voi...
    method getCachedOrder (line 28) | async getCachedOrder(orderId: string): Promise<SerializedOrder | null> {
    method invalidateOrder (line 36) | async invalidateOrder(orderId: string): Promise<void> {
    method cacheCustomerOrders (line 45) | async cacheCustomerOrders(
    method getCachedCustomerOrders (line 59) | async getCachedCustomerOrders(customerId: string): Promise<SerializedO...
    method invalidateCustomerOrders (line 67) | async invalidateCustomerOrders(customerId: string): Promise<void> {
    method getOrSetOrder (line 76) | async getOrSetOrder(
    method withOrderLock (line 97) | async withOrderLock<T>(
    method incrementOrderViewCount (line 110) | async incrementOrderViewCount(orderId: string): Promise<number> {
    method getOrderViewCount (line 118) | async getOrderViewCount(orderId: string): Promise<number> {
    method serializeOrder (line 124) | private serializeOrder(order: Order): SerializedOrder {
  type SerializedOrder (line 143) | interface SerializedOrder {
  type SerializedOrderItem (line 153) | interface SerializedOrderItem {

FILE: apps/api/src/modules/order/infrastructure/persistence/kysely-order.repository.ts
  class OrderRepository (line 13) | class OrderRepository implements IOrderRepository {
    method constructor (line 14) | constructor(private readonly db: KyselyService<Database>) {}
    method findById (line 16) | async findById(id: string): Promise<Order | null> {
    method findByCustomerId (line 36) | async findByCustomerId(customerId: string): Promise<Order[]> {
    method save (line 58) | async save(order: Order): Promise<Order> {
    method delete (line 125) | async delete(id: string): Promise<void> {
    method mapStatusToDb (line 139) | private mapStatusToDb(status: OrderStatusEnum): 'pending' | 'confirmed...
    method mapStatusFromDb (line 152) | private mapStatusFromDb(status: string): OrderStatusEnum {
    method toDomain (line 165) | private toDomain(

FILE: apps/api/src/modules/order/order.module.ts
  class OrderModule (line 42) | class OrderModule {}

FILE: apps/api/src/modules/order/presentation/order.controller.ts
  class OrderController (line 15) | class OrderController {
    method constructor (line 16) | constructor(
    method createOrder (line 24) | async createOrder(@Body() dto: CreateOrderDto): Promise<{ orderId: str...
    method getOrder (line 33) | async getOrder(@Param('id') id: string): Promise<OrderResponseDto> {
    method listOrders (line 41) | async listOrders(@Query('customerId') customerId?: string): Promise<Or...
    method confirmOrder (line 50) | async confirmOrder(@Param('id') id: string): Promise<void> {
    method cancelOrder (line 59) | async cancelOrder(@Param('id') id: string): Promise<void> {

FILE: apps/api/src/shared/api-response/api-response.dto.ts
  class ApiResponseDto (line 7) | class ApiResponseDto<T> {
    method constructor (line 23) | constructor(partial: Partial<ApiResponseDto<T>>) {
  class PaginatedResponseDto (line 29) | class PaginatedResponseDto<T> {
    method constructor (line 51) | constructor(partial: Partial<PaginatedResponseDto<T>>) {
  class ApiErrorResponseDto (line 56) | class ApiErrorResponseDto {
    method constructor (line 72) | constructor(partial: Partial<ApiErrorResponseDto>) {

FILE: apps/api/src/shared/api-response/api-response.interceptor.ts
  class ApiResponseInterceptor (line 17) | class ApiResponseInterceptor implements NestInterceptor {
    method intercept (line 18) | intercept(context: ExecutionContext, next: CallHandler): Observable<un...

FILE: apps/api/src/shared/api-response/api-response.service.ts
  class ApiResponseService (line 14) | class ApiResponseService {
    method success (line 20) | success<T>(data?: T, message = 'Success', requestId?: string): ApiResp...
    method created (line 32) | created<T>(data?: T, message = 'Created', requestId?: string): ApiResp...
    method accepted (line 44) | accepted<T>(data?: T, message = 'Accepted', requestId?: string): ApiRe...
    method noContent (line 56) | noContent(requestId?: string): ApiResponseDto<null> {
    method paginated (line 67) | paginated<T>(
    method error (line 91) | error(
    method getRequestId (line 108) | getRequestId(request: Request): string | undefined {

FILE: apps/api/src/shared/api-versioning/api-versioning.decorator.ts
  constant API_VERSION_KEY (line 7) | const API_VERSION_KEY = 'api_version';

FILE: apps/api/src/shared/api-versioning/api-versioning.interceptor.ts
  type ApiVersionOptions (line 9) | interface ApiVersionOptions {
  constant DEFAULT_API_VERSION (line 21) | const DEFAULT_API_VERSION = '1';
  function extractVersionFromUrl (line 27) | function extractVersionFromUrl(url: string): string | null {
  function extractVersionFromHeader (line 36) | function extractVersionFromHeader(header: string | string[] | undefined)...
  function extractVersionFromCustomHeader (line 48) | function extractVersionFromCustomHeader(
  class ApiVersioningInterceptor (line 57) | class ApiVersioningInterceptor implements NestInterceptor {
    method intercept (line 58) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...

FILE: apps/api/src/shared/application/dto.base.ts
  method constructor (line 2) | constructor(partial?: Partial<any>) {

FILE: apps/api/src/shared/application/query.interface.ts
  type IQuery (line 1) | interface IQuery<IResponse> {

FILE: apps/api/src/shared/application/use-case.interface.ts
  type IUseCase (line 1) | interface IUseCase<IRequest, IResponse> {

FILE: apps/api/src/shared/audit/audit.decorator.ts
  constant AUDIT_KEY (line 7) | const AUDIT_KEY = 'audit';

FILE: apps/api/src/shared/audit/audit.interceptor.ts
  constant AUDIT_ACTION_KEY (line 17) | const AUDIT_ACTION_KEY = 'audit_action';
  constant AUDIT_RESOURCE_KEY (line 18) | const AUDIT_RESOURCE_KEY = 'audit_resource';
  function AuditedAction (line 23) | function AuditedAction(action: string) {
  function AuditedResource (line 33) | function AuditedResource(resource: string) {
  class AuditInterceptor (line 41) | class AuditInterceptor implements NestInterceptor {
    method constructor (line 44) | constructor(private readonly auditService: AuditService) {}
    method intercept (line 46) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...
    method getAction (line 103) | private getAction(context: ExecutionContext): string | undefined {
    method getResource (line 110) | private getResource(context: ExecutionContext): string | undefined {
    method inferAction (line 117) | private inferAction(context: ExecutionContext): string {
    method inferResource (line 133) | private inferResource(context: ExecutionContext): string {
    method getClientIp (line 138) | private getClientIp(request: Request): string {

FILE: apps/api/src/shared/audit/audit.service.ts
  type AuditLogEntry (line 9) | interface AuditLogEntry {
  type AuditQueryOptions (line 25) | interface AuditQueryOptions {
  type PaginatedAuditResult (line 36) | interface PaginatedAuditResult {
  class AuditService (line 47) | class AuditService {
    method constructor (line 51) | constructor(private readonly kysely: KyselyService<any>) {}
    method log (line 56) | async log(entry: AuditLogEntry): Promise<void> {
    method logCreate (line 85) | async logCreate(
    method logUpdate (line 110) | async logUpdate(
    method logDelete (line 144) | async logDelete(
    method logFailure (line 169) | async logFailure(
    method query (line 191) | async query(options: AuditQueryOptions): Promise<PaginatedAuditResult> {
    method getById (line 246) | async getById(id: string, organizationId: string): Promise<AuditLogEnt...

FILE: apps/api/src/shared/auth/auth.module.ts
  class AuthModule (line 29) | class AuthModule {}

FILE: apps/api/src/shared/auth/dto/auth.dto.ts
  class LoginDto (line 11) | class LoginDto {
  class RegisterDto (line 25) | class RegisterDto {
  class RefreshTokenDto (line 55) | class RefreshTokenDto {
  class ChangePasswordDto (line 64) | class ChangePasswordDto {
  class ForgotPasswordDto (line 78) | class ForgotPasswordDto {
  class ResetPasswordDto (line 87) | class ResetPasswordDto {
  class VerifyEmailDto (line 101) | class VerifyEmailDto {
  class TokenResponseDto (line 110) | class TokenResponseDto {
  class UserResponseDto (line 127) | class UserResponseDto {

FILE: apps/api/src/shared/auth/guards/jwt-auth.guard.ts
  constant IS_PUBLIC_KEY (line 19) | const IS_PUBLIC_KEY = 'isPublic';
  class JwtAuthGuard (line 27) | class JwtAuthGuard implements CanActivate {
    method constructor (line 28) | constructor(private readonly jwtService: JwtService) {}
    method canActivate (line 30) | async canActivate(context: ExecutionContext): Promise<boolean> {
    method extractTokenFromHeader (line 51) | private extractTokenFromHeader(request: Request): string | undefined {

FILE: apps/api/src/shared/auth/guards/permissions.guard.ts
  constant PERMISSIONS_KEY (line 19) | const PERMISSIONS_KEY = 'permissions';
  class PermissionsGuard (line 32) | class PermissionsGuard implements CanActivate {
    method constructor (line 33) | constructor(
    method canActivate (line 38) | canActivate(context: ExecutionContext): boolean {

FILE: apps/api/src/shared/auth/guards/roles.guard.ts
  constant ROLES_KEY (line 19) | const ROLES_KEY = 'roles';
  class RolesGuard (line 30) | class RolesGuard implements CanActivate {
    method constructor (line 31) | constructor(
    method canActivate (line 36) | canActivate(context: ExecutionContext): boolean {

FILE: apps/api/src/shared/auth/jwt/jwt.service.ts
  class JwtService (line 11) | class JwtService {
    method constructor (line 17) | constructor(private readonly configService: ConfigService) {
    method generateAccessToken (line 27) | generateAccessToken(payload: JwtPayload): string {
    method generateRefreshToken (line 36) | generateRefreshToken(payload: JwtPayload): string {
    method generateTokenPair (line 45) | generateTokenPair(payload: JwtPayload): TokenPair {
    method verifyAccessToken (line 55) | verifyAccessToken(token: string): JwtPayload {
    method verifyRefreshToken (line 66) | verifyRefreshToken(token: string): JwtPayload {
    method decodeToken (line 77) | decodeToken(token: string): JwtPayload | null {
    method isTokenExpiringSoon (line 88) | isTokenExpiringSoon(token: string): boolean {

FILE: apps/api/src/shared/auth/jwt/jwt.types.ts
  type JwtPayload (line 8) | interface JwtPayload {
  type AccessToken (line 30) | interface AccessToken {
  type RefreshToken (line 38) | interface RefreshToken {
  type TokenPair (line 46) | interface TokenPair {
  type TokenResponse (line 54) | interface TokenResponse {

FILE: apps/api/src/shared/auth/rbac/rbac.service.ts
  type Permission (line 11) | interface Permission {
  type Role (line 19) | interface Role {
  constant DEFAULT_PERMISSIONS (line 27) | const DEFAULT_PERMISSIONS: Permission[] = [
  constant DEFAULT_ROLES (line 38) | const DEFAULT_ROLES: Role[] = [
  class RbacService (line 75) | class RbacService {
    method constructor (line 78) | constructor() {
    method getRole (line 88) | getRole(roleName: string): Role | undefined {
    method hasPermission (line 95) | hasPermission(roleName: string, resource: string, action: string): boo...
    method hasAnyPermission (line 112) | hasAnyPermission(roleNames: string[], resource: string, action: string...
    method hasAllPermissions (line 119) | hasAllPermissions(roleNames: string[], resource: string, action: strin...
    method getPermissions (line 126) | getPermissions(roleName: string): Permission[] {
    method registerRole (line 134) | registerRole(role: Role): void {
    method hasRole (line 141) | hasRole(userRoles: string[], requiredRole: string): boolean {
    method hasAnyRole (line 148) | hasAnyRole(userRoles: string[], requiredRoles: string[]): boolean {

FILE: apps/api/src/shared/base/base.entity.ts
  type BaseEntity (line 5) | interface BaseEntity {
  type TenantEntity (line 11) | interface TenantEntity extends BaseEntity {
  type SoftDeleteEntity (line 15) | interface SoftDeleteEntity extends BaseEntity {
  type VersionedEntity (line 20) | interface VersionedEntity extends BaseEntity {

FILE: apps/api/src/shared/base/base.service.ts
  type FindOptions (line 9) | interface FindOptions<FilterDto, SortDto> {
  type PaginatedResult (line 15) | interface PaginatedResult<T> {
  method constructor (line 31) | constructor(
  method create (line 39) | async create(dto: CreateDto, additionalData?: Partial<Entity>): Promise<...
  method findById (line 61) | async findById(id: string): Promise<Entity | null> {
  method findByIdOrThrow (line 73) | async findByIdOrThrow(id: string): Promise<Entity> {
  method findAll (line 84) | async findAll(options?: FindOptions<FilterDto, SortDto>): Promise<Pagina...
  method findAllCursor (line 125) | async findAllCursor(
  method update (line 157) | async update(id: string, dto: UpdateDto): Promise<Entity> {
  method updateMany (line 180) | async updateMany(filter: FilterDto, dto: Partial<UpdateDto>): Promise<nu...
  method delete (line 196) | async delete(id: string): Promise<void> {
  method softDelete (line 208) | async softDelete(id: string, deletedBy?: string): Promise<void> {
  method softDeleteMany (line 225) | async softDeleteMany(filter: FilterDto, deletedBy?: string): Promise<num...
  method exists (line 242) | async exists(id: string): Promise<boolean> {
  method count (line 255) | async count(filter?: FilterDto): Promise<number> {
  method createMany (line 272) | async createMany(dtos: CreateDto[]): Promise<Entity[]> {
  method applyFilters (line 295) | protected applyFilters(qb: any, filter: FilterDto): any {
  method applySort (line 303) | protected applySort(qb: any, sort: SortDto): any {

FILE: apps/api/src/shared/base/pagination.dto.ts
  class PaginationQueryDto (line 9) | class PaginationQueryDto {
  class CursorPaginationQueryDto (line 26) | class CursorPaginationQueryDto {
  type PaginationOptions (line 47) | interface PaginationOptions {
  type CursorPaginationOptions (line 54) | interface CursorPaginationOptions {
  function parsePaginationOptions (line 60) | function parsePaginationOptions(query: PaginationQueryDto): PaginationOp...
  function parseCursorPaginationOptions (line 69) | function parseCursorPaginationOptions(query: CursorPaginationQueryDto): ...
  class PaginatedResponseDto (line 77) | class PaginatedResponseDto<T> {
    method constructor (line 99) | constructor(partial: Partial<PaginatedResponseDto<T>>) {
  class CursorPaginatedResponseDto (line 104) | class CursorPaginatedResponseDto<T> {
    method constructor (line 114) | constructor(partial: Partial<CursorPaginatedResponseDto<T>>) {

FILE: apps/api/src/shared/cache/cache.decorator.ts
  constant CACHE_KEY (line 8) | const CACHE_KEY = 'cache_options';
  function Cache (line 13) | function Cache(options: CacheDecoratorOptions) {

FILE: apps/api/src/shared/cache/cache.interceptor.ts
  class CacheInterceptor (line 19) | class CacheInterceptor implements NestInterceptor {
    method constructor (line 22) | constructor(
    method intercept (line 27) | async intercept(context: ExecutionContext, next: CallHandler): Promise...
    method buildCacheKey (line 60) | private buildCacheKey(context: ExecutionContext, options: CacheDecorat...

FILE: apps/api/src/shared/cache/cache.service.ts
  type CacheOptions (line 8) | interface CacheOptions {
  type CacheStats (line 17) | interface CacheStats {
  type CacheDecoratorOptions (line 28) | interface CacheDecoratorOptions extends CacheOptions {
  constant DEFAULT_CACHE_OPTIONS (line 38) | const DEFAULT_CACHE_OPTIONS: Required<CacheOptions> = {
  class CacheService (line 48) | class CacheService implements OnModuleDestroy {
    method constructor (line 53) | constructor(private readonly redis: RedissonService) {}
    method get (line 58) | async get<T>(key: string, options?: CacheOptions): Promise<T | null> {
    method set (line 83) | async set<T>(key: string, value: T, options?: CacheOptions): Promise<v...
    method delete (line 95) | async delete(key: string, options?: CacheOptions): Promise<void> {
    method deleteByPattern (line 104) | async deleteByPattern(pattern: string): Promise<number> {
    method has (line 114) | async has(key: string, options?: CacheOptions): Promise<boolean> {
    method getOrSet (line 122) | async getOrSet<T>(
    method getMany (line 140) | async getMany<T>(keys: string[], options?: CacheOptions): Promise<Arra...
    method setMany (line 148) | async setMany<T>(entries: Array<{ key: string; value: T }>, options?: ...
    method increment (line 156) | async increment(key: string, amount = 1, options?: CacheOptions): Prom...
    method decrement (line 168) | async decrement(key: string, amount = 1, options?: CacheOptions): Prom...
    method getStats (line 177) | getStats(): CacheStats {
    method resetStats (line 188) | resetStats(): void {
    method buildKey (line 195) | private buildKey(key: string, options?: CacheOptions): string {
    method onModuleDestroy (line 200) | onModuleDestroy(): void {

FILE: apps/api/src/shared/circuit-breaker/circuit-breaker.decorator.ts
  constant CIRCUIT_BREAKER_KEY (line 8) | const CIRCUIT_BREAKER_KEY = 'circuit_breaker';
  constant CIRCUIT_BREAKER_OPTIONS (line 9) | const CIRCUIT_BREAKER_OPTIONS = 'circuit_breaker_options';
  function CircuitBreaker (line 14) | function CircuitBreaker(options: CircuitBreakerOptions) {

FILE: apps/api/src/shared/circuit-breaker/circuit-breaker.module.ts
  class CircuitBreakerModule (line 13) | class CircuitBreakerModule {}

FILE: apps/api/src/shared/circuit-breaker/circuit-breaker.service.ts
  type CircuitState (line 7) | enum CircuitState {
  type CircuitBreakerOptions (line 13) | interface CircuitBreakerOptions {
  type CircuitBreakerStats (line 24) | interface CircuitBreakerStats {
  constant DEFAULT_OPTIONS (line 37) | const DEFAULT_OPTIONS: Required<CircuitBreakerOptions> = {
  class CircuitBreakerService (line 51) | class CircuitBreakerService implements OnModuleDestroy {
    method constructor (line 55) | constructor() {}
    method getCircuitBreaker (line 60) | getCircuitBreaker(name: string, options?: CircuitBreakerOptions): Circ...
    method execute (line 76) | async execute<T>(
    method getAllStats (line 100) | getAllStats(): CircuitBreakerStats[] {
    method getStats (line 107) | getStats(name: string): CircuitBreakerStats | undefined {
    method reset (line 114) | reset(name: string): void {
    method resetAll (line 121) | resetAll(): void {
    method onModuleDestroy (line 127) | onModuleDestroy(): void {
  class CircuitBreaker (line 135) | class CircuitBreaker {
    method constructor (line 145) | constructor(name: string, options: Required<CircuitBreakerOptions>) {
    method canExecute (line 153) | canExecute(): boolean {
    method recordSuccess (line 175) | recordSuccess(): void {
    method recordFailure (line 189) | recordFailure(): void {
    method getStats (line 206) | getStats(): CircuitBreakerStats {
    method getNextAttempt (line 221) | getNextAttempt(): Date | undefined {
    method reset (line 228) | reset(): void {
    method transitionToOpen (line 240) | private transitionToOpen(): void {
    method transitionToHalfOpen (line 249) | private transitionToHalfOpen(): void {
    method transitionToClosed (line 257) | private transitionToClosed(): void {
  class CircuitBreakerOpenError (line 268) | class CircuitBreakerOpenError extends Error {
    method constructor (line 269) | constructor(

FILE: apps/api/src/shared/database/database.module.ts
  class DatabaseModule (line 34) | class DatabaseModule { }

FILE: apps/api/src/shared/database/database.types.ts
  type Database (line 6) | interface Database {
  type OrderTable (line 14) | interface OrderTable {
  type OrderItemTable (line 26) | interface OrderItemTable {
  type Order (line 39) | type Order = Selectable<OrderTable>;
  type NewOrder (line 40) | type NewOrder = Insertable<OrderTable>;
  type OrderUpdate (line 41) | type OrderUpdate = Updateable<OrderTable>;
  type OrderItem (line 43) | type OrderItem = Selectable<OrderItemTable>;
  type NewOrderItem (line 44) | type NewOrderItem = Insertable<OrderItemTable>;
  type OrderItemUpdate (line 45) | type OrderItemUpdate = Updateable<OrderItemTable>;

FILE: apps/api/src/shared/domain/aggregate-root.ts
  method domainEvents (line 7) | get domainEvents(): DomainEvent[] {
  method addDomainEvent (line 11) | protected addDomainEvent(domainEvent: DomainEvent): void {
  method clearEvents (line 15) | public clearEvents(): void {

FILE: apps/api/src/shared/domain/domain-event-publisher.ts
  type IDomainEventPublisher (line 3) | interface IDomainEventPublisher {
  constant DOMAIN_EVENT_PUBLISHER (line 8) | const DOMAIN_EVENT_PUBLISHER = Symbol('DOMAIN_EVENT_PUBLISHER');

FILE: apps/api/src/shared/domain/domain-event.ts
  type IDomainEvent (line 1) | interface IDomainEvent {
  method constructor (line 9) | constructor() {

FILE: apps/api/src/shared/domain/entity.ts
  method constructor (line 12) | constructor(id: T) {
  method id (line 16) | get id(): T {
  method equals (line 23) | equals(entity?: Entity<T>): boolean {
  method equalsById (line 42) | equalsById(id: T): boolean {
  method toString (line 49) | toString(): string {
  method toObject (line 56) | toObject(): { id: T } {
  type IAuditableEntity (line 64) | interface IAuditableEntity {
  type ISoftDeletable (line 74) | interface ISoftDeletable {
  method constructor (line 88) | constructor(
  method isCreatedAfter (line 105) | isCreatedAfter(date: Date): boolean {
  method isUpdatedAfter (line 112) | isUpdatedAfter(date: Date): boolean {
  method isUpdatedBy (line 119) | isUpdatedBy(userId: string): boolean {
  method constructor (line 131) | constructor(
  method isDeleted (line 148) | isDeleted(): boolean {
  method isDeletedBy (line 155) | isDeletedBy(userId: string): boolean {
  method daysSinceDeletion (line 162) | daysSinceDeletion(): number | null {

FILE: apps/api/src/shared/domain/value-object.ts
  type ValueObjectProps (line 1) | interface ValueObjectProps {
  method constructor (line 8) | constructor(props: T) {
  method equals (line 12) | public equals(vo?: ValueObject<T>): boolean {

FILE: apps/api/src/shared/errors/business.exception.ts
  type BusinessExceptionOptions (line 8) | interface BusinessExceptionOptions {
  class BusinessException (line 15) | class BusinessException extends HttpException {
    method constructor (line 19) | constructor(options: BusinessExceptionOptions) {
    method getResponse (line 35) | getResponse(): Record<string, unknown> {
  class ValidationException (line 48) | class ValidationException extends BusinessException {
    method constructor (line 49) | constructor(message: string, details?: Record<string, unknown>) {
  class NotFoundException (line 58) | class NotFoundException extends BusinessException {
    method constructor (line 59) | constructor(resource: string, identifier?: string | number) {
  class DuplicateEntryException (line 72) | class DuplicateEntryException extends BusinessException {
    method constructor (line 73) | constructor(resource: string, field: string, value: string) {
  class ForbiddenException (line 82) | class ForbiddenException extends BusinessException {
    method constructor (line 83) | constructor(message = 'You do not have permission to perform this acti...
  class UnauthorizedException (line 92) | class UnauthorizedException extends BusinessException {
    method constructor (line 93) | constructor(message = 'Authentication required') {
  class OperationFailedException (line 102) | class OperationFailedException extends BusinessException {
    method constructor (line 103) | constructor(operation: string, reason?: string) {

FILE: apps/api/src/shared/errors/error-codes.ts
  type ErrorCode (line 5) | enum ErrorCode {

FILE: apps/api/src/shared/errors/error.filter.ts
  type ErrorResponse (line 17) | interface ErrorResponse {
  class GlobalErrorFilter (line 28) | class GlobalErrorFilter implements ExceptionFilter {
    method catch (line 31) | catch(exception: unknown, host: ArgumentsHost): void {
    method getErrorCode (line 143) | private getErrorCode(status: number): string {

FILE: apps/api/src/shared/feature-flags/feature-flags.guard.ts
  constant FEATURE_FLAG_KEY (line 15) | const FEATURE_FLAG_KEY = 'feature_flag';
  class FeatureFlagsGuard (line 24) | class FeatureFlagsGuard implements CanActivate {
    method constructor (line 25) | constructor(
    method canActivate (line 30) | async canActivate(context: ExecutionContext): Promise<boolean> {

FILE: apps/api/src/shared/feature-flags/feature-flags.service.ts
  type FeatureFlag (line 8) | interface FeatureFlag {
  type FeatureFlagEvaluation (line 26) | interface FeatureFlagEvaluation {
  constant DEFAULT_FEATURE_FLAGS (line 35) | const DEFAULT_FEATURE_FLAGS: Record<string, Omit<FeatureFlag, 'name' | '...
  class FeatureFlagsService (line 46) | class FeatureFlagsService implements OnModuleDestroy {
    method constructor (line 51) | constructor(private readonly redis: RedissonService) {}
    method isEnabled (line 56) | async isEnabled(flagName: string): Promise<boolean> {
    method evaluate (line 64) | async evaluate(
    method getFlag (line 125) | async getFlag(flagName: string): Promise<FeatureFlag | null> {
    method enable (line 167) | async enable(flagName: string, metadata?: Record<string, unknown>): Pr...
    method disable (line 187) | async disable(flagName: string): Promise<void> {
    method setRollout (line 207) | async setRollout(flagName: string, percentage: number): Promise<void> {
    method setExpiration (line 227) | async setExpiration(flagName: string, expiresAt: Date): Promise<void> {
    method getAllFlags (line 248) | async getAllFlags(): Promise<FeatureFlag[]> {
    method saveFlag (line 285) | private async saveFlag(flag: FeatureFlag): Promise<void> {
    method hashUserId (line 294) | private hashUserId(userId: string, flagName: string): number {
    method onModuleDestroy (line 305) | onModuleDestroy(): void {

FILE: apps/api/src/shared/file-upload/file-upload.decorator.ts
  constant FILE_UPLOAD_KEY (line 7) | const FILE_UPLOAD_KEY = 'file_upload';

FILE: apps/api/src/shared/file-upload/file-upload.interceptor.ts
  type UploadedFile (line 16) | interface UploadedFile {
  type FileUploadMetadata (line 25) | interface FileUploadMetadata {
  class FileUploadInterceptor (line 42) | class FileUploadInterceptor implements NestInterceptor {
    method constructor (line 43) | constructor(private readonly fileUploadService: FileUploadService) {}
    method intercept (line 45) | async intercept(context: ExecutionContext, next: CallHandler): Promise...
    method extractFiles (line 86) | private extractFiles(request: any): UploadedFile[] {
  class SingleFileUploadInterceptor (line 112) | class SingleFileUploadInterceptor implements NestInterceptor {
    method constructor (line 113) | constructor(private readonly fileUploadService: FileUploadService) {}
    method intercept (line 115) | async intercept(context: ExecutionContext, next: CallHandler): Promise...

FILE: apps/api/src/shared/file-upload/file-upload.service.ts
  type UploadedFile (line 8) | interface UploadedFile {
  type FileValidationOptions (line 25) | interface FileValidationOptions {
  constant DEFAULT_FILE_VALIDATION (line 37) | const DEFAULT_FILE_VALIDATION: FileValidationOptions = {
  class FileUploadService (line 54) | class FileUploadService {
    method constructor (line 57) | constructor(private readonly rustFsService: RustFSService) {}
    method uploadFile (line 62) | async uploadFile(
    method uploadFiles (line 95) | async uploadFiles(
    method deleteFile (line 112) | async deleteFile(storedName: string): Promise<void> {
    method getFileUrl (line 121) | async getFileUrl(storedName: string, expiresInSeconds = 3600): Promise...
    method fileExists (line 129) | async fileExists(storedName: string): Promise<boolean> {
    method validateFile (line 137) | private validateFile(
    method generateStoredName (line 171) | private generateStoredName(originalName: string): string {
    method getExtension (line 181) | private getExtension(filename: string): string {

FILE: apps/api/src/shared/health/health.controller.ts
  class HealthController (line 17) | class HealthController {
    method constructor (line 18) | constructor(
    method check (line 29) | async check(): Promise<HealthCheckResult> {
    method live (line 39) | live(): { status: string } {
    method ready (line 48) | async ready(): Promise<HealthCheckResult> {

FILE: apps/api/src/shared/health/health.module.ts
  class HealthModule (line 23) | class HealthModule {}

FILE: apps/api/src/shared/health/indicators/database.indicator.ts
  class DatabaseHealthIndicator (line 10) | class DatabaseHealthIndicator extends HealthIndicator {
    method constructor (line 11) | constructor(private readonly kysely: KyselyService) {
    method isHealthy (line 15) | async isHealthy(key: string): Promise<HealthIndicatorResult> {

FILE: apps/api/src/shared/health/indicators/nats.indicator.ts
  class NatsHealthIndicator (line 11) | class NatsHealthIndicator extends HealthIndicator {
    method constructor (line 12) | constructor(private readonly nats: IMessagingService) {
    method isHealthy (line 16) | async isHealthy(key: string): Promise<HealthIndicatorResult> {

FILE: apps/api/src/shared/health/indicators/redis.indicator.ts
  class RedisHealthIndicator (line 10) | class RedisHealthIndicator extends HealthIndicator {
    method constructor (line 11) | constructor(private readonly redis: RedissonService) {
    method isHealthy (line 15) | async isHealthy(key: string): Promise<HealthIndicatorResult> {

FILE: apps/api/src/shared/health/indicators/rustfs.indicator.ts
  class RustFSHealthIndicator (line 11) | class RustFSHealthIndicator extends HealthIndicator {
    method constructor (line 12) | constructor(private readonly rustfs: IStorageService) {
    method isHealthy (line 16) | async isHealthy(key: string): Promise<HealthIndicatorResult> {

FILE: apps/api/src/shared/infrastructure/messaging/event-bus.interface.ts
  type IEventBus (line 3) | interface IEventBus {
  constant EVENT_BUS (line 8) | const EVENT_BUS = Symbol('EVENT_BUS');

FILE: apps/api/src/shared/infrastructure/messaging/event-bus.service.ts
  class EventBusService (line 7) | class EventBusService implements IEventBus {
    method constructor (line 8) | constructor(private readonly eventBus: NestEventBus) {}
    method publish (line 10) | async publish(event: DomainEvent): Promise<void> {
    method publishAll (line 14) | async publishAll(events: DomainEvent[]): Promise<void> {

FILE: apps/api/src/shared/infrastructure/messaging/messaging.interface.ts
  type ISubscription (line 8) | interface ISubscription {
  type IMessagingService (line 16) | interface IMessagingService {

FILE: apps/api/src/shared/infrastructure/persistence/repository.interface.ts
  type IRepository (line 1) | interface IRepository<T> {

FILE: apps/api/src/shared/infrastructure/persistence/unit-of-work.interface.ts
  type IUnitOfWork (line 1) | interface IUnitOfWork {

FILE: apps/api/src/shared/infrastructure/storage/storage.interface.ts
  type IStorageService (line 7) | interface IStorageService {
  constant STORAGE_SERVICE (line 81) | const STORAGE_SERVICE = Symbol('STORAGE_SERVICE');

FILE: apps/api/src/shared/metrics/metrics.controller.ts
  class MetricsController (line 11) | class MetricsController {
    method constructor (line 12) | constructor(private readonly metricsService: MetricsService) {}
    method getMetrics (line 18) | getMetrics(): string {
    method getMetricsJson (line 25) | getMetricsJson(): Record<string, unknown> {

FILE: apps/api/src/shared/metrics/metrics.interceptor.ts
  class MetricsInterceptor (line 18) | class MetricsInterceptor implements NestInterceptor {
    method constructor (line 21) | constructor(private readonly metricsService: MetricsService) {}
    method intercept (line 23) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...
    method recordMetrics (line 49) | private recordMetrics(request: Request, response: Response, duration: ...
    method normalizePath (line 75) | private normalizePath(path: string): string {
    method getDuration (line 86) | private getDuration(startTime: bigint): number {

FILE: apps/api/src/shared/metrics/metrics.service.ts
  type CounterMetric (line 10) | interface CounterMetric {
  type GaugeMetric (line 19) | interface GaugeMetric {
  type HistogramMetric (line 28) | interface HistogramMetric {
  constant DEFAULT_HISTOGRAM_BUCKETS (line 38) | const DEFAULT_HISTOGRAM_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, ...
  constant DEFAULT_SIZE_BUCKETS (line 43) | const DEFAULT_SIZE_BUCKETS = [100, 1000, 10000, 100000, 1000000, 10000000];
  class MetricsService (line 49) | class MetricsService implements OnModuleDestroy {
    method constructor (line 79) | constructor() {
    method initializeMetrics (line 83) | private initializeMetrics(): void {
    method getKey (line 105) | private getKey(name: string, labelNames?: string[]): string {
    method getLabelValues (line 109) | private getLabelValues(labelNames: string[], labels: Record<string, st...
    method incCounter (line 120) | incCounter(name: string, labels?: Record<string, string>, amount = 1):...
    method getCounter (line 129) | getCounter(name: string, labels?: Record<string, string>): number {
    method setGauge (line 141) | setGauge(name: string, value: number, labels?: Record<string, string>)...
    method incGauge (line 149) | incGauge(name: string, labels?: Record<string, string>, amount = 1): v...
    method decGauge (line 158) | decGauge(name: string, labels?: Record<string, string>, amount = 1): v...
    method getGauge (line 167) | getGauge(name: string, labels?: Record<string, string>): number {
    method observeHistogram (line 179) | observeHistogram(name: string, value: number, labels?: Record<string, ...
    method getHistogram (line 189) | getHistogram(name: string, labels?: Record<string, string>): number[] {
    method getHistogramStats (line 197) | getHistogramStats(name: string, labels?: Record<string, string>): { co...
    method recordHttpRequest (line 226) | recordHttpRequest(method: string, path: string, status: number, durati...
    method recordDbQuery (line 238) | recordDbQuery(operation: string, table: string, duration: number): void {
    method recordCacheHit (line 246) | recordCacheHit(cache: string): void {
    method recordCacheMiss (line 253) | recordCacheMiss(cache: string): void {
    method toPrometheusFormat (line 264) | toPrometheusFormat(): string {
    method toJSON (line 298) | toJSON(): Record<string, unknown> {
    method onModuleDestroy (line 308) | onModuleDestroy(): void {

FILE: apps/api/src/shared/openapi/openapi-common.dto.ts
  class PaginationParamsDto (line 7) | class PaginationParamsDto {
  class IdParamDto (line 15) | class IdParamDto {
  class SlugParamDto (line 20) | class SlugParamDto {
  class CreatedAtFilterDto (line 25) | class CreatedAtFilterDto {
  class StatusFilterDto (line 33) | class StatusFilterDto {
  class SearchQueryDto (line 38) | class SearchQueryDto {

FILE: apps/api/src/shared/openapi/openapi-decorators.ts
  function ApiAuth (line 22) | function ApiAuth(summary?: string) {
  function ApiPermission (line 39) | function ApiPermission(resource: string, action: string, summary?: strin...
  function ApiStandardResponse (line 59) | function ApiStandardResponse<T>(options: {
  function ApiCreatedResponse (line 97) | function ApiCreatedResponse<T>(options: {
  function ApiNoContentResponse (line 108) | function ApiNoContentResponse(summary?: string) {
  function ApiPaginatedResponse (line 122) | function ApiPaginatedResponse<T>(options: {
  function ApiBadRequestResponse (line 167) | function ApiBadRequestResponse(description = 'Bad Request - Invalid inpu...
  function ApiNotFoundResponse (line 184) | function ApiNotFoundResponse(resource = 'Resource') {
  function ApiConflictResponse (line 200) | function ApiConflictResponse(description = 'Conflict - Resource already ...
  function ApiServerErrorResponse (line 216) | function ApiServerErrorResponse() {

FILE: apps/api/src/shared/presentation/filters/domain-exception.filter.ts
  class DomainException (line 4) | class DomainException extends Error {
    method constructor (line 5) | constructor(message: string) {
  class DomainExceptionFilter (line 13) | class DomainExceptionFilter implements ExceptionFilter {
    method catch (line 16) | catch(exception: DomainException, host: ArgumentsHost) {

FILE: apps/api/src/shared/presentation/filters/http-exception.filter.ts
  class HttpExceptionFilter (line 5) | class HttpExceptionFilter implements ExceptionFilter {
    method catch (line 8) | catch(exception: HttpException, host: ArgumentsHost) {

FILE: apps/api/src/shared/presentation/interceptors/logging.interceptor.ts
  class LoggingInterceptor (line 6) | class LoggingInterceptor implements NestInterceptor {
    method intercept (line 9) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...

FILE: apps/api/src/shared/rate-limiting/rate-limiting.decorator.ts
  type RateLimitOptions (line 11) | interface RateLimitOptions {
  constant RATE_LIMIT_KEY (line 18) | const RATE_LIMIT_KEY = 'rate_limit_config';

FILE: apps/api/src/shared/rate-limiting/rate-limiting.guard.ts
  constant RATE_LIMIT_KEY (line 17) | const RATE_LIMIT_KEY = 'rate_limit';
  constant RATE_LIMIT_CONFIG_KEY (line 18) | const RATE_LIMIT_CONFIG_KEY = 'rate_limit_config';
  type RateLimitMetadata (line 20) | interface RateLimitMetadata {
  class RateLimitingGuard (line 35) | class RateLimitingGuard implements CanActivate {
    method constructor (line 36) | constructor(
    method canActivate (line 41) | async canActivate(context: ExecutionContext): Promise<boolean> {
    method getIdentifier (line 85) | private getIdentifier(request: Request): string {
    method getClientIp (line 99) | private getClientIp(request: Request): string {

FILE: apps/api/src/shared/rate-limiting/rate-limiting.service.ts
  type RateLimitConfig (line 8) | interface RateLimitConfig {
  type RateLimitResult (line 17) | interface RateLimitResult {
  constant DEFAULT_RATE_LIMITS (line 27) | const DEFAULT_RATE_LIMITS: Record<string, RateLimitConfig> = {
  class RateLimitingService (line 41) | class RateLimitingService implements OnModuleDestroy {
    method constructor (line 45) | constructor(private readonly redis: RedissonService) {}
    method checkLimit (line 50) | async checkLimit(
    method checkLimitLocal (line 107) | private checkLimitLocal(
    method resetLimit (line 150) | async resetLimit(identifier: string): Promise<void> {
    method getUsage (line 159) | async getUsage(identifier: string): Promise<{ count: number; resetAt: ...
    method onModuleDestroy (line 180) | onModuleDestroy(): void {
  class RateLimitExceededException (line 188) | class RateLimitExceededException extends HttpException {
    method constructor (line 189) | constructor(retryAfter: number) {

FILE: apps/api/src/shared/redis/redis.module.ts
  class RedisModule (line 25) | class RedisModule {}

FILE: apps/api/src/shared/retry/retry.module.ts
  class RetryModule (line 13) | class RetryModule {}

FILE: apps/api/src/shared/retry/retry.service.ts
  type RetryOptions (line 7) | interface RetryOptions {
  type RetryResult (line 24) | interface RetryResult<T> {
  constant DEFAULT_OPTIONS (line 35) | const DEFAULT_OPTIONS: Required<Omit<RetryOptions, 'onRetry' | 'retryabl...
  constant DEFAULT_RETRYABLE_HTTP_CODES (line 47) | const DEFAULT_RETRYABLE_HTTP_CODES = [408, 429, 500, 502, 503, 504];
  class RetryService (line 53) | class RetryService {
    method constructor (line 56) | constructor() {}
    method execute (line 61) | async execute<T>(
    method executeOrThrow (line 126) | async executeOrThrow<T>(
    method isRetryable (line 146) | private isRetryable(error: Error, options: { retryableErrors: any[]; i...
    method calculateDelay (line 163) | private calculateDelay(attempt: number, options: { initialDelay: numbe...
    method calculateJitter (line 171) | private calculateJitter(delay: number): number {
    method sleep (line 179) | private sleep(ms: number): Promise<void> {
  class RetryExhaustedError (line 187) | class RetryExhaustedError extends Error {
    method constructor (line 188) | constructor(
  type RetryDecoratorOptions (line 201) | interface RetryDecoratorOptions extends RetryOptions {
  function Retry (line 209) | function Retry(options: RetryDecoratorOptions = {}) {

FILE: apps/api/src/shared/serialization/example.ts
  type UserEntity (line 10) | interface UserEntity {
  class UserDto (line 26) | class UserDto {
  class CreateUserDto (line 58) | class CreateUserDto {
  class UpdateUserDto (line 75) | class UpdateUserDto {
  function userToDto (line 90) | function userToDto(user: UserEntity): UserDto {
  function userToCreateDto (line 107) | function userToCreateDto(user: UserEntity): CreateUserDto {
  function userListToDto (line 119) | function userListToDto(users: UserEntity[]): UserDto[] {
  class UserSerializer (line 129) | class UserSerializer extends Serializer<UserEntity, UserDto> {
    method instance (line 132) | static get instance(): UserSerializer {
    method toDto (line 136) | toDto(user: UserEntity): UserDto {
  class CreateUserSerializer (line 151) | class CreateUserSerializer extends Serializer<UserEntity, CreateUserDto> {
    method instance (line 154) | static get instance(): CreateUserSerializer {
    method toDto (line 158) | toDto(user: UserEntity): CreateUserDto {

FILE: apps/api/src/shared/serialization/serializer.ts
  method toDtoList (line 49) | toDtoList(entities: Entity[]): Dto[] {
  type ClassType (line 58) | type ClassType<T = any> = new (...args: any[]) => T;
  function transformToInstance (line 63) | function transformToInstance<T>(
  function transformToPlain (line 77) | function transformToPlain<T>(entity: T, options?: { excludeExtraneousVal...
  function transformListToInstance (line 86) | function transformListToInstance<T>(
  function transformListToPlain (line 100) | function transformListToPlain<T>(entities: T[]): Record<string, unknown>...
  type Mapper (line 107) | type Mapper<Entity, Dto> = (entity: Entity) => Dto;
  function toDto (line 112) | function toDto<Entity, Dto>(mapper: Mapper<Entity, Dto>): Mapper<Entity,...
  function toDtoList (line 119) | function toDtoList<Entity, Dto>(mapper: Mapper<Entity, Dto>): (entities:...

FILE: apps/api/src/shared/tenant/tenant.guard.ts
  class TenantGuard (line 18) | class TenantGuard implements CanActivate {
    method constructor (line 19) | constructor(private readonly tenantService: TenantService) {}
    method canActivate (line 21) | canActivate(context: ExecutionContext): boolean {
  class OptionalTenantGuard (line 44) | class OptionalTenantGuard implements CanActivate {
    method constructor (line 45) | constructor(private readonly tenantService: TenantService) {}
    method canActivate (line 47) | canActivate(context: ExecutionContext): boolean {

FILE: apps/api/src/shared/tenant/tenant.interceptor.ts
  class TenantInterceptor (line 15) | class TenantInterceptor implements NestInterceptor {
    method constructor (line 16) | constructor(private readonly tenantService: TenantService) {}
    method intercept (line 18) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...

FILE: apps/api/src/shared/tenant/tenant.service.ts
  type TenantContext (line 12) | interface TenantContext {
  class TenantService (line 23) | class TenantService {
    method constructor (line 26) | constructor(@Optional() @Inject(REQUEST) private readonly request: Req...
    method setContext (line 31) | setContext(context: TenantContext): void {
    method getOrganizationId (line 38) | getOrganizationId(): string {
    method getUserId (line 48) | getUserId(): string | undefined {
    method getContext (line 55) | getContext(): TenantContext | null {
    method hasContext (line 62) | hasContext(): boolean {
    method getMetadata (line 69) | getMetadata<T>(key: string): T | undefined {
  class TenantStorage (line 77) | class TenantStorage {
    method set (line 80) | static set(context: TenantContext): void {
    method get (line 84) | static get(): TenantContext | null {
    method clear (line 88) | static clear(): void {

FILE: apps/api/src/shared/testing/testing.utils.ts
  function createTestingModule (line 11) | async function createTestingModule(options: {
  function createMock (line 37) | function createMock<T>(overrides?: Partial<T>): jest.Mocked<T> {
  function createMockInstance (line 53) | function createMockInstance<T>(classType: new (...args: any[]) => T): je...
  class FixtureBuilder (line 73) | class FixtureBuilder<T> {
    method constructor (line 76) | constructor(private defaultData: T) {
    method with (line 80) | with<K extends keyof T>(key: K, value: T[K]): this {
    method withPartial (line 85) | withPartial(partial: Partial<T>): this {
    method build (line 90) | build(): T {
    method buildMany (line 94) | buildMany(count: number): T[] {
  function fixture (line 102) | function fixture<T>(defaultData: T): FixtureBuilder<T> {
  function createPaginatedFixture (line 109) | function createPaginatedFixture<T>(items: T[], total: number, page = 1, ...
  function createMockRequest (line 136) | function createMockRequest(overrides?: Partial<Request>): Request {
  method freeze (line 152) | freeze(date: Date = new Date()): void {
  method useReal (line 158) | useReal(): void {
  method advance (line 163) | advance(ms: number): void {
  method setFuture (line 168) | setFuture(days = 1): Date {
  function createMockResponse (line 179) | function createMockResponse() {
  function createMockQueryBuilder (line 192) | function createMockQueryBuilder() {
  function createMockRedis (line 215) | function createMockRedis() {

FILE: apps/api/src/shared/tracking/tracking.interceptor.ts
  type TrackingContext (line 18) | interface TrackingContext {
  class TrackingInterceptor (line 27) | class TrackingInterceptor implements NestInterceptor {
    method intercept (line 28) | intercept(context: ExecutionContext, next: CallHandler): Observable<un...
  function getTrackingContext (line 67) | function getTrackingContext(): TrackingContext | undefined {
  function getRequestId (line 72) | function getRequestId(): string | undefined {
  function getCorrelationId (line 77) | function getCorrelationId(): string | undefined {

FILE: apps/api/src/shared/transform/transform.interceptor.ts
  type TransformOptions (line 16) | interface TransformOptions {
  type ResponseMetadata (line 30) | interface ResponseMetadata {
  class TransformInterceptor (line 42) | class TransformInterceptor implements NestInterceptor {
    method constructor (line 46) | constructor(options: TransformOptions = {}) {
    method intercept (line 55) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...
    method buildMetadata (line 91) | private buildMetadata(request: Request, response: Response, duration: ...
    method wrapResponse (line 104) | private wrapResponse(data: any, metadata: ResponseMetadata): any {
    method isPrimitive (line 119) | private isPrimitive(value: any): boolean {
    method isWrappedResponse (line 129) | private isWrappedResponse(value: any): boolean {
    method getDuration (line 139) | private getDuration(startTime: bigint): bigint {
  function transformKeysToCamelCase (line 147) | function transformKeysToCamelCase<T>(obj: any): T {
  function transformKeysToSnakeCase (line 168) | function transformKeysToSnakeCase<T>(obj: any): T {
  class KeyTransformInterceptor (line 190) | class KeyTransformInterceptor implements NestInterceptor {
    method constructor (line 191) | constructor(
    method intercept (line 195) | intercept(context: ExecutionContext, next: CallHandler): Observable<an...

FILE: apps/api/src/shared/utils/guard.ts
  class Guard (line 1) | class Guard {
    method againstNullOrUndefined (line 2) | public static againstNullOrUndefined(argument: any, argumentName: stri...
    method againstNullOrUndefinedBulk (line 9) | public static againstNullOrUndefinedBulk(args: GuardArgument[]): Result {
    method isOneOf (line 17) | public static isOneOf(value: any, validValues: any[], argumentName: st...
    method inRange (line 37) | public static inRange(num: number, min: number, max: number, argumentN...
    method allInRange (line 48) | public static allInRange(numbers: number[], min: number, max: number, ...
  type GuardArgument (line 62) | interface GuardArgument {
  type Result (line 67) | interface Result {

FILE: apps/api/src/shared/utils/result.ts
  class Result (line 9) | class Result<T> {
    method constructor (line 15) | private constructor(isSuccess: boolean, error: string | null, value: T...
    method getValue (line 27) | getValue(): T {
    method getValueOrElse (line 37) | getValueOrElse(defaultValue: T): T {
    method getValueOrUndefined (line 44) | getValueOrUndefined(): T | undefined {
    method map (line 51) | map<U>(fn: (value: T) => U): Result<U> {
    method mapAsync (line 61) | async mapAsync<U>(fn: (value: T) => Promise<U>): Promise<Result<U>> {
    method flatMap (line 71) | flatMap<U>(fn: (value: T) => Result<U>): Result<U> {
    method flatMapAsync (line 81) | async flatMapAsync<U>(fn: (value: T) => Promise<Result<U>>): Promise<R...
    method fold (line 91) | fold<U>(onSuccess: (value: T) => U, onFailure: (error: string) => U): U {
    method foldAsync (line 101) | async foldAsync<U>(
    method tap (line 114) | tap(fn: (value: T) => void): Result<T> {
    method tapAsync (line 124) | async tapAsync(fn: (value: T) => Promise<void>): Promise<Result<T>> {
    method contains (line 134) | contains(value: T): boolean {
    method existsError (line 141) | existsError(predicate: (error: string) => boolean): boolean {
    method ok (line 149) | static ok<U>(value?: U): Result<U> {
    method fail (line 153) | static fail<U>(error: string): Result<U> {
    method fromTry (line 160) | static fromTry<U>(fn: () => U): Result<U> {
    method fromTryAsync (line 171) | static async fromTryAsync<U>(fn: () => Promise<U>): Promise<Result<U>> {
    method combine (line 182) | static combine<T extends Result<any>[]>(...results: T): Result<{ [K in...
    method combineAll (line 201) | static combineAll<T>(...results: Array<Result<T>>): Result<T[]> {
  type UnwrapResult (line 224) | type UnwrapResult<T> = T extends Result<infer U> ? U : T;
  type VoidResult (line 229) | type VoidResult = Result<null>;

FILE: apps/api/src/shared/validation/validation-options.ts
  function IsPassword (line 53) | function IsPassword(options?: { minLength?: number; ValidationOptions?: ...
  function IsStrongPassword (line 80) | function IsStrongPassword(options?: { minLength?: number; ValidationOpti...
  function IsUsername (line 108) | function IsUsername(options?: { minLength?: number; maxLength?: number; ...
  function IsSlug (line 133) | function IsSlug(options?: { maxLength?: number; ValidationOptions?: Vali...
  function IsJsonString (line 157) | function IsJsonString(ValidationOptions?: ValidationOptions) {
  function IsObjectId (line 188) | function IsObjectId(ValidationOptions?: ValidationOptions) {
  function IsPrefixedId (line 210) | function IsPrefixedId(prefix: string, ValidationOptions?: ValidationOpti...
  function IsNonEmptyArray (line 237) | function IsNonEmptyArray(ValidationOptions?: ValidationOptions) {
  function IsUniqueArray (line 258) | function IsUniqueArray(ValidationOptions?: ValidationOptions) {
  function IsIso8601Date (line 284) | function IsIso8601Date(ValidationOptions?: ValidationOptions) {
  function IsFutureDate (line 291) | function IsFutureDate(ValidationOptions?: ValidationOptions) {
  function IsPastDate (line 314) | function IsPastDate(ValidationOptions?: ValidationOptions) {
  function IsInRange (line 341) | function IsInRange(min: number, max: number, ValidationOptions?: Validat...
  function IsLengthInRange (line 362) | function IsLengthInRange(min: number, max: number, ValidationOptions?: V...
  function MatchesField (line 388) | function MatchesField(
  function IsInstanceOf (line 418) | function IsInstanceOf<T extends new (...args: unknown[]) => unknown>(
  function IsArrayOf (line 442) | function IsArrayOf<T>(

FILE: apps/api/src/shared/validation/validation.pipe.ts
  constant DEFAULT_VALIDATOR_OPTIONS (line 15) | const DEFAULT_VALIDATOR_OPTIONS: ValidatorOptions = {
  constant DEFAULT_TRANSFORM_OPTIONS (line 24) | const DEFAULT_TRANSFORM_OPTIONS = {
  function formatValidationErrors (line 31) | function formatValidationErrors(
  function createValidationPipe (line 62) | function createValidationPipe(

FILE: packages/bullmq/src/bullmq.module-definition.ts
  constant MODULE_OPTIONS_TOKEN (line 8) | const MODULE_OPTIONS_TOKEN = 'BULLMQ_MODULE_OPTIONS';

FILE: packages/bullmq/src/bullmq.module.ts
  class BullMQModule (line 14) | class BullMQModule {
    method register (line 18) | static register(options: BullMQModuleOptions): DynamicModule {
    method registerAsync (line 34) | static registerAsync(options: {

FILE: packages/bullmq/src/bullmq.service.ts
  type JobData (line 9) | interface JobData {
  type JobResult (line 13) | interface JobResult {
  type JobProcessor (line 19) | type JobProcessor<T extends JobData = JobData> = (job: Job<T>) => Promis...
  type QueueMetrics (line 24) | interface QueueMetrics {
  class BullMQService (line 33) | class BullMQService implements OnModuleDestroy {
    method constructor (line 39) | constructor(private readonly options: BullMQModuleOptions) {}
    method getQueue (line 44) | getQueue(name: string): Queue {
    method addJob (line 63) | async addJob<T extends JobData>(
    method addDelayedJob (line 92) | async addDelayedJob<T extends JobData>(
    method addRepeatableJob (line 104) | async addRepeatableJob<T extends JobData>(
    method createWorker (line 118) | createWorker<T extends JobData = JobData>(
    method getQueueEvents (line 166) | getQueueEvents(queueName: string): QueueEvents {
    method getQueueMetrics (line 182) | async getQueueMetrics(queueName: string): Promise<QueueMetrics> {
    method pauseQueue (line 199) | async pauseQueue(queueName: string): Promise<void> {
    method resumeQueue (line 208) | async resumeQueue(queueName: string): Promise<void> {
    method drainQueue (line 217) | async drainQueue(queueName: string): Promise<void> {
    method cleanQueue (line 226) | async cleanQueue(
    method removeJob (line 238) | async removeJob(queueName: string, jobId: string): Promise<void> {
    method getJob (line 249) | async getJob(queueName: string, jobId: string): Promise<Job | undefine...
    method onModuleDestroy (line 257) | async onModuleDestroy(): Promise<void> {

FILE: packages/bullmq/src/bullmq.types.ts
  type BullMQModuleOptions (line 5) | interface BullMQModuleOptions {
  type BullMQOptionsFactory (line 27) | interface BullMQOptionsFactory {

FILE: packages/etcd/src/config.service.ts
  type ConfigSubscriber (line 4) | type ConfigSubscriber = (value: unknown) => void;
  class EtcdConfigService (line 10) | class EtcdConfigService implements OnModuleInit {
    method constructor (line 17) | constructor(private readonly etcd: EtcdService) {}
    method onModuleInit (line 19) | async onModuleInit() {
    method get (line 25) | async get<T = string>(key: string, useCache = true): Promise<T | null> {
    method getJSON (line 40) | async getJSON<T = unknown>(key: string, useCache = true): Promise<T | ...
    method getByPrefix (line 55) | async getByPrefix<T = unknown>(prefix: string): Promise<Map<string, T>> {
    method set (line 61) | async set(key: string, value: string | number | boolean | object, opti...
    method setJSON (line 66) | async setJSON<T extends object>(key: string, value: T, options?: { ttl...
    method delete (line 72) | async delete(key: string): Promise<boolean> {
    method deleteByPrefix (line 78) | async deleteByPrefix(prefix: string): Promise<number> {
    method subscribe (line 90) | subscribe<T = string>(key: string, callback: (value: T) => void): () =...
    method subscribePrefix (line 125) | subscribePrefix<T = string>(prefix: string, callback: (event: { key: s...
    method exists (line 135) | async exists(key: string): Promise<boolean> {
    method clearCache (line 139) | clearCache(): void {
    method setCacheTtl (line 143) | setCacheTtl(ttl: number): void {

FILE: packages/etcd/src/etcd.module.ts
  class EtcdModule (line 11) | class EtcdModule {
    method register (line 12) | static register(options: EtcdModuleOptions): DynamicModule {
    method registerAsync (line 25) | static registerAsync(options: {

FILE: packages/etcd/src/etcd.service.ts
  class EtcdService (line 6) | class EtcdService implements OnModuleInit, OnModuleDestroy {
    method constructor (line 11) | constructor(private readonly options: EtcdModuleOptions) {
    method onModuleInit (line 28) | async onModuleInit() {
    method onModuleDestroy (line 42) | async onModuleDestroy() {
    method get (line 58) | async get<T = string>(key: string): Promise<T | null> {
    method getJSON (line 69) | async getJSON<T = unknown>(key: string): Promise<T | null> {
    method set (line 79) | async set(key: string, value: string | number | boolean | object, opti...
    method delete (line 92) | async delete(key: string): Promise<boolean> {
    method deleteByPrefix (line 97) | async deleteByPrefix(prefix: string): Promise<number> {
    method exists (line 102) | async exists(key: string): Promise<boolean> {
    method getKeysByPrefix (line 106) | async getKeysByPrefix(prefix: string): Promise<string[]> {
    method getEntries (line 112) | async getEntries<T = string>(prefix: string): Promise<ConfigEntry<T>[]> {
    method getEntriesAsJSON (line 129) | async getEntriesAsJSON<T = unknown>(prefix: string): Promise<Map<strin...
    method createLease (line 146) | async createLease(ttl: number): Promise<LeaseInfo> {
    method grantLease (line 152) | async grantLease(ttl: number): Promise<string> {
    method keepAlive (line 157) | async keepAlive(leaseId: string): Promise<void> {
    method revokeLease (line 162) | async revokeLease(leaseId: string): Promise<void> {
    method watch (line 168) | watch<T = string>(key: string, callback: WatchCallback<T>): () => void {
    method watchPrefix (line 207) | watchPrefix<T = string>(prefix: string, callback: WatchCallback<T>): (...
    method healthCheck (line 248) | async healthCheck(): Promise<HealthResult> {
    method getMembers (line 261) | async getMembers(): Promise<string[]> {
    method getLeader (line 266) | async getLeader(): Promise<string | null> {
    method compareAndSet (line 277) | async compareAndSet(
    method getClient (line 301) | getClient(): Etcd3 {

FILE: packages/etcd/src/etcd.types.ts
  type EtcdModuleOptions (line 4) | interface EtcdModuleOptions {
  type WatchEventType (line 28) | type WatchEventType = 'put' | 'delete';
  type WatchEvent (line 33) | interface WatchEvent<T = unknown> {
  type WatchCallback (line 44) | type WatchCallback<T = unknown> = (event: WatchEvent<T>) => void;
  type ConfigEntry (line 49) | interface ConfigEntry<T = unknown> {
  type LeaseInfo (line 60) | interface LeaseInfo {
  type HealthResult (line 69) | interface HealthResult {

FILE: packages/kysely/src/kysely-module-options.interface.ts
  type KyselyModuleOptions (line 4) | interface KyselyModuleOptions<DB = unknown> {
  type KyselyModuleOptionsFactory (line 12) | interface KyselyModuleOptionsFactory<DB = unknown> {
  type KyselyModuleAsyncOptions (line 18) | interface KyselyModuleAsyncOptions<DB = unknown>

FILE: packages/kysely/src/kysely.logger.ts
  constant SQL_KEYWORDS (line 12) | const SQL_KEYWORDS = [

FILE: packages/kysely/src/kysely.module.ts
  class KyselyModule (line 10) | class KyselyModule extends ConfigurableModuleClass {
    method register (line 11) | static register(options: typeof OPTIONS_TYPE): DynamicModule {
    method registerAsync (line 20) | static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {

FILE: packages/kysely/src/kysely.service.ts
  class KyselyService (line 7) | class KyselyService<T> extends Kysely<T> implements OnModuleDestroy {
    method constructor (line 13) | constructor(
    method onModuleDestroy (line 29) | async onModuleDestroy(): Promise<void> {

FILE: packages/logger/src/logger.module.ts
  class LoggerModule (line 12) | class LoggerModule extends ConfigurableModuleClass {
    method register (line 13) | static register(options: typeof OPTIONS_TYPE) {
    method registerAsync (line 31) | static registerAsync(options: typeof ASYNC_OPTIONS_TYPE) {

FILE: packages/logger/src/logger.service.ts
  class LoggerServiceImpl (line 15) | class LoggerServiceImpl implements NestLoggerService {
    method constructor (line 20) | constructor(options: LoggerModuleOptions = {}) {
    method log (line 61) | log(levelOrMessage: string | LogLevel, messageOrContext?: string | Log...
    method fatal (line 77) | fatal(message: string, context?: Partial<LogContext>): void {
    method error (line 83) | error(errorOrMessage: Error | string, context?: Partial<LogContext>): ...
    method warn (line 91) | warn(message: string, context?: Partial<LogContext>): void {
    method info (line 95) | info(message: string, context?: Partial<LogContext>): void {
    method debug (line 99) | debug(message: string, context?: Partial<LogContext>): void {
    method trace (line 103) | trace(message: string, context?: Partial<LogContext>): void {
    method verbose (line 107) | verbose(message: string, context?: Partial<LogContext>): void {
    method child (line 115) | child(context: Partial<LogContext>): LoggerService {
    method getRequestContext (line 130) | static getRequestContext(): LogContext | undefined {
    method runWithContext (line 134) | static runWithContext<T>(context: LogContext, fn: () => T): T {
    method setRequestContext (line 138) | static setRequestContext(context: LogContext): void {
    method logRequest (line 146) | logRequest(options: {
    method logAtLevel (line 179) | private logAtLevel(level: LogLevel, message: string, context?: Partial...
    method logError (line 190) | private logError(error: Error, context?: Partial<LogContext>): void {
    method getMergedContext (line 207) | private getMergedContext(context?: Partial<LogContext>): Record<string...
    method isLogLevel (line 216) | private isLogLevel(value: string): value is LogLevel {

FILE: packages/logger/src/logger.types.ts
  type LoggerModuleOptions (line 5) | interface LoggerModuleOptions {
  type LogLevel (line 14) | type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' ...
  type LogContext (line 16) | interface LogContext {
  type LogEntry (line 30) | interface LogEntry {
  type ErrorLog (line 40) | interface ErrorLog {
  type RequestLoggingOptions (line 48) | interface RequestLoggingOptions {
  type LogInterceptorOptions (line 56) | interface LogInterceptorOptions {

FILE: packages/logger/src/logging.interceptor.ts
  class LoggingInterceptor (line 15) | class LoggingInterceptor implements NestInterceptor {
    method constructor (line 23) | constructor(
    method intercept (line 30) | intercept(context: ExecutionContext, next: CallHandler): Observable<un...
    method isExcludedPath (line 85) | private isExcludedPath(url: string): boolean {
    method getClientIp (line 91) | private getClientIp(request: Request): string {

FILE: packages/nats/src/nats.module.ts
  class NatsModule (line 10) | class NatsModule extends ConfigurableModuleClass {
    method register (line 11) | static register(options: typeof OPTIONS_TYPE): DynamicModule {
    method registerAsync (line 20) | static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {

FILE: packages/nats/src/nats.service.ts
  type Subscription (line 21) | interface Subscription {
  class NatsServiceImpl (line 30) | class NatsServiceImpl implements OnModuleInit, OnModuleDestroy {
    method constructor (line 38) | constructor(private readonly options: NatsPackageOptions) {
    method onModuleInit (line 46) | async onModuleInit() {
    method onModuleDestroy (line 50) | async onModuleDestroy() {
    method connect (line 58) | private async connect(): Promise<void> {
    method disconnect (line 124) | private async disconnect(): Promise<void> {
    method getConnection (line 143) | async getConnection(): Promise<NatsConnection> {
    method getJetStream (line 150) | async getJetStream(): Promise<JetStreamClient> {
    method getState (line 157) | getState(): NatsConnectionState {
    method isHealthy (line 161) | async isHealthy(): Promise<boolean> {
    method publish (line 176) | async publish(options: PublishOptions): Promise<void> {
    method pubsub (line 195) | async pubsub(subject: string, data: object): Promise<void> {
    method request (line 206) | async request(options: RequestOptions): Promise<NatsMessage> {
    method request$ (line 226) | async request$<T>(subject: string, data?: object): Promise<T> {
    method subscribe (line 239) | async subscribe(
    method subscribe$ (line 276) | async subscribe$(
    method unsubscribe (line 289) | unsubscribe(subscription: Subscription): void {
    method handleSubscription (line 297) | private async handleSubscription(
    method jsPublish (line 326) | async jsPublish(options: JetStreamPublishOptions): Promise<PubAck> {
    method jsSubscribe (line 360) | async jsSubscribe(
    method encodeData (line 416) | private encodeData(data?: Uint8Array | string | object): Uint8Array {
    method decodeData (line 432) | private decodeData(data: Uint8Array): unknown {
    method convertMessage (line 449) | private convertMessage(msg: Msg): NatsMessage {

FILE: packages/nats/src/nats.types.ts
  type NatsPackageOptions (line 7) | interface NatsPackageOptions {
  type NatsModuleOptions (line 24) | type NatsModuleOptions = NatsPackageOptions;
  type TlsOptions (line 26) | interface TlsOptions {
  type AuthOptions (line 33) | interface AuthOptions {
  type JetStreamOptions (line 39) | interface JetStreamOptions {
  type NatsConnectionState (line 45) | interface NatsConnectionState {
  type PublishOptions (line 56) | interface PublishOptions {
  type RequestOptions (line 64) | interface RequestOptions extends PublishOptions {
  type PubAckPromise (line 69) | interface PubAckPromise {
  type SubscribeOptions (line 79) | interface SubscribeOptions {
  type SubscriptionConfig (line 88) | interface SubscriptionConfig {
  type DeliverPolicy (line 101) | type DeliverPolicy =
  type AckPolicy (line 110) | type AckPolicy =
  type ReplayPolicy (line 116) | type ReplayPolicy =
  type NatsMessage (line 122) | interface NatsMessage {
  type SubscriptionHandler (line 131) | interface SubscriptionHandler {
  type JetStreamPublishOptions (line 139) | interface JetStreamPublishOptions {
  type JetStreamSubscribeOptions (line 148) | interface JetStreamSubscribeOptions extends SubscribeOptions {
  type StreamSubscriptionConfig (line 154) | interface StreamSubscriptionConfig extends SubscriptionConfig {
  type StreamInfo (line 167) | interface StreamInfo {
  type StreamConfig (line 174) | interface StreamConfig {
  type RetentionPolicy (line 190) | type RetentionPolicy =
  type StorageType (line 195) | type StorageType =
  type StreamState (line 199) | interface StreamState {
  type ClusterInfo (line 210) | interface ClusterInfo {
  type PeerInfo (line 215) | interface PeerInfo {
  class NatsError (line 227) | class NatsError extends Error {
    method constructor (line 228) | constructor(
  class NatsConnectionError (line 238) | class NatsConnectionError extends NatsError {
    method constructor (line 239) | constructor(server: string, reason?: string) {
  class NatsPublishError (line 248) | class NatsPublishError extends NatsError {
    method constructor (line 249) | constructor(subject: string, reason?: string) {
  class NatsSubscribeError (line 258) | class NatsSubscribeError extends NatsError {
    method constructor (line 259) | constructor(subject: string, reason?: string) {
  class NatsRequestError (line 268) | class NatsRequestError extends NatsError {
    method constructor (line 269) | constructor(subject: string, reason?: string) {

FILE: packages/redisson/src/redisson-module-options.interface.ts
  type RedissonModuleOptions (line 3) | interface RedissonModuleOptions extends IRedissonConfig {}

FILE: packages/redisson/src/redisson.module.ts
  class RedissonModule (line 6) | class RedissonModule extends ConfigurableModuleClass {
    method register (line 7) | static register(options: typeof OPTIONS_TYPE): DynamicModule {
    method registerAsync (line 16) | static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {

FILE: packages/redisson/src/redisson.service.ts
  class RedissonService (line 7) | class RedissonService extends Redisson implements OnModuleInit, OnModule...
    method constructor (line 10) | constructor(
    method onModuleInit (line 20) | async onModuleInit() {
    method onModuleDestroy (line 31) | async onModuleDestroy() {
    method withLock (line 48) | async withLock<T>(key: string, callback: () => Promise<T> | T, waitTim...
    method getOrSet (line 72) | async getOrSet<T>(key: string, factory: () => Promise<T> | T, ttl?: nu...
    method deleteByPattern (line 103) | async deleteByPattern(pattern: string): Promise<number> {
    method setJSON (line 121) | async setJSON<T>(key: string, value: T, ttl?: number): Promise<void> {
    method getJSON (line 136) | async getJSON<T>(key: string): Promise<T | null> {
    method exists (line 156) | async exists(key: string): Promise<boolean> {
    method expire (line 167) | async expire(key: string, ttl: number): Promise<boolean> {
    method delete (line 177) | async delete(...keys: string[]): Promise<number> {
    method increment (line 187) | async increment(key: string, increment = 1): Promise<number> {
    method decrement (line 197) | async decrement(key: string, decrement = 1): Promise<number> {
    method get (line 206) | async get(key: string): Promise<string | null> {
    method set (line 216) | async set(key: string, value: string, ttl?: number): Promise<void> {
    method hset (line 230) | async hset(key: string, field: string, value: string): Promise<void> {
    method hget (line 240) | async hget(key: string, field: string): Promise<string | null> {
    method hgetall (line 249) | async hgetall(key: string): Promise<Record<string, string>> {
    method hdel (line 259) | async hdel(key: string, ...fields: string[]): Promise<number> {
    method eval (line 270) | async eval(script: string, keys: string[], args: string[]): Promise<an...
    method evalsha (line 282) | async evalsha(sha1: string, keys: string[], args: string[]): Promise<a...
    method scriptLoad (line 292) | async scriptLoad(script: string): Promise<string> {
    method getRedis (line 301) | getRedis(): ReturnType<Redisson['getRedis']> {
    method tryLock (line 312) | async tryLock(key: string, waitTime = 5000, leaseTime = 10000): Promis...
    method unlock (line 321) | async unlock(key: string): Promise<void> {

FILE: packages/redisson/src/types.ts
  type CacheOptions (line 4) | interface CacheOptions {
  type LockOptions (line 14) | interface LockOptions {
  type CacheMetadata (line 24) | interface CacheMetadata {
  type LockMetadata (line 34) | interface LockMetadata {
  type BatchResult (line 46) | interface BatchResult<T = any> {

FILE: packages/rustfs/src/rustfs.module.ts
  class RustFSModule (line 10) | class RustFSModule extends ConfigurableModuleClass {
    method register (line 11) | static register(options: typeof OPTIONS_TYPE): DynamicModule {
    method registerAsync (line 20) | static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {

FILE: packages/rustfs/src/rustfs.service.ts
  class RustFSServiceImpl (line 50) | class RustFSServiceImpl implements OnModuleInit {
    method constructor (line 55) | constructor(private readonly options: RustFSPackageOptions) {
    method onModuleInit (line 60) | async onModuleInit() {
    method createClient (line 70) | private createClient(): S3Client {
    method createBucket (line 100) | async createBucket(options: CreateBucketOptions): Promise<Bucket> {
    method listBuckets (line 128) | async listBuckets(): Promise<Bucket[]> {
    method getBucketAcl (line 138) | async getBucketAcl(bucketName: string): Promise<BucketAcl> {
    method setBucketAcl (line 156) | async setBucketAcl(bucketName: string, acl: BucketAcl): Promise<void> {
    method deleteBucket (line 165) | async deleteBucket(bucketName: string): Promise<void> {
    method bucketExists (line 171) | async bucketExists(bucketName: string): Promise<boolean> {
    method putObject (line 185) | async putObject(bucketName: string, options: PutObjectOptions): Promis...
    method getObject (line 216) | async getObject(bucketName: string, options: GetObjectOptions): Promis...
    method getObjectMetadata (line 249) | async getObjectMetadata(bucketName: string, key: string): Promise<Stor...
    method copyObject (line 278) | async copyObject(bucketName: string, options: CopyObjectOptions): Prom...
    method deleteObject (line 304) | async deleteObject(bucketName: string, key: string): Promise<void> {
    method deleteObjects (line 314) | async deleteObjects(bucketName: string, keys: string[]): Promise<void> {
    method listObjects (line 329) | async listObjects(bucketName: string, options?: ListObjectsOptions): P...
    method getPresignedUrl (line 363) | async getPresignedUrl(
    method getPresignedPostUrl (line 380) | async getPresignedPostUrl(
    method createMultipartUpload (line 405) | async createMultipartUpload(
    method uploadPart (line 427) | async uploadPart(bucketName: string, options: UploadPartOptions): Prom...
    method completeMultipartUpload (line 442) | async completeMultipartUpload(
    method abortMultipartUpload (line 469) | async abortMultipartUpload(bucketName: string, key: string, uploadId: ...
    method listParts (line 479) | async listParts(bucketName: string, options: ListPartsOptions): Promis...
    method isHealthy (line 508) | async isHealthy(): Promise<boolean> {

FILE: packages/rustfs/src/rustfs.types.ts
  type RustFSPackageOptions (line 5) | interface RustFSPackageOptions {
  type RustFSModuleOptions (line 18) | type RustFSModuleOptions = RustFSPackageOptions;
  type Bucket (line 24) | interface Bucket {
  type CreateBucketOptions (line 29) | interface CreateBucketOptions {
  type BucketCannedAcl (line 35) | type BucketCannedAcl =
  type BucketAcl (line 42) | interface BucketAcl {
  type BucketGrant (line 47) | interface BucketGrant {
  type Grantee (line 52) | interface Grantee {
  type BucketPermission (line 59) | type BucketPermission = 'READ' | 'WRITE' | 'READ_ACP' | 'WRITE_ACP' | 'F...
  type StorageObject (line 65) | interface StorageObject {
  type StorageClass (line 77) | type StorageClass =
  type PutObjectOptions (line 85) | interface PutObjectOptions {
  type ObjectCannedAcl (line 99) | type ObjectCannedAcl =
  type GetObjectOptions (line 108) | interface GetObjectOptions {
  type CopyObjectOptions (line 120) | interface CopyObjectOptions {
  type ListObjectsOptions (line 130) | interface ListObjectsOptions {
  type ListObjectsResult (line 139) | interface ListObjectsResult {
  type PresignedUrlOptions (line 152) | interface PresignedUrlOptions {
  type PresignedPostOptions (line 160) | interface PresignedPostOptions {
  type CreateMultipartUploadOptions (line 177) | interface CreateMultipartUploadOptions {
  type UploadPartOptions (line 185) | interface UploadPartOptions {
  type CompleteMultipartUploadOptions (line 194) | interface CompleteMultipartUploadOptions {
  type UploadPart (line 200) | interface UploadPart {
  type ListPartsOptions (line 206) | interface ListPartsOptions {
  type ListPartsResult (line 213) | interface ListPartsResult {
  class RustFSError (line 226) | class RustFSError extends Error {
    method constructor (line 227) | constructor(
  class BucketNotFoundError (line 237) | class BucketNotFoundError extends RustFSError {
    method constructor (line 238) | constructor(bucketName: string) {
  class ObjectNotFoundError (line 247) | class ObjectNotFoundError extends RustFSError {
    method constructor (line 248) | constructor(key: string, bucketName: string) {
  class BucketAlreadyExistsError (line 257) | class BucketAlreadyExistsError extends RustFSError {
    method constructor (line 258) | constructor(bucketName: string) {
  class InvalidAccessKeyIdError (line 267) | class InvalidAccessKeyIdError extends RustFSError {
    method constructor (line 268) | constructor() {
  class SignatureDoesNotMatchError (line 277) | class SignatureDoesNotMatchError extends RustFSError {
    method constructor (line 278) | constructor() {
  class RegionMismatchError (line 287) | class RegionMismatchError extends RustFSError {
    method constructor (line 288) | constructor(expected: string, actual: string) {
Condensed preview — 241 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (598K chars).
[
  {
    "path": ".gitignore",
    "chars": 307,
    "preview": "# Dependencies\nnode_modules/\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Build\ndist/\nbuild/\n*.tsbuildinfo\n\n# Envir"
  },
  {
    "path": ".npmrc",
    "chars": 53,
    "preview": "shamefully-hoist=true\nstrict-peer-dependencies=false\n"
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "MIT License\n\nCopyright (c) 2025 A3S Lab\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "README.md",
    "chars": 12395,
    "preview": "# Nestify - Production-Ready NestJS Monorepo Template\n\nA production-ready NestJS monorepo template with pnpm workspace, "
  },
  {
    "path": "apps/api/DATABASE.md",
    "chars": 3555,
    "preview": "# Database Setup\n\nThis project uses PostgreSQL with Kysely for type-safe SQL queries.\n\n## Prerequisites\n\n- PostgreSQL 14"
  },
  {
    "path": "apps/api/REDIS_USAGE.md",
    "chars": 10843,
    "preview": "# Redis & Redisson Usage Guide\n\nThis guide demonstrates how to use the `@a3s-lab/redisson` package for caching, distribu"
  },
  {
    "path": "apps/api/migrations/001_create_orders_tables.sql",
    "chars": 1028,
    "preview": "-- Create orders table\nCREATE TABLE IF NOT EXISTS orders (\n    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n    custom"
  },
  {
    "path": "apps/api/nest-cli.json",
    "chars": 285,
    "preview": "{\n    \"$schema\": \"https://json.schemastore.org/nest-cli\",\n    \"collection\": \"@nestjs/schematics\",\n    \"sourceRoot\": \"src"
  },
  {
    "path": "apps/api/package.json",
    "chars": 2877,
    "preview": "{\n    \"name\": \"@a3s-lab/api\",\n    \"version\": \"1.0.0\",\n    \"description\": \"Production-ready NestJS API with Domain-Driven"
  },
  {
    "path": "apps/api/src/app.module.ts",
    "chars": 2950,
    "preview": "import { Module } from '@nestjs/common';\nimport { ConfigModule } from '@nestjs/config';\nimport { OrderModule } from './m"
  },
  {
    "path": "apps/api/src/main.ts",
    "chars": 1507,
    "preview": "import { NestFactory } from '@nestjs/core';\nimport { ValidationPipe } from '@nestjs/common';\nimport { SwaggerModule, Doc"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/cancel-order/cancel-order.command.ts",
    "chars": 88,
    "preview": "export class CancelOrderCommand {\n    constructor(public readonly orderId: string) {}\n}\n"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/cancel-order/cancel-order.dto.ts",
    "chars": 207,
    "preview": "import { IsString } from 'class-validator';\nimport { ApiProperty } from '@nestjs/swagger';\n\nexport class CancelOrderDto "
  },
  {
    "path": "apps/api/src/modules/order/application/commands/cancel-order/cancel-order.handler.spec.ts",
    "chars": 5540,
    "preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { CancelOrderHandler } from './cancel-order.handler';\nimpo"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/cancel-order/cancel-order.handler.ts",
    "chars": 1185,
    "preview": "import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';\nimport { Inject } from '@nestjs/common';\nimport { Cancel"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/confirm-order/confirm-order.command.ts",
    "chars": 89,
    "preview": "export class ConfirmOrderCommand {\n    constructor(public readonly orderId: string) {}\n}\n"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/confirm-order/confirm-order.dto.ts",
    "chars": 208,
    "preview": "import { IsString } from 'class-validator';\nimport { ApiProperty } from '@nestjs/swagger';\n\nexport class ConfirmOrderDto"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/confirm-order/confirm-order.handler.spec.ts",
    "chars": 5731,
    "preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { ConfirmOrderHandler } from './confirm-order.handler';\nim"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/confirm-order/confirm-order.handler.ts",
    "chars": 1192,
    "preview": "import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';\nimport { Inject } from '@nestjs/common';\nimport { Confir"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/create-order/create-order.command.ts",
    "chars": 249,
    "preview": "export class CreateOrderCommand {\n    constructor(\n        public readonly customerId: string,\n        public readonly i"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/create-order/create-order.dto.ts",
    "chars": 756,
    "preview": "import { IsString, IsArray, ValidateNested, IsNumber, Min } from 'class-validator';\nimport { Type } from 'class-transfor"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/create-order/create-order.handler.spec.ts",
    "chars": 5530,
    "preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { CreateOrderHandler } from './create-order.handler';\nimpo"
  },
  {
    "path": "apps/api/src/modules/order/application/commands/create-order/create-order.handler.ts",
    "chars": 1641,
    "preview": "import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';\nimport { Inject } from '@nestjs/common';\nimport { Create"
  },
  {
    "path": "apps/api/src/modules/order/application/event-handlers/order-confirmed.handler.ts",
    "chars": 491,
    "preview": "import { EventsHandler, IEventHandler } from '@nestjs/cqrs';\nimport { Logger } from '@nestjs/common';\nimport { OrderConf"
  },
  {
    "path": "apps/api/src/modules/order/application/event-handlers/order-created.handler.ts",
    "chars": 570,
    "preview": "import { EventsHandler, IEventHandler } from '@nestjs/cqrs';\nimport { Logger } from '@nestjs/common';\nimport { OrderCrea"
  },
  {
    "path": "apps/api/src/modules/order/application/queries/get-order/get-order.handler.spec.ts",
    "chars": 6020,
    "preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { GetOrderHandler } from './get-order.handler';\nimport { G"
  },
  {
    "path": "apps/api/src/modules/order/application/queries/get-order/get-order.handler.ts",
    "chars": 1443,
    "preview": "import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';\nimport { Inject } from '@nestjs/common';\nimport { GetOrderQu"
  },
  {
    "path": "apps/api/src/modules/order/application/queries/get-order/get-order.query.ts",
    "chars": 83,
    "preview": "export class GetOrderQuery {\n    constructor(public readonly orderId: string) {}\n}\n"
  },
  {
    "path": "apps/api/src/modules/order/application/queries/get-order/order.response.dto.ts",
    "chars": 661,
    "preview": "import { ApiProperty } from '@nestjs/swagger';\n\nexport class OrderItemResponseDto {\n    @ApiProperty()\n    id: string;\n\n"
  },
  {
    "path": "apps/api/src/modules/order/application/queries/list-orders/list-orders.handler.spec.ts",
    "chars": 5460,
    "preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { ListOrdersHandler } from './list-orders.handler';\nimport"
  },
  {
    "path": "apps/api/src/modules/order/application/queries/list-orders/list-orders.handler.ts",
    "chars": 1461,
    "preview": "import { IQueryHandler, QueryHandler } from '@nestjs/cqrs';\nimport { Inject } from '@nestjs/common';\nimport { ListOrders"
  },
  {
    "path": "apps/api/src/modules/order/application/queries/list-orders/list-orders.query.ts",
    "chars": 89,
    "preview": "export class ListOrdersQuery {\n    constructor(public readonly customerId?: string) {}\n}\n"
  },
  {
    "path": "apps/api/src/modules/order/application/queries/list-orders/order-list.response.dto.ts",
    "chars": 272,
    "preview": "import { ApiProperty } from '@nestjs/swagger';\nimport { OrderResponseDto } from '../get-order/order.response.dto';\n\nexpo"
  },
  {
    "path": "apps/api/src/modules/order/domain/entities/order-item.entity.spec.ts",
    "chars": 4121,
    "preview": "import { OrderItem } from './order-item.entity';\nimport { Money } from '../value-objects/money.vo';\nimport { Quantity } "
  },
  {
    "path": "apps/api/src/modules/order/domain/entities/order-item.entity.ts",
    "chars": 1147,
    "preview": "import { Entity } from '@/shared/domain/entity';\nimport { Money } from '../value-objects/money.vo';\nimport { Quantity } "
  },
  {
    "path": "apps/api/src/modules/order/domain/entities/order.entity.spec.ts",
    "chars": 11348,
    "preview": "import { Order } from './order.entity';\nimport { OrderItem } from './order-item.entity';\nimport { Money } from '../value"
  },
  {
    "path": "apps/api/src/modules/order/domain/entities/order.entity.ts",
    "chars": 3749,
    "preview": "import { AggregateRoot } from '@/shared/domain/aggregate-root';\nimport { OrderId } from '../value-objects/order-id.vo';\n"
  },
  {
    "path": "apps/api/src/modules/order/domain/events/order-cancelled.event.ts",
    "chars": 259,
    "preview": "import { DomainEvent } from '@/shared/domain/domain-event';\n\nexport class OrderCancelledEvent extends DomainEvent {\n    "
  },
  {
    "path": "apps/api/src/modules/order/domain/events/order-confirmed.event.ts",
    "chars": 259,
    "preview": "import { DomainEvent } from '@/shared/domain/domain-event';\n\nexport class OrderConfirmedEvent extends DomainEvent {\n    "
  },
  {
    "path": "apps/api/src/modules/order/domain/events/order-created.event.ts",
    "chars": 411,
    "preview": "import { DomainEvent } from '@/shared/domain/domain-event';\nimport { Money } from '../value-objects/money.vo';\n\nexport c"
  },
  {
    "path": "apps/api/src/modules/order/domain/exceptions/invalid-order-state.exception.ts",
    "chars": 223,
    "preview": "import { DomainException } from '@/shared/presentation/filters/domain-exception.filter';\n\nexport class InvalidOrderState"
  },
  {
    "path": "apps/api/src/modules/order/domain/exceptions/order-not-found.exception.ts",
    "chars": 248,
    "preview": "import { DomainException } from '@/shared/presentation/filters/domain-exception.filter';\n\nexport class OrderNotFoundExce"
  },
  {
    "path": "apps/api/src/modules/order/domain/repositories/order.repository.interface.ts",
    "chars": 338,
    "preview": "import { Order } from '../entities/order.entity';\n\nexport interface IOrderRepository {\n    findById(id: string): Promise"
  },
  {
    "path": "apps/api/src/modules/order/domain/services/order-pricing.service.spec.ts",
    "chars": 3362,
    "preview": "import { OrderPricingService } from './order-pricing.service';\nimport { Order } from '../entities/order.entity';\nimport "
  },
  {
    "path": "apps/api/src/modules/order/domain/services/order-pricing.service.ts",
    "chars": 646,
    "preview": "import { Injectable } from '@nestjs/common';\nimport { Order } from '../entities/order.entity';\nimport { Money } from '.."
  },
  {
    "path": "apps/api/src/modules/order/domain/value-objects/money.vo.spec.ts",
    "chars": 3854,
    "preview": "import { Money } from './money.vo';\n\ndescribe('Money Value Object', () => {\n    describe('create', () => {\n        it('s"
  },
  {
    "path": "apps/api/src/modules/order/domain/value-objects/money.vo.ts",
    "chars": 1226,
    "preview": "import { ValueObject } from '@/shared/domain/value-object';\nimport { Guard } from '@/shared/utils/guard';\n\ninterface Mon"
  },
  {
    "path": "apps/api/src/modules/order/domain/value-objects/order-id.vo.spec.ts",
    "chars": 1724,
    "preview": "import { OrderId } from './order-id.vo';\n\ndescribe('OrderId Value Object', () => {\n    describe('create', () => {\n      "
  },
  {
    "path": "apps/api/src/modules/order/domain/value-objects/order-id.vo.ts",
    "chars": 522,
    "preview": "import { ValueObject } from '@/shared/domain/value-object';\nimport { v4 as uuidv4 } from 'uuid';\n\ninterface OrderIdProps"
  },
  {
    "path": "apps/api/src/modules/order/domain/value-objects/order-status.vo.spec.ts",
    "chars": 2708,
    "preview": "import { OrderStatus, OrderStatusEnum } from './order-status.vo';\n\ndescribe('OrderStatus Value Object', () => {\n    desc"
  },
  {
    "path": "apps/api/src/modules/order/domain/value-objects/order-status.vo.ts",
    "chars": 1490,
    "preview": "import { ValueObject } from '@/shared/domain/value-object';\n\nexport enum OrderStatusEnum {\n    PENDING = 'PENDING',\n    "
  },
  {
    "path": "apps/api/src/modules/order/domain/value-objects/quantity.vo.spec.ts",
    "chars": 1640,
    "preview": "import { Quantity } from './quantity.vo';\n\ndescribe('Quantity Value Object', () => {\n    describe('create', () => {\n    "
  },
  {
    "path": "apps/api/src/modules/order/domain/value-objects/quantity.vo.ts",
    "chars": 618,
    "preview": "import { ValueObject } from '@/shared/domain/value-object';\n\ninterface QuantityProps {\n    value: number;\n}\n\nexport clas"
  },
  {
    "path": "apps/api/src/modules/order/infrastructure/cache/order-cache.service.ts",
    "chars": 4894,
    "preview": "import { Injectable, Logger } from '@nestjs/common';\nimport { RedissonService } from '@a3s-lab/redisson';\nimport { Order"
  },
  {
    "path": "apps/api/src/modules/order/infrastructure/persistence/kysely-order.repository.ts",
    "chars": 6676,
    "preview": "import { Injectable } from '@nestjs/common';\nimport { KyselyService } from '@a3s-lab/kysely';\nimport { Database, NewOrde"
  },
  {
    "path": "apps/api/src/modules/order/order.module.ts",
    "chars": 2007,
    "preview": "import { Module } from '@nestjs/common';\nimport { CqrsModule } from '@nestjs/cqrs';\nimport { OrderController } from './p"
  },
  {
    "path": "apps/api/src/modules/order/presentation/order.controller.ts",
    "chars": 2893,
    "preview": "import { Controller, Get, Post, Body, Param, Query, HttpCode, HttpStatus } from '@nestjs/common';\nimport { CommandBus, Q"
  },
  {
    "path": "apps/api/src/shared/api-response/api-response.dto.ts",
    "chars": 2247,
    "preview": "// ============================================================================\n// API Response DTO - Standardized API r"
  },
  {
    "path": "apps/api/src/shared/api-response/api-response.interceptor.ts",
    "chars": 1445,
    "preview": "// ============================================================================\n// API Response Interceptor - Wrap all r"
  },
  {
    "path": "apps/api/src/shared/api-response/api-response.service.ts",
    "chars": 2848,
    "preview": "// ============================================================================\n// API Response Service - Factory for cr"
  },
  {
    "path": "apps/api/src/shared/api-response/index.ts",
    "chars": 120,
    "preview": "export * from './api-response.dto';\nexport * from './api-response.service';\nexport * from './api-response.interceptor';\n"
  },
  {
    "path": "apps/api/src/shared/api-versioning/api-versioning.decorator.ts",
    "chars": 642,
    "preview": "// ============================================================================\n// API Versioning Decorators\n// ========"
  },
  {
    "path": "apps/api/src/shared/api-versioning/api-versioning.interceptor.ts",
    "chars": 2483,
    "preview": "// ============================================================================\n// API Versioning - URL and Header based"
  },
  {
    "path": "apps/api/src/shared/api-versioning/index.ts",
    "chars": 336,
    "preview": "// ============================================================================\n// API Versioning Module\n// ============"
  },
  {
    "path": "apps/api/src/shared/application/dto.base.ts",
    "chars": 157,
    "preview": "export abstract class BaseDto {\n    constructor(partial?: Partial<any>) {\n        if (partial) {\n            Object.assi"
  },
  {
    "path": "apps/api/src/shared/application/query.interface.ts",
    "chars": 74,
    "preview": "export interface IQuery<IResponse> {\n    execute(): Promise<IResponse>;\n}\n"
  },
  {
    "path": "apps/api/src/shared/application/use-case.interface.ts",
    "chars": 103,
    "preview": "export interface IUseCase<IRequest, IResponse> {\n    execute(request: IRequest): Promise<IResponse>;\n}\n"
  },
  {
    "path": "apps/api/src/shared/audit/audit.decorator.ts",
    "chars": 386,
    "preview": "// ============================================================================\n// Audit Decorators\n// ================="
  },
  {
    "path": "apps/api/src/shared/audit/audit.interceptor.ts",
    "chars": 4906,
    "preview": "// ============================================================================\n// Audit Interceptor - Automatically log"
  },
  {
    "path": "apps/api/src/shared/audit/audit.service.ts",
    "chars": 7249,
    "preview": "// ============================================================================\n// Audit Service - Operation logging for"
  },
  {
    "path": "apps/api/src/shared/audit/index.ts",
    "chars": 282,
    "preview": "// ============================================================================\n// Audit Module\n// ====================="
  },
  {
    "path": "apps/api/src/shared/auth/auth.module.ts",
    "chars": 844,
    "preview": "// ============================================================================\n// Auth Module - Authentication and Auth"
  },
  {
    "path": "apps/api/src/shared/auth/decorators/current-user.decorator.ts",
    "chars": 1582,
    "preview": "// ============================================================================\n// Auth Decorators - Convenience decorat"
  },
  {
    "path": "apps/api/src/shared/auth/decorators/index.ts",
    "chars": 222,
    "preview": "// ============================================================================\n// Auth Decorators\n// =================="
  },
  {
    "path": "apps/api/src/shared/auth/dto/auth.dto.ts",
    "chars": 3191,
    "preview": "// ============================================================================\n// Auth DTOs - Data Transfer Objects for"
  },
  {
    "path": "apps/api/src/shared/auth/dto/index.ts",
    "chars": 202,
    "preview": "// ============================================================================\n// Auth DTOs\n// ========================"
  },
  {
    "path": "apps/api/src/shared/auth/guards/index.ts",
    "chars": 278,
    "preview": "// ============================================================================\n// Auth Guards\n// ======================"
  },
  {
    "path": "apps/api/src/shared/auth/guards/jwt-auth.guard.ts",
    "chars": 1849,
    "preview": "// ============================================================================\n// JWT Auth Guard - Validates JWT tokens"
  },
  {
    "path": "apps/api/src/shared/auth/guards/permissions.guard.ts",
    "chars": 2165,
    "preview": "// ============================================================================\n// Permissions Guard - Checks user permi"
  },
  {
    "path": "apps/api/src/shared/auth/guards/roles.guard.ts",
    "chars": 1752,
    "preview": "// ============================================================================\n// Roles Guard - Checks user roles\n// =="
  },
  {
    "path": "apps/api/src/shared/auth/index.ts",
    "chars": 368,
    "preview": "// ============================================================================\n// Auth Module - Authentication and Auth"
  },
  {
    "path": "apps/api/src/shared/auth/jwt/index.ts",
    "chars": 235,
    "preview": "// ============================================================================\n// JWT Module\n// ======================="
  },
  {
    "path": "apps/api/src/shared/auth/jwt/jwt.service.ts",
    "chars": 3209,
    "preview": "// ============================================================================\n// JWT Service - Token generation and ve"
  },
  {
    "path": "apps/api/src/shared/auth/jwt/jwt.types.ts",
    "chars": 1255,
    "preview": "// ============================================================================\n// JWT Types\n// ========================"
  },
  {
    "path": "apps/api/src/shared/auth/rbac/index.ts",
    "chars": 208,
    "preview": "// ============================================================================\n// RBAC Module\n// ======================"
  },
  {
    "path": "apps/api/src/shared/auth/rbac/rbac.service.ts",
    "chars": 4573,
    "preview": "// ============================================================================\n// RBAC - Role-Based Access Control\n// ="
  },
  {
    "path": "apps/api/src/shared/base/base.entity.ts",
    "chars": 577,
    "preview": "// ============================================================================\n// Base Entity Interface - Common proper"
  },
  {
    "path": "apps/api/src/shared/base/base.service.ts",
    "chars": 9327,
    "preview": "// ============================================================================\n// Base Service - Generic CRUD operation"
  },
  {
    "path": "apps/api/src/shared/base/index.ts",
    "chars": 97,
    "preview": "export * from './base.entity';\nexport * from './base.service';\nexport * from './pagination.dto';\n"
  },
  {
    "path": "apps/api/src/shared/base/pagination.dto.ts",
    "chars": 3292,
    "preview": "// ============================================================================\n// Pagination DTO - Standardized paginat"
  },
  {
    "path": "apps/api/src/shared/cache/cache.decorator.ts",
    "chars": 614,
    "preview": "// ============================================================================\n// Cache Decorator - Method-level cachin"
  },
  {
    "path": "apps/api/src/shared/cache/cache.interceptor.ts",
    "chars": 2437,
    "preview": "// ============================================================================\n// Cache Interceptor - Automatically cac"
  },
  {
    "path": "apps/api/src/shared/cache/cache.service.ts",
    "chars": 5826,
    "preview": "// ============================================================================\n// Cache Service - Redis-based caching a"
  },
  {
    "path": "apps/api/src/shared/cache/index.ts",
    "chars": 425,
    "preview": "// ============================================================================\n// Cache Module\n// ====================="
  },
  {
    "path": "apps/api/src/shared/circuit-breaker/circuit-breaker.decorator.ts",
    "chars": 619,
    "preview": "// ============================================================================\n// Circuit Breaker Decorator\n// ========"
  },
  {
    "path": "apps/api/src/shared/circuit-breaker/circuit-breaker.module.ts",
    "chars": 468,
    "preview": "// ============================================================================\n// Circuit Breaker Module - Fault tolera"
  },
  {
    "path": "apps/api/src/shared/circuit-breaker/circuit-breaker.service.ts",
    "chars": 7373,
    "preview": "// ============================================================================\n// Circuit Breaker - Protect external ca"
  },
  {
    "path": "apps/api/src/shared/circuit-breaker/index.ts",
    "chars": 484,
    "preview": "// ============================================================================\n// Circuit Breaker Module\n// ==========="
  },
  {
    "path": "apps/api/src/shared/database/database.module.ts",
    "chars": 1332,
    "preview": "import { Module, Global } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { "
  },
  {
    "path": "apps/api/src/shared/database/database.types.ts",
    "chars": 1166,
    "preview": "import type { ColumnType, Generated, Insertable, Selectable, Updateable } from 'kysely';\n\n/**\n * Database schema types f"
  },
  {
    "path": "apps/api/src/shared/database/index.ts",
    "chars": 69,
    "preview": "export * from './database.module';\nexport * from './database.types';\n"
  },
  {
    "path": "apps/api/src/shared/domain/aggregate-root.ts",
    "chars": 461,
    "preview": "import { Entity } from './entity';\nimport { DomainEvent } from './domain-event';\n\nexport abstract class AggregateRoot<T>"
  },
  {
    "path": "apps/api/src/shared/domain/domain-event-publisher.ts",
    "chars": 265,
    "preview": "import { DomainEvent } from './domain-event';\n\nexport interface IDomainEventPublisher {\n    publish(event: DomainEvent):"
  },
  {
    "path": "apps/api/src/shared/domain/domain-event.ts",
    "chars": 292,
    "preview": "export interface IDomainEvent {\n    occurredOn: Date;\n    getAggregateId(): string;\n}\n\nexport abstract class DomainEvent"
  },
  {
    "path": "apps/api/src/shared/domain/entity.ts",
    "chars": 3882,
    "preview": "// ============================================================================\n// Entity - Base class for domain entiti"
  },
  {
    "path": "apps/api/src/shared/domain/index.ts",
    "chars": 364,
    "preview": "// ============================================================================\n// Domain - DDD core building blocks\n// "
  },
  {
    "path": "apps/api/src/shared/domain/value-object.ts",
    "chars": 528,
    "preview": "export interface ValueObjectProps {\n    [index: string]: any;\n}\n\nexport abstract class ValueObject<T extends ValueObject"
  },
  {
    "path": "apps/api/src/shared/errors/business.exception.ts",
    "chars": 3315,
    "preview": "// ============================================================================\n// Business Exception - Base exception f"
  },
  {
    "path": "apps/api/src/shared/errors/error-codes.ts",
    "chars": 2763,
    "preview": "// ============================================================================\n// Error Codes - Standardized error code"
  },
  {
    "path": "apps/api/src/shared/errors/error.filter.ts",
    "chars": 5878,
    "preview": "// ============================================================================\n// Global Error Filter - Handle all exce"
  },
  {
    "path": "apps/api/src/shared/errors/index.ts",
    "chars": 101,
    "preview": "export * from './error-codes';\nexport * from './business.exception';\nexport * from './error.filter';\n"
  },
  {
    "path": "apps/api/src/shared/feature-flags/feature-flags.decorator.ts",
    "chars": 1366,
    "preview": "// ============================================================================\n// Feature Flags Decorators\n// ========="
  },
  {
    "path": "apps/api/src/shared/feature-flags/feature-flags.guard.ts",
    "chars": 1633,
    "preview": "// ============================================================================\n// Feature Flags Guard - Protects routes"
  },
  {
    "path": "apps/api/src/shared/feature-flags/feature-flags.service.ts",
    "chars": 10423,
    "preview": "// ============================================================================\n// Feature Flags Service - Feature toggl"
  },
  {
    "path": "apps/api/src/shared/feature-flags/index.ts",
    "chars": 404,
    "preview": "// ============================================================================\n// Feature Flags Module\n// ============="
  },
  {
    "path": "apps/api/src/shared/file-upload/file-upload.decorator.ts",
    "chars": 773,
    "preview": "// ============================================================================\n// File Upload Decorators\n// ==========="
  },
  {
    "path": "apps/api/src/shared/file-upload/file-upload.interceptor.ts",
    "chars": 4350,
    "preview": "// ============================================================================\n// File Upload Interceptor - Handles mul"
  },
  {
    "path": "apps/api/src/shared/file-upload/file-upload.service.ts",
    "chars": 5513,
    "preview": "// ============================================================================\n// File Upload Service - Multipart handl"
  },
  {
    "path": "apps/api/src/shared/file-upload/index.ts",
    "chars": 420,
    "preview": "// ============================================================================\n// File Upload Module\n// ==============="
  },
  {
    "path": "apps/api/src/shared/health/health.controller.ts",
    "chars": 2045,
    "preview": "// ============================================================================\n// Health Controller - Health check endp"
  },
  {
    "path": "apps/api/src/shared/health/health.module.ts",
    "chars": 866,
    "preview": "// ============================================================================\n// Health Module\n// ===================="
  },
  {
    "path": "apps/api/src/shared/health/index.ts",
    "chars": 165,
    "preview": "export * from './health.controller';\nexport * from './health.module';\nexport * from './indicators/database.indicator';\ne"
  },
  {
    "path": "apps/api/src/shared/health/indicators/database.indicator.ts",
    "chars": 940,
    "preview": "// ============================================================================\n// Health Indicator - Database\n// ======"
  },
  {
    "path": "apps/api/src/shared/health/indicators/nats.indicator.ts",
    "chars": 1133,
    "preview": "// ============================================================================\n// Health Indicator - NATS\n// =========="
  },
  {
    "path": "apps/api/src/shared/health/indicators/redis.indicator.ts",
    "chars": 922,
    "preview": "// ============================================================================\n// Health Indicator - Redis\n// ========="
  },
  {
    "path": "apps/api/src/shared/health/indicators/rustfs.indicator.ts",
    "chars": 1130,
    "preview": "// ============================================================================\n// Health Indicator - RustFS\n// ========"
  },
  {
    "path": "apps/api/src/shared/infrastructure/messaging/event-bus.interface.ts",
    "chars": 241,
    "preview": "import { DomainEvent } from '@/shared/domain/domain-event';\n\nexport interface IEventBus {\n    publish(event: DomainEvent"
  },
  {
    "path": "apps/api/src/shared/infrastructure/messaging/event-bus.service.ts",
    "chars": 594,
    "preview": "import { Injectable } from '@nestjs/common';\nimport { EventBus as NestEventBus } from '@nestjs/cqrs';\nimport { IEventBus"
  },
  {
    "path": "apps/api/src/shared/infrastructure/messaging/messaging.interface.ts",
    "chars": 1193,
    "preview": "// ============================================================================\n// Messaging Infrastructure Interface\n//"
  },
  {
    "path": "apps/api/src/shared/infrastructure/persistence/repository.interface.ts",
    "chars": 153,
    "preview": "export interface IRepository<T> {\n    findById(id: string): Promise<T | null>;\n    save(entity: T): Promise<T>;\n    dele"
  },
  {
    "path": "apps/api/src/shared/infrastructure/persistence/unit-of-work.interface.ts",
    "chars": 121,
    "preview": "export interface IUnitOfWork {\n    start(): Promise<void>;\n    commit(): Promise<void>;\n    rollback(): Promise<void>;\n}"
  },
  {
    "path": "apps/api/src/shared/infrastructure/storage/storage.interface.ts",
    "chars": 2920,
    "preview": "// ============================================================================\n// Storage Infrastructure Interface\n// ="
  },
  {
    "path": "apps/api/src/shared/metrics/index.ts",
    "chars": 349,
    "preview": "// ============================================================================\n// Metrics Module\n// ==================="
  },
  {
    "path": "apps/api/src/shared/metrics/metrics.controller.ts",
    "chars": 1085,
    "preview": "// ============================================================================\n// Metrics Controller - Exposes /metrics"
  },
  {
    "path": "apps/api/src/shared/metrics/metrics.interceptor.ts",
    "chars": 3441,
    "preview": "// ============================================================================\n// Metrics Interceptor - Automatically r"
  },
  {
    "path": "apps/api/src/shared/metrics/metrics.service.ts",
    "chars": 11010,
    "preview": "// ============================================================================\n// Metrics Service - Prometheus + OpenTe"
  },
  {
    "path": "apps/api/src/shared/openapi/index.ts",
    "chars": 76,
    "preview": "export * from './openapi-decorators';\nexport * from './openapi-common.dto';\n"
  },
  {
    "path": "apps/api/src/shared/openapi/openapi-common.dto.ts",
    "chars": 1559,
    "preview": "// ============================================================================\n// OpenAPI Common DTOs - Reusable docume"
  },
  {
    "path": "apps/api/src/shared/openapi/openapi-decorators.ts",
    "chars": 7964,
    "preview": "// ============================================================================\n// OpenAPI Common Decorators - Reusable "
  },
  {
    "path": "apps/api/src/shared/presentation/filters/domain-exception.filter.ts",
    "chars": 1149,
    "preview": "import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus, Logger } from '@nestjs/common';\nimport { Response } from 'ex"
  },
  {
    "path": "apps/api/src/shared/presentation/filters/http-exception.filter.ts",
    "chars": 1169,
    "preview": "import { ExceptionFilter, Catch, ArgumentsHost, HttpException, Logger } from '@nestjs/common';\nimport { Request, Respons"
  },
  {
    "path": "apps/api/src/shared/presentation/interceptors/logging.interceptor.ts",
    "chars": 957,
    "preview": "import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common';\nimport { Observable"
  },
  {
    "path": "apps/api/src/shared/rate-limiting/index.ts",
    "chars": 517,
    "preview": "// ============================================================================\n// Rate Limiting Module\n// ============="
  },
  {
    "path": "apps/api/src/shared/rate-limiting/rate-limiting.decorator.ts",
    "chars": 1118,
    "preview": "// ============================================================================\n// Rate Limiting Decorators\n// ========="
  },
  {
    "path": "apps/api/src/shared/rate-limiting/rate-limiting.guard.ts",
    "chars": 3393,
    "preview": "// ============================================================================\n// Rate Limiting Guard - Guard that enfo"
  },
  {
    "path": "apps/api/src/shared/rate-limiting/rate-limiting.service.ts",
    "chars": 6344,
    "preview": "// ============================================================================\n// Rate Limiting Service - API throttlin"
  },
  {
    "path": "apps/api/src/shared/redis/index.ts",
    "chars": 32,
    "preview": "export * from './redis.module';\n"
  },
  {
    "path": "apps/api/src/shared/redis/redis.module.ts",
    "chars": 846,
    "preview": "import { Module, Global } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport { "
  },
  {
    "path": "apps/api/src/shared/retry/index.ts",
    "chars": 372,
    "preview": "// ============================================================================\n// Retry Module\n// ====================="
  },
  {
    "path": "apps/api/src/shared/retry/retry.module.ts",
    "chars": 429,
    "preview": "// ============================================================================\n// Retry Module - Automatic retry with e"
  },
  {
    "path": "apps/api/src/shared/retry/retry.service.ts",
    "chars": 6625,
    "preview": "// ============================================================================\n// Retry Service - Automatic retry with "
  },
  {
    "path": "apps/api/src/shared/serialization/example.ts",
    "chars": 5173,
    "preview": "// ============================================================================\n// Serializer Examples\n// =============="
  },
  {
    "path": "apps/api/src/shared/serialization/index.ts",
    "chars": 57,
    "preview": "export * from './serializer';\nexport * from './example';\n"
  },
  {
    "path": "apps/api/src/shared/serialization/serializer.ts",
    "chars": 3281,
    "preview": "// ============================================================================\n// Serializer - Entity ↔ DTO mapping\n// "
  },
  {
    "path": "apps/api/src/shared/tenant/index.ts",
    "chars": 342,
    "preview": "// ============================================================================\n// Tenant Module - Multi-tenancy support"
  },
  {
    "path": "apps/api/src/shared/tenant/tenant.decorator.ts",
    "chars": 1255,
    "preview": "// ============================================================================\n// Tenant Decorators\n// ================"
  },
  {
    "path": "apps/api/src/shared/tenant/tenant.guard.ts",
    "chars": 1720,
    "preview": "// ============================================================================\n// Tenant Guard - Ensures tenant context"
  },
  {
    "path": "apps/api/src/shared/tenant/tenant.interceptor.ts",
    "chars": 1008,
    "preview": "// ============================================================================\n// Tenant Interceptor - Automatically ex"
  },
  {
    "path": "apps/api/src/shared/tenant/tenant.service.ts",
    "chars": 2182,
    "preview": "// ============================================================================\n// Tenant Service - Multi-tenancy contex"
  },
  {
    "path": "apps/api/src/shared/testing/index.ts",
    "chars": 470,
    "preview": "// ============================================================================\n// Testing Module\n// ==================="
  },
  {
    "path": "apps/api/src/shared/testing/testing.utils.ts",
    "chars": 6728,
    "preview": "// ============================================================================\n// Testing Utils - Mock factories, fixtu"
  },
  {
    "path": "apps/api/src/shared/tracking/index.ts",
    "chars": 40,
    "preview": "export * from './tracking.interceptor';\n"
  },
  {
    "path": "apps/api/src/shared/tracking/tracking.interceptor.ts",
    "chars": 2500,
    "preview": "// ============================================================================\n// Tracking Interceptor - Request ID and"
  },
  {
    "path": "apps/api/src/shared/transform/index.ts",
    "chars": 422,
    "preview": "// ============================================================================\n// Transform Module\n// ================="
  },
  {
    "path": "apps/api/src/shared/transform/transform.interceptor.ts",
    "chars": 6526,
    "preview": "// ============================================================================\n// Transform Interceptor - Global reques"
  },
  {
    "path": "apps/api/src/shared/utils/guard.ts",
    "chars": 2290,
    "preview": "export class Guard {\n    public static againstNullOrUndefined(argument: any, argumentName: string): Result {\n        if "
  },
  {
    "path": "apps/api/src/shared/utils/result.ts",
    "chars": 6414,
    "preview": "// ============================================================================\n// Result Type - Functional error handli"
  },
  {
    "path": "apps/api/src/shared/validation/index.ts",
    "chars": 293,
    "preview": "// ============================================================================\n// Validation - Common validation decora"
  },
  {
    "path": "apps/api/src/shared/validation/validation-options.ts",
    "chars": 15228,
    "preview": "// ============================================================================\n// Validation Options & Decorators\n// =="
  },
  {
    "path": "apps/api/src/shared/validation/validation.pipe.ts",
    "chars": 2762,
    "preview": "// ============================================================================\n// Validation Pipe - Global validation c"
  },
  {
    "path": "apps/api/test/jest-e2e.json",
    "chars": 274,
    "preview": "{\n    \"moduleFileExtensions\": [\"js\", \"json\", \"ts\"],\n    \"rootDir\": \".\",\n    \"testEnvironment\": \"node\",\n    \"testRegex\": "
  },
  {
    "path": "apps/api/tsconfig.build.json",
    "chars": 101,
    "preview": "{\n    \"extends\": \"./tsconfig.json\",\n    \"exclude\": [\"node_modules\", \"test\", \"dist\", \"**/*spec.ts\"]\n}\n"
  },
  {
    "path": "apps/api/tsconfig.json",
    "chars": 672,
    "preview": "{\n    \"compilerOptions\": {\n        \"module\": \"commonjs\",\n        \"declaration\": true,\n        \"removeComments\": true,\n  "
  },
  {
    "path": "biome.json",
    "chars": 2858,
    "preview": "{\n    \"$schema\": \"https://biomejs.dev/schemas/2.3.11/schema.json\",\n    \"vcs\": {\n        \"enabled\": true,\n        \"defaul"
  },
  {
    "path": "docker/Dockerfile",
    "chars": 3339,
    "preview": "# =============================================================================\n# Stage 1: Dependencies\n# =============="
  },
  {
    "path": "docker/Dockerfile.dev",
    "chars": 959,
    "preview": "# =============================================================================\n# Development Dockerfile\n# ============="
  },
  {
    "path": "docker/docker-compose.prod.yml",
    "chars": 2924,
    "preview": "services:\n  # =============================================================================\n  # PostgreSQL Database (Pro"
  },
  {
    "path": "docker/docker-compose.yml",
    "chars": 3080,
    "preview": "services:\n  # =============================================================================\n  # PostgreSQL Database\n  # "
  },
  {
    "path": "docs/architecture.md",
    "chars": 12134,
    "preview": "# Architecture Guide\n\n## Overview\n\nThis template implements Clean Architecture and Domain-Driven Design (DDD) principles"
  },
  {
    "path": "docs/ddd-patterns.md",
    "chars": 14368,
    "preview": "# DDD Patterns Guide\n\nThis document explains the Domain-Driven Design patterns used in this template.\n\n## Core Building "
  },
  {
    "path": "nest-cli.json",
    "chars": 285,
    "preview": "{\n    \"$schema\": \"https://json.schemastore.org/nest-cli\",\n    \"collection\": \"@nestjs/schematics\",\n    \"sourceRoot\": \"src"
  },
  {
    "path": "package.json",
    "chars": 1330,
    "preview": "{\n    \"name\": \"@a3s-lab/nestify\",\n    \"version\": \"1.0.0\",\n    \"description\": \"Production-ready NestJS monorepo with pnpm"
  },
  {
    "path": "packages/bullmq/package.json",
    "chars": 543,
    "preview": "{\n  \"name\": \"@a3s-lab/bullmq\",\n  \"version\": \"0.0.1\",\n  \"description\": \"BullMQ module for NestJS\",\n  \"main\": \"./dist/inde"
  },
  {
    "path": "packages/bullmq/src/bullmq.module-definition.ts",
    "chars": 903,
    "preview": "// ============================================================================\n// BullMQ Module Definition - Configurab"
  },
  {
    "path": "packages/bullmq/src/bullmq.module.ts",
    "chars": 1664,
    "preview": "// ============================================================================\n// BullMQ Module - Distributed task queu"
  },
  {
    "path": "packages/bullmq/src/bullmq.service.ts",
    "chars": 8066,
    "preview": "// ============================================================================\n// BullMQ Service - Queue management and"
  },
  {
    "path": "packages/bullmq/src/bullmq.types.ts",
    "chars": 824,
    "preview": "// ============================================================================\n// BullMQ Module Options\n// ============"
  },
  {
    "path": "packages/bullmq/src/index.ts",
    "chars": 143,
    "preview": "export * from './bullmq.module';\nexport * from './bullmq.service';\nexport * from './bullmq.types';\nexport * from './bull"
  },
  {
    "path": "packages/bullmq/tsconfig.json",
    "chars": 246,
    "preview": "{\n    \"extends\": \"../../tsconfig.base.json\",\n    \"compilerOptions\": {\n        \"outDir\": \"./dist\",\n        \"rootDir\": \"./"
  },
  {
    "path": "packages/etcd/package.json",
    "chars": 751,
    "preview": "{\n    \"name\": \"@a3s-lab/etcd\",\n    \"version\": \"0.0.1\",\n    \"description\": \"Etcd distributed configuration center for Nes"
  },
  {
    "path": "packages/etcd/src/config.service.ts",
    "chars": 4863,
    "preview": "import { Injectable, Logger, OnModuleInit } from '@nestjs/common';\nimport { EtcdService } from './etcd.service';\n\nexport"
  },
  {
    "path": "packages/etcd/src/etcd.module.ts",
    "chars": 1348,
    "preview": "import { Module, Global, DynamicModule, Provider } from '@nestjs/common';\nimport { EtcdModuleOptions } from './etcd.type"
  },
  {
    "path": "packages/etcd/src/etcd.service.ts",
    "chars": 9698,
    "preview": "import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';\nimport { Etcd3, EtcdOptions } from '"
  },
  {
    "path": "packages/etcd/src/etcd.types.ts",
    "chars": 1256,
    "preview": "/**\n * Etcd configuration options\n */\nexport interface EtcdModuleOptions {\n    /** Etcd endpoints */\n    endpoints: stri"
  },
  {
    "path": "packages/etcd/src/index.ts",
    "chars": 127,
    "preview": "export * from './etcd.module';\nexport * from './etcd.service';\nexport * from './config.service';\nexport * from './etcd.t"
  },
  {
    "path": "packages/etcd/tsconfig.json",
    "chars": 246,
    "preview": "{\n    \"extends\": \"../../tsconfig.base.json\",\n    \"compilerOptions\": {\n        \"outDir\": \"./dist\",\n        \"rootDir\": \"./"
  },
  {
    "path": "packages/kysely/package.json",
    "chars": 2257,
    "preview": "{\n    \"name\": \"@a3s-lab/kysely\",\n    \"version\": \"1.0.0\",\n    \"description\": \"NestJS module for Kysely SQL query builder "
  },
  {
    "path": "packages/kysely/src/__tests__/kysely.logger.spec.ts",
    "chars": 3152,
    "preview": "import { createKyselyLogger } from '../kysely.logger';\n\ndescribe('KyselyLogger', () => {\n    let consoleSpy: jest.SpyIns"
  },
  {
    "path": "packages/kysely/src/__tests__/kysely.module.spec.ts",
    "chars": 2285,
    "preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { KyselyModule } from '../kysely.module';\nimport { KyselyS"
  },
  {
    "path": "packages/kysely/src/index.ts",
    "chars": 195,
    "preview": "export * from \"./kysely.module\";\nexport * from \"./kysely.service\";\nexport * from \"./kysely.logger\";\nexport * from \"./kys"
  }
]

// ... and 41 more files (download for full content)

About this extraction

This page contains the full source code of the ZhiXiao-Lin/nestify GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 241 files (541.9 KB), approximately 125.0k tokens, and a symbol index with 995 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!