Skip to main content

Overview

UsuarioRepository is an abstract class that defines the contract for user authentication and registration operations in the Karma Ecommerce application. It follows the Repository Pattern from Domain-Driven Design (DDD), providing an abstraction layer between the domain logic and data access implementation.

Why Abstract?

The repository is defined as an abstract class to:
  • Decouple domain logic from infrastructure: The domain layer doesn’t depend on specific HTTP implementations
  • Enable dependency injection: Angular’s DI system can inject different implementations
  • Facilitate testing: Mock implementations can be easily injected for unit tests
  • Follow SOLID principles: Dependency Inversion Principle (DIP) - depend on abstractions, not concretions

Source Code

import { Observable } from "rxjs";
import { Usuario } from "../models/usuario";

export abstract class UsuarioRepository {
    abstract loginUsuario(usuario: string, contrasena: string): Observable<Usuario>;
    abstract registrarUsuario(usuario: string, contrasena: string): Observable<Usuario>;
}
Location: src/app/usuario/domain/repositories/usuario.repository.ts

Methods

loginUsuario()

Authenticates a user with their credentials and returns user information.
abstract loginUsuario(usuario: string, contrasena: string): Observable<Usuario>
usuario
string
required
The username for authentication
contrasena
string
required
The user’s password
Observable<Usuario>
Observable
Returns an RxJS Observable that emits a Usuario object upon successful authentication

registrarUsuario()

Registers a new user in the system.
abstract registrarUsuario(usuario: string, contrasena: string): Observable<Usuario>
usuario
string
required
The desired username for the new account
contrasena
string
required
The password for the new account
Observable<Usuario>
Observable
Returns an RxJS Observable that emits the newly created Usuario object

Implementation

AuthService

The concrete implementation of UsuarioRepository is provided by the AuthService class.
@Injectable({
  providedIn: 'root',
})
export class AuthService implements UsuarioRepository {
  private readonly BASE_URL = 'http://localhost:8080/'
  
  constructor(private httpClient: HttpClient) { }

  loginUsuario(usuario: string, contrasena: string): Observable<Usuario> {
    return this.httpClient.get<Usuario>(
      `${this.BASE_URL}login/${usuario}/${contrasena}`
    );
  }
  
  registrarUsuario(usuario: string, contrasena: string): Observable<Usuario> {
    const body = { usuario, contrasena };
    return this.httpClient.post<Usuario>(
      `${this.BASE_URL}registro`,
      body,
      { headers: { 'Content-Type': 'application/json' } }
    );
  }
}
Location: src/app/usuario/infrastructure/services/auth-service.ts

Usage Examples

In Query Handlers (Login)

The LoginUserHandler uses the repository to authenticate users:
@Injectable()
export class LoginUserHandler {
    constructor(private usuarioRepository: UsuarioRepository) {}

    handle(query: LoginUserQuery): Observable<Usuario> {
        return this.usuarioRepository.loginUsuario(
            query.usuario, 
            query.contrasena
        );
    }
}
Location: src/app/usuario/application/usecases/queryHandlers/login-user.handler.ts

In Command Handlers (Registration)

The RegisterUserHandler uses the repository to register new users:
@Injectable()
export class RegisterUserHandler {
    constructor(private usuarioRepository: UsuarioRepository) {}

    handle(command: RegisterUserCommand): Observable<Usuario> {
        return this.usuarioRepository.registrarUsuario(
            command.usuario, 
            command.contrasena
        );
    }
}
Location: src/app/usuario/application/usecases/commandHandlers/register-user.handler.ts

Subscribing to Results

// Login example
usuarioRepository.loginUsuario('john_doe', 'password123')
  .subscribe({
    next: (usuario: Usuario) => {
      console.log('Login successful:', usuario.id);
      // Handle successful login
    },
    error: (error) => {
      console.error('Login failed:', error);
      // Handle login error
    }
  });

// Registration example
usuarioRepository.registrarUsuario('jane_doe', 'securePass456')
  .subscribe({
    next: (usuario: Usuario) => {
      console.log('User registered:', usuario.usuario);
      // Navigate to login or home page
    },
    error: (error) => {
      console.error('Registration failed:', error);
      // Show error message to user
    }
  });

Architecture Context

Repository Pattern Benefits

  1. Separation of Concerns: Domain logic is isolated from data access details
  2. Testability: Easy to mock for unit testing handlers
  3. Flexibility: Can swap implementations (HTTP, localStorage, mock) without changing domain code
  4. Single Responsibility: Repository only handles data access, handlers manage business logic

CQRS Integration

The repository is used by both:
  • Query Handlers: LoginUserHandler for read operations (authentication)
  • Command Handlers: RegisterUserHandler for write operations (user creation)
This aligns with the CQRS (Command Query Responsibility Segregation) pattern used throughout the application.

AuthService

HTTP implementation of UsuarioRepository

Usuario Model

User domain model definition

LoginUserHandler

Query handler for user authentication

RegisterUserHandler

Command handler for user registration

Type Definitions

// Usuario interface
interface Usuario {
    id: number;
    usuario: string;
    contrasena: string;
}

// Observable from RxJS
import { Observable } from "rxjs";

Error Handling

When using the repository methods, handle potential errors:
usuarioRepository.loginUsuario(username, password)
  .pipe(
    catchError((error: HttpErrorResponse) => {
      if (error.status === 401) {
        // Invalid credentials
        return throwError(() => new Error('Invalid username or password'));
      } else if (error.status === 500) {
        // Server error
        return throwError(() => new Error('Server error, please try again'));
      }
      return throwError(() => error);
    })
  )
  .subscribe({
    next: (usuario) => { /* Handle success */ },
    error: (err) => { /* Handle error */ }
  });

Build docs developers (and LLMs) love