The Karma Ecommerce authentication system provides secure user login functionality using Angular’s reactive patterns and clean architecture principles.
Overview
The authentication feature follows the CQRS (Command Query Responsibility Segregation) pattern with a clear separation of concerns:
- UI Layer: Login page component handles user interaction
- Application Layer: Query handlers process login requests
- Domain Layer: Repository abstraction defines contracts
- Infrastructure Layer: AuthService implements API communication
Login Flow
User initiates login
The user clicks the login button, triggering the login() method with credentials.
Component calls repository
The LoginPage component invokes the UsuarioRepository’s loginUsuario() method.
API request sent
The AuthService makes an HTTP GET request to http://localhost:8080/login/{usuario}/{contrasena}.
Response handling
RxJS operators process the response and provide user feedback via alerts.
Component Implementation
The login page is located at ~/workspace/source/src/app/usuario/infrastructure/ui/pages/login-page/login-page.ts:1:
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../../services/auth-service';
import { tap } from 'rxjs';
import { UsuarioRepository } from '../../../../domain/repositories/usuario.repository';
@Component({
selector: 'app-login-page',
imports: [],
templateUrl: './login-page.html',
styleUrl: './login-page.css',
})
export class LoginPage implements OnInit {
constructor(private authService: UsuarioRepository) {}
ngOnInit(): void {}
login(usuario: string, contrasena: string) {
this.authService.loginUsuario(usuario, contrasena).pipe(tap(usuario => {
console.log(usuario);
usuario ? alert('usuario y contraseña correctos!') : alert('usuario o contraseña incorrectos');
})).subscribe();
}
}
Template
The login template at ~/workspace/source/src/app/usuario/infrastructure/ui/pages/login-page/login-page.html:1:
<button (click)="login('jose', 'chivito3000ydash')">LOGIN</button>
Security Note: The current implementation hardcodes credentials in the template for demonstration purposes. In production, credentials should be captured via form inputs with proper validation.
CQRS Pattern: Query & Handler
Login Query
The LoginUserQuery encapsulates login request data at ~/workspace/source/src/app/usuario/application/queries/login-user.query.ts:1:
export class LoginUserQuery {
constructor(readonly usuario: string, readonly contrasena: string) {}
}
Login Handler
The LoginUserHandler processes login queries at ~/workspace/source/src/app/usuario/application/usecases/queryHandlers/login-user.handler.ts:1:
import { Observable } from "rxjs";
import { UsuarioRepository } from "../../../domain/repositories/usuario.repository";
import { LoginUserQuery } from "../../queries/login-user.query";
import { Usuario } from "../../../domain/models/usuario";
import { Injectable } from "@angular/core";
@Injectable()
export class LoginUserHandler {
constructor(private usuarioRepository: UsuarioRepository){}
handle(query: LoginUserQuery): Observable<Usuario> {
return this.usuarioRepository.loginUsuario(query.usuario, query.contrasena);
}
}
Repository Pattern
Domain Repository Interface
The abstract repository defines the contract at ~/workspace/source/src/app/usuario/domain/repositories/usuario.repository.ts:1:
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>;
}
AuthService Implementation
The concrete implementation at ~/workspace/source/src/app/usuario/infrastructure/services/auth-service.ts:1:
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Usuario } from '../../domain/models/usuario';
import { UsuarioRepository } from '../../domain/repositories/usuario.repository';
@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' } }
);
}
}
API Integration
Login Endpoint
URL: http://localhost:8080/login/{usuario}/{contrasena}
Method: GET
Response: Usuario object
interface Usuario {
id: number,
usuario: string,
contrasena: string
}
Request Example
curl http://localhost:8080/login/jose/chivito3000ydash
RxJS Response Handling
The authentication system uses RxJS operators for reactive programming:
- tap: Side effects for logging and user feedback
- subscribe: Triggers the observable execution
this.authService.loginUsuario(usuario, contrasena).pipe(
tap(usuario => {
console.log(usuario);
usuario ?
alert('usuario y contraseña correctos!') :
alert('usuario o contraseña incorrectos');
})
).subscribe();
Important: The current implementation uses browser alerts for user feedback. Consider replacing with toast notifications or inline validation messages for better UX.
User Model
The Usuario interface defines the user entity at ~/workspace/source/src/app/usuario/domain/models/usuario.ts:1:
export interface Usuario {
id: number,
usuario: string,
contrasena: string
}