Overview
The infrastructure layer is where the “how” of your application lives. This layer implements the repository interfaces defined in the domain layer and handles all external dependencies like HTTP requests, databases, file systems, and third-party APIs.
This is the only layer that should directly interact with:
HTTP clients (Angular’s HttpClient)
Database connections
External APIs
File systems
Framework-specific features
What Goes in the Infrastructure Layer
The infrastructure layer typically contains:
Concrete implementations of domain repository interfaces that handle data persistence and retrieval. Example: AuthService implements UsuarioRepository
Services that communicate with external systems:
REST APIs via HttpClient
GraphQL clients
WebSocket connections
Cloud services (AWS, Azure, etc.)
Angular components (pages, layouts) that present data to users:
Login page
Registration form
User dashboard
Code that adapts external data formats to domain models:
DTO to domain entity mapping
Data transformation
API response parsing
Repository Implementation
The infrastructure layer implements repository interfaces defined in the domain layer. This is where HTTP requests, database queries, or any other data access mechanism is implemented.
AuthService: Implementing UsuarioRepository
Location : ~/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' } }
);
}
}
Breaking Down AuthService
Makes the service available for dependency injection throughout the application
providedIn: 'root' means this is a singleton service available app-wide
Angular creates one instance and shares it everywhere
implements UsuarioRepository
Declares that this class fulfills the contract defined in the domain layer
TypeScript enforces that all methods from UsuarioRepository are implemented
Allows the service to be injected wherever UsuarioRepository is required
Base URL for the authentication API
In production, this would come from environment configuration
Points to the backend server handling authentication
Angular’s HTTP client for making requests
Injected via constructor dependency injection
Returns Observables for all HTTP operations
Handles request/response serialization automatically
HTTP Integration
Let’s examine how each method integrates with the backend API.
Login Implementation
loginUsuario ( usuario : string , contrasena : string ): Observable < Usuario > {
return this.httpClient.get<Usuario>(this.BASE_URL + 'login' + '/' + usuario + '/' + contrasena);
}
HTTP Request Details:
Method : GET
URL : http://localhost:8080/login/{usuario}/{contrasena}
Response : JSON object matching the Usuario interface
Type Safety : get<Usuario>() tells TypeScript the response type
Using GET for login with credentials in the URL is not secure for production. Credentials should be sent in the request body with POST, and the URL should be served over HTTPS. URL parameters can be logged by servers, proxies, and appear in browser history.
Better approach for production:
loginUsuario ( usuario : string , contrasena : string ): Observable < Usuario > {
const body = { usuario , contrasena };
return this.httpClient.post<Usuario>(
` ${ this . BASE_URL } login` ,
body ,
{ headers : { 'Content-Type' : 'application/json' } }
);
}
Registration Implementation
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' } }
);
}
HTTP Request Details:
Method : POST
URL : http://localhost:8080/registro
Body : { "usuario": "username", "contrasena": "password" }
Headers : Content-Type: application/json
Response : JSON object with the created user data
Request payload sent to the server Username for the new account
Password for the new account
Observable Pattern with HttpClient
Angular’s HttpClient returns Observables, which provide powerful asynchronous capabilities:
Basic Subscription
Error Handling
Data Transformation
this . authService . loginUsuario ( 'john' , 'password123' ). subscribe ({
next : ( usuario ) => {
console . log ( 'Logged in:' , usuario );
// Navigate to dashboard
},
error : ( err ) => {
console . error ( 'Login failed:' , err );
// Show error message
},
complete : () => {
console . log ( 'Request complete' );
}
});
import { catchError } from 'rxjs/operators' ;
import { throwError } from 'rxjs' ;
loginUsuario ( usuario : string , contrasena : string ): Observable < Usuario > {
return this.httpClient.get<Usuario>(
this.BASE_URL + 'login' + '/' + usuario + '/' + contrasena
).pipe(
catchError(error => {
console.error( 'Login error:' , error);
if (error.status === 401) {
return throwError (() => new Error ( 'Invalid credentials' ));
}
return throwError (() => new Error ( 'Server error' ));
})
);
}
import { map } from 'rxjs/operators' ;
registrarUsuario ( usuario : string , contrasena : string ): Observable < Usuario > {
const body = { usuario , contrasena };
return this.httpClient.post<any>(
` ${ this . BASE_URL } registro` ,
body
).pipe(
// Transform API response to domain model
map ( response => ({
id: response . userId ,
usuario: response . username ,
contrasena: '' // Never return password
}))
);
}
RxJS Operators for HTTP
Common RxJS operators used with HTTP requests:
Operator Purpose Example mapTransform response data map(res => res.data)catchErrorHandle errors gracefully catchError(err => of(null))tapSide effects (logging) tap(data => console.log(data))retryRetry failed requests retry(3)timeoutCancel slow requests timeout(5000)switchMapChain dependent requests switchMap(user => getProfile(user.id))
Observables from HttpClient are cold and single-use . They don’t execute until you subscribe, and complete after one response.
Infrastructure Layer Structure
src/app/usuario/infrastructure/
├── services/
│ └── auth-service.ts # Repository implementation
└── pages/
├── login/
│ ├── login.component.ts
│ ├── login.component.html
│ └── login.component.css
└── registro/
├── registro.component.ts
├── registro.component.html
└── registro.component.css
UI Components (Pages)
UI components in the infrastructure layer consume handlers from the application layer:
import { Component } from '@angular/core' ;
import { Router } from '@angular/router' ;
import { LoginUserHandler } from '../../application/usecases/queryHandlers/login-user.handler' ;
import { LoginUserQuery } from '../../application/queries/login-user.query' ;
@ Component ({
selector: 'app-login' ,
templateUrl: './login.component.html'
})
export class LoginComponent {
username = '' ;
password = '' ;
error = '' ;
constructor (
private loginHandler : LoginUserHandler ,
private router : Router
) {}
login () {
const query = new LoginUserQuery ( this . username , this . password );
this . loginHandler . handle ( query ). subscribe ({
next : ( usuario ) => {
localStorage . setItem ( 'currentUser' , JSON . stringify ( usuario ));
this . router . navigate ([ '/products' ]);
},
error : ( err ) => {
this . error = 'Invalid username or password' ;
}
});
}
}
Component responsibilities:
User Input : Collect username and password from the form
Create Query : Build a LoginUserQuery with the input
Call Handler : Use the injected LoginUserHandler
Handle Response : Navigate on success, show error on failure
State Management : Store user data (in localStorage, state management, etc.)
External Dependencies
The infrastructure layer is the only place that should import:
Angular HTTP
Angular Router
Angular Forms
Third-party Libraries
import { HttpClient , HttpHeaders } from '@angular/common/http' ;
Environment Configuration
In production applications, configuration should come from environment files:
import { environment } from '../../../environments/environment' ;
@ Injectable ({
providedIn: 'root' ,
})
export class AuthService implements UsuarioRepository {
private readonly BASE_URL = environment . apiUrl ;
// ... rest of implementation
}
export const environment = {
production: false ,
apiUrl: 'http://localhost:8080/'
};
export const environment = {
production: true ,
apiUrl: 'https://api.karma-ecommerce.com/'
};
Dependency Injection Configuration
To wire up the repository implementation, configure your Angular module:
import { NgModule } from '@angular/core' ;
import { CommonModule } from '@angular/common' ;
import { HttpClientModule } from '@angular/common/http' ;
import { UsuarioRepository } from '../domain/repositories/usuario.repository' ;
import { AuthService } from './services/auth-service' ;
import { RegisterUserHandler } from '../application/usecases/commandHandlers/register-user.handler' ;
import { LoginUserHandler } from '../application/usecases/queryHandlers/login-user.handler' ;
@ NgModule ({
declarations: [
// Components go here
],
imports: [
CommonModule ,
HttpClientModule // Required for HttpClient
],
providers: [
// Provide the concrete implementation for the abstract repository
{ provide: UsuarioRepository , useClass: AuthService },
// Register handlers
RegisterUserHandler ,
LoginUserHandler
]
})
export class UsuarioModule {}
The key line is { provide: UsuarioRepository, useClass: AuthService }. This tells Angular’s DI system: “Whenever someone requests UsuarioRepository, provide an instance of AuthService.”
Testing Infrastructure Layer
Testing HTTP services with mocked HttpClient:
import { TestBed } from '@angular/core/testing' ;
import { HttpClientTestingModule , HttpTestingController } from '@angular/common/http/testing' ;
import { AuthService } from './auth-service' ;
import { Usuario } from '../../domain/models/usuario' ;
describe ( 'AuthService' , () => {
let service : AuthService ;
let httpMock : HttpTestingController ;
beforeEach (() => {
TestBed . configureTestingModule ({
imports: [ HttpClientTestingModule ],
providers: [ AuthService ]
});
service = TestBed . inject ( AuthService );
httpMock = TestBed . inject ( HttpTestingController );
});
afterEach (() => {
httpMock . verify (); // Ensure no outstanding requests
});
it ( 'should login user successfully' , () => {
const mockUser : Usuario = {
id: 1 ,
usuario: 'john' ,
contrasena: 'hashed_password'
};
service . loginUsuario ( 'john' , 'password123' ). subscribe ( user => {
expect ( user ). toEqual ( mockUser );
});
const req = httpMock . expectOne ( 'http://localhost:8080/login/john/password123' );
expect ( req . request . method ). toBe ( 'GET' );
req . flush ( mockUser ); // Simulate server response
});
});