# 08 - Development Guidelines ## 8.1 Project Structure ``` openwa/ ├── src/ │ ├── main.ts # Application entry │ ├── app.module.ts # Root module │ │ │ ├── common/ # Shared code │ │ ├── decorators/ │ │ ├── dto/ │ │ ├── exceptions/ │ │ ├── filters/ │ │ ├── guards/ │ │ ├── interceptors/ │ │ ├── interfaces/ │ │ ├── pipes/ │ │ └── utils/ │ │ │ ├── config/ # Configuration │ │ ├── config.module.ts │ │ └── configuration.ts │ │ │ ├── modules/ # Feature modules │ │ ├── session/ │ │ ├── message/ │ │ ├── webhook/ │ │ ├── contact/ │ │ ├── group/ │ │ ├── auth/ │ │ └── health/ │ │ │ ├── engine/ # WhatsApp engine │ │ ├── engine.module.ts │ │ ├── engine.service.ts │ │ └── interfaces/ │ │ │ ├── queue/ # Job queues │ │ ├── queue.module.ts │ │ └── processors/ │ │ │ └── database/ # Database │ ├── database.module.ts │ ├── entities/ │ └── migrations/ │ ├── test/ # Tests │ ├── unit/ │ ├── integration/ │ └── e2e/ │ ├── dashboard/ # Frontend dashboard │ ├── src/ │ └── package.json │ ├── docs/ # Documentation ├── scripts/ # Utility scripts ├── docker/ # Docker files │ ├── .github/ # GitHub config │ └── workflows/ │ ├── package.json ├── tsconfig.json ├── nest-cli.json ├── .eslintrc.js ├── .prettierrc ├── docker-compose.yml └── README.md ``` ## 8.2 Coding Standards ### TypeScript Configuration ```json // tsconfig.json { "compilerOptions": { "module": "commonjs", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "target": "ES2022", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", "incremental": true, "skipLibCheck": true, "strictNullChecks": true, "noImplicitAny": true, "strictBindCallApply": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "paths": { "@/*": ["src/*"], "@common/*": ["src/common/*"], "@modules/*": ["src/modules/*"], "@config/*": ["src/config/*"] } } } ``` ### ESLint Configuration ```javascript // .eslintrc.js module.exports = { parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.json', sourceType: 'module', }, plugins: ['@typescript-eslint/eslint-plugin'], extends: [ 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', ], root: true, env: { node: true, jest: true, }, ignorePatterns: ['.eslintrc.js'], rules: { '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/explicit-function-return-type': 'warn', '@typescript-eslint/explicit-module-boundary-types': 'warn', '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], 'no-console': 'warn', }, }; ``` ### Naming Conventions ```typescript // Files: kebab-case session.controller.ts send-message.dto.ts api-key.guard.ts // Classes: PascalCase class SessionController {} class SendMessageDto {} class ApiKeyGuard {} // Interfaces: PascalCase with 'I' prefix (optional) interface ISessionConfig {} interface SessionConfig {} // Also acceptable // Functions/Methods: camelCase function createSession() {} async sendMessage() {} // Variables: camelCase const sessionId = 'abc'; let messageCount = 0; // Constants: UPPER_SNAKE_CASE const MAX_RETRY_COUNT = 3; const DEFAULT_TIMEOUT = 30000; // Enums: PascalCase with PascalCase values enum SessionStatus { Created = 'created', Ready = 'ready', Disconnected = 'disconnected', } ``` ## 8.3 Module Structure ### Standard Module Template ```typescript // modules/example/example.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ExampleController } from './example.controller'; import { ExampleService } from './example.service'; import { ExampleRepository } from './example.repository'; import { Example } from './entities/example.entity'; @Module({ imports: [TypeOrmModule.forFeature([Example])], controllers: [ExampleController], providers: [ExampleService, ExampleRepository], exports: [ExampleService], }) export class ExampleModule {} ``` ### Controller Template ```typescript // modules/example/example.controller.ts import { Controller, Get, Post, Body, Headers, Param, Delete, UseGuards, HttpCode, HttpStatus, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; import { ApiKeyGuard } from '@common/guards/api-key.guard'; import { ExampleService } from './example.service'; import { CreateExampleDto } from './dto/create-example.dto'; import { ExampleResponseDto } from './dto/example-response.dto'; @ApiTags('examples') @Controller('examples') @UseGuards(ApiKeyGuard) export class ExampleController { constructor(private readonly exampleService: ExampleService) {} @Post() @HttpCode(HttpStatus.CREATED) @ApiOperation({ summary: 'Create example' }) @ApiResponse({ status: 201, type: ExampleResponseDto }) async create( @Body() dto: CreateExampleDto, @Headers('x-request-id') requestId?: string ): Promise { return this.exampleService.create(dto, { requestId }); } @Get(':id') @ApiOperation({ summary: 'Get example by ID' }) @ApiResponse({ status: 200, type: ExampleResponseDto }) async findOne(@Param('id') id: string): Promise { return this.exampleService.findOne(id); } @Delete(':id') @HttpCode(HttpStatus.NO_CONTENT) @ApiOperation({ summary: 'Delete example' }) async remove(@Param('id') id: string): Promise { return this.exampleService.remove(id); } } ``` ### Service Template ```typescript // modules/example/example.service.ts import { Injectable, NotFoundException, Logger } from '@nestjs/common'; import { ExampleRepository } from './example.repository'; import { CreateExampleDto } from './dto/create-example.dto'; import { Example } from './entities/example.entity'; @Injectable() export class ExampleService { private readonly logger = new Logger(ExampleService.name); constructor(private readonly repository: ExampleRepository) {} async create( dto: CreateExampleDto, context?: { requestId?: string } ): Promise { this.logger.log(`Creating example: ${dto.name}`, context); const example = this.repository.create(dto); return this.repository.save(example); } async findOne(id: string): Promise { const example = await this.repository.findOne({ where: { id } }); if (!example) { throw new NotFoundException(`Example with ID ${id} not found`); } return example; } async remove(id: string): Promise { const example = await this.findOne(id); await this.repository.remove(example); this.logger.log(`Deleted example: ${id}`); } } ``` ### DTO Template ```typescript // modules/example/dto/create-example.dto.ts import { IsString, IsOptional, MaxLength, IsUrl } from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class CreateExampleDto { @ApiProperty({ description: 'Example name', example: 'My Example' }) @IsString() @MaxLength(100) name: string; @ApiPropertyOptional({ description: 'Optional description' }) @IsOptional() @IsString() @MaxLength(500) description?: string; @ApiPropertyOptional({ description: 'Callback URL' }) @IsOptional() @IsUrl({ protocols: ['https'] }) callbackUrl?: string; } ``` ## 8.4 Git Workflow ### Branch Strategy ```mermaid gitGraph commit id: "initial" branch develop commit id: "setup" branch feature/session-api commit id: "session controller" commit id: "session service" checkout develop merge feature/session-api branch feature/webhook commit id: "webhook impl" checkout develop merge feature/webhook checkout main merge develop tag: "v1.0.0" checkout develop branch hotfix/bug-fix commit id: "fix bug" checkout main merge hotfix/bug-fix tag: "v1.0.1" checkout develop merge hotfix/bug-fix ``` ### Branch Naming ``` main # Production-ready code develop # Integration branch feature/* # New features bugfix/* # Bug fixes hotfix/* # Production hotfixes release/* # Release preparation Examples: feature/session-management feature/webhook-retry bugfix/qr-code-timeout hotfix/security-patch release/1.0.0 ``` ### Commit Message Convention ``` ():