Skip to main content

Overview

The RegisterUserCommand is a CQRS command object that encapsulates the data required to register a new user in the system. It follows the Command pattern, separating the request for user registration from the actual execution logic.

CQRS Pattern

This implementation follows the Command Query Responsibility Segregation (CQRS) pattern:
  • Commands (like RegisterUserCommand) represent write operations that change system state
  • Command Handlers (like RegisterUserHandler) contain the business logic to execute commands
  • This separation provides better testability, maintainability, and scalability

RegisterUserCommand Class

Source Code

export class RegisterUserCommand {
    constructor( readonly usuario: string, readonly contrasena: string){}
}
Location: src/app/usuario/application/commands/register-user.command.ts

Parameters

usuario
string
required
The username for the new user account. This is passed as a readonly property to ensure immutability.
contrasena
string
required
The password for the new user account. This is passed as a readonly property to ensure immutability.

Properties

The command has two readonly properties that are set via the constructor:
  • usuario: The username string
  • contrasena: The password string
Both properties are readonly to ensure command immutability, which is a key principle in CQRS.

RegisterUserHandler

Source Code

import { Injectable } from "@angular/core";
import { UsuarioRepository } from "../../../domain/repositories/usuario.repository";
import { RegisterUserCommand } from "../../commands/register-user.command";
import { Observable } from "rxjs";
import { Usuario } from "../../../domain/models/usuario";

@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

Handler Method

handle(command: RegisterUserCommand): Observable<Usuario>

Executes the user registration command. Parameters:
  • command: The RegisterUserCommand instance containing user registration data
Returns:
  • Observable<Usuario>: An RxJS Observable that emits the created Usuario object
Flow:
  1. Receives the command with user credentials
  2. Delegates to the UsuarioRepository to persist the new user
  3. Returns an Observable of the created user

Dependencies

usuarioRepository
UsuarioRepository
required
Repository interface that handles user persistence operations. Injected via Angular’s dependency injection.

Usage Example

Creating and Executing the Command

import { RegisterUserCommand } from '@app/usuario/application/commands/register-user.command';
import { RegisterUserHandler } from '@app/usuario/application/usecases/commandHandlers/register-user.handler';

// In your component or service
export class UserRegistrationService {
  constructor(private registerUserHandler: RegisterUserHandler) {}

  registerNewUser(username: string, password: string): Observable<Usuario> {
    // Create the command
    const command = new RegisterUserCommand(username, password);
    
    // Execute via the handler
    return this.registerUserHandler.handle(command);
  }
}

In an Angular Component

import { Component } from '@angular/core';
import { RegisterUserHandler } from '@app/usuario/application/usecases/commandHandlers/register-user.handler';
import { RegisterUserCommand } from '@app/usuario/application/commands/register-user.command';

@Component({
  selector: 'app-register',
  template: `
    <form (ngSubmit)="onSubmit()">
      <input [(ngModel)]="username" placeholder="Username" />
      <input [(ngModel)]="password" type="password" placeholder="Password" />
      <button type="submit">Register</button>
    </form>
  `
})
export class RegisterComponent {
  username = '';
  password = '';

  constructor(private registerUserHandler: RegisterUserHandler) {}

  onSubmit(): void {
    const command = new RegisterUserCommand(this.username, this.password);
    
    this.registerUserHandler.handle(command).subscribe({
      next: (user) => console.log('User registered:', user),
      error: (err) => console.error('Registration failed:', err)
    });
  }
}

Architecture Flow

Usuario Interface

export interface Usuario {
    id: number,
    usuario: string,
    contrasena: string
}

UsuarioRepository Interface

export abstract class UsuarioRepository {
    abstract loginUsuario(usuario: string, contrasena: string): Observable<Usuario>;
    abstract registrarUsuario(usuario: string, contrasena: string): Observable<Usuario>;
}

Best Practices

  1. Immutability: Command properties are readonly to prevent modification after creation
  2. Single Responsibility: The command only carries data; the handler contains logic
  3. Dependency Injection: Handler receives repository via Angular DI
  4. Observable Pattern: Returns Observable for async operations and reactive programming
  5. Separation of Concerns: Clear separation between command creation and execution

Benefits of This Pattern

  • Testability: Easy to unit test handlers independently
  • Maintainability: Clear structure and separation of concerns
  • Scalability: Can add middleware, validation, or logging without changing core logic
  • Reusability: Commands can be reused across different parts of the application
  • Type Safety: Strong typing ensures compile-time checks

See Also

Build docs developers (and LLMs) love