Skip to main content

Development Guide - EML Backend

1. Introduction

This document provides details about the code structure, execution flow, design patterns, and best practices used in EML Backend. Its goal is to facilitate code understanding and serve as a reference for new developers joining the team.


2. Project Structure

The project follows a NestJS-based structure, organized as follows:

EML-Backend/
│── src/
│ ├── modules/ # Main system modules
│ │ ├── auth/ # Authentication module
│ │ ├── users/ # User management
│ │ ├── calls/ # Call management
│ │ ├── messages/ # Message management
│ │ ├── notifications/ # Notification system
│ │ ├── reports/ # Report generation
│ ├── common/ # Common utilities and decorators
│ ├── config/ # Environment variable configuration
│ ├── main.ts # Application entry point
│── test/ # Unit and integration tests
│── docker-compose.yml # Docker environment configuration
│── package.json # Project dependencies
│── README.md # Main documentation

Each module in src/modules/ follows this structure:

modules/
├── moduleName/
│ ├── moduleName.module.ts # Module definition
│ ├── moduleName.controller.ts # Controller handling routes
│ ├── moduleName.service.ts # Business logic
│ ├── dto/ # Data Transfer Object definitions
│ ├── entities/ # Model or entity definitions

3. Execution Flows

3.1 Authentication Flow

  1. The user sends credentials to POST /auth/login.
  2. The AuthController receives and validates the request.
  3. The database is queried via UserService.
  4. If credentials are correct, a JWT token is generated.
  5. The response returns the generated token.
@Post('login')
async login(@Body() loginDto: LoginDto) {
return this.authService.validateUser(loginDto);
}

3.2 Message Sending Flow

  1. A request is received at POST /messages/send with a recipient and message.
  2. The MessageService stores the message in the database.
  3. An event is sent to RabbitMQ for asynchronous processing.
  4. A consumer in the Notification Service processes the message and sends it to an external API.
@Post('send')
async sendMessage(@Body() messageDto: SendMessageDto) {
return this.messageService.processMessage(messageDto);
}

4. Design Patterns Used

4.1 Dependency Injection

NestJS dependency injection is used to facilitate module management.

@Injectable()
export class UserService {
constructor(private readonly userRepository: UserRepository) {}
}

4.2 Repository Pattern

TypeORM is used with a Repository pattern for database access:

@EntityRepository(User)
export class UserRepository extends Repository<User> {
async findByEmail(email: string): Promise<User | null> {
return this.findOne({ where: { email } });
}
}

4.3 Event-Driven Architecture (RabbitMQ)

RabbitMQ is used for microservice communication without direct dependencies.

@RabbitSubscribe({ exchange: 'notifications', routingKey: 'message.sent', queue: 'notifications_queue' })
async handleMessage(payload: MessagePayloadDto) {
console.log(`Message received: ${payload.message}`);
}

5. Configuration and Environment Variables

The .env file defines sensitive configurations:

PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/eml_db
JWT_SECRET=supersecret
RABBITMQ_URL=amqp://rabbitmq:5672

Loaded in the code using ConfigService:

@Injectable()
export class ConfigService {
constructor(private readonly config: ConfigModule) {}

get databaseUrl(): string {
return this.config.get('DATABASE_URL');
}
}

6. Testing and Coverage

Unit and integration tests are implemented using Jest.

6.1 User Service Unit Test

describe('UserService', () => {
let service: UserService;
let repository: UserRepository;

beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [UserService, { provide: UserRepository, useValue: mockRepository }],
}).compile();

service = module.get<UserService>(UserService);
repository = module.get<UserRepository>(UserRepository);
});

it('should find a user by email', async () => {
repository.findByEmail.mockResolvedValue(mockUser);
const user = await service.getUserByEmail('[email protected]');
expect(user).toEqual(mockUser);
});
});

6.2 API Gateway Integration Test

describe('AuthController (e2e)', () => {
it('/auth/login (POST)', () => {
return request(app.getHttpServer())
.post('/auth/login')
.send({ email: '[email protected]', password: '12345' })
.expect(200)
.expect(res => {
expect(res.body).toHaveProperty('accessToken');
});
});
});

7. Conclusion

This Development Guide provides a detailed overview of the code, execution flows, and best practices in EML Backend. For further information, reviewing the documentation of each module is recommended.

🚀 This document will be updated as the system evolves.