Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/marchena96/Paradigma-lab1/llms.txt

Use this file to discover all available pages before exploring further.

LibraryService API relies exclusively on ASP.NET Core’s built-in DI container — no third-party IoC framework is used. Every service is registered in Startup.ConfigureServices and resolved through standard constructor injection by the runtime. Controllers, services, and EF Core’s context pool all participate in the same container.

Registered services

Interface / TypeImplementationLifetimePurpose
ILibrariesServiceLibrariesServiceTransientCRUD operations on the Libraries table
IBooksServiceBooksServiceTransientCRUD operations on the Books table
IAuthenticationServiceAuthenticationServiceScopedValidates user credentials and returns a User object
JwtSettings— (singleton object bound from config)SingletonJWT configuration (Issuer, Audience, SecretKey) bound from appsettings.json
LibraryContext— (EF Core DbContext pool)DbContextPoolEF Core database context; pool size 20, Npgsql retry-on-failure
The registrations in Startup.ConfigureServices look like this:
services.AddSingleton(jwtSettings);
services.AddScoped<IAuthenticationService, AuthenticationService>();

services.AddTransient<ILibrariesService, LibrariesService>();
services.AddTransient<IBooksService, BooksService>();

services.AddDbContextPool<LibraryContext>(options =>
    options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), npgsqlOptions =>
    {
        npgsqlOptions.EnableRetryOnFailure(
            maxRetryCount: 1,
            maxRetryDelay: TimeSpan.FromSeconds(5),
            errorCodesToAdd: null);
    }),
    poolSize: 20);

Service interfaces

The DI container binds controllers and other consumers to interfaces, not concrete classes. The full signatures for the two primary service interfaces are:

ILibrariesService

public interface ILibrariesService
{
    Task<IEnumerable<Library>> Get(int[] ids);

    Task<Library> Add(Library library);

    Task<Library> Update(Library library);

    Task<bool> Delete(Library library);
}

IBooksService

public interface IBooksService
{
    Task<IEnumerable<Book>> Get(int libraryId, int[] ids);

    Task<Book> Add(Book book);

    Task<Book> Update(Book book);

    Task<bool> Delete(Book book);
}

IAuthenticationService

public interface IAuthenticationService
{
    Task<User> AuthenticateAsync(string email, string password);
}

Constructor injection examples

LibrariesController — single service dependency

LibrariesController declares one constructor parameter. The runtime resolves ILibrariesServiceLibrariesService automatically:
[ApiController]
[Route("api/[controller]")]
public class LibrariesController : ControllerBase
{
    private readonly ILibrariesService _librariesService;

    public LibrariesController(ILibrariesService librariesService)
    {
        _librariesService = librariesService;
    }

    // ...
}

BooksController — two service dependencies

BooksController needs both IBooksService and ILibrariesService (to validate that the parent library exists before operating on its books):
[ApiController]
[Route("api/libraries/{libraryId}/[controller]")]
public class BooksController : ControllerBase
{
    private readonly ILibrariesService _librariesService;
    private readonly IBooksService _booksService;

    public BooksController(IBooksService booksService, ILibrariesService librariesService)
    {
        _librariesService = librariesService;
        _booksService = booksService;
    }

    // ...
}

AuthController — configuration singleton + scoped service

AuthController injects the JwtSettings singleton (for token signing) alongside the scoped IAuthenticationService:
public AuthController(IAuthenticationService _authenticationService, JwtSettings _jwtSettings)
{
    authenticationService = _authenticationService;
    jwtSettings = _jwtSettings;
}

Why Transient for services?

LibrariesService and BooksService hold no state of their own beyond a single operation. They receive LibraryContext through constructor injection, perform one or more async database calls, and complete. Because EF Core’s DbContextPool manages context lifetime independently, the services themselves are safe to create and discard on every request — there is no benefit to caching them as scoped or singleton objects, and making them transient avoids any risk of stale change-tracker state leaking across requests.
AuthenticationService is registered as Scoped rather than Transient because it is conceptually tied to the lifetime of a single HTTP request — it validates credentials and returns a User for that request. The current implementation is stateless, but Scoped is the conventional lifetime for services that may in future access per-request state (e.g., IHttpContextAccessor).

Idempotency guard

The very first statement inside ConfigureServices is a guard that makes the entire method idempotent:
public void ConfigureServices(IServiceCollection services)
{
    // La WebApplicationFactory de los tests puede invocar ConfigureServices más de una vez
    // (el host de Program y el UseStartup<Startup> del propio test registran Startup).
    // Este guard hace la configuración idempotente y evita registros duplicados
    // (p. ej. "Scheme already exists: Bearer").
    if (services.Any(d => d.ServiceType == typeof(JwtSettings)))
    {
        return;
    }

    // ... rest of registration
}
During integration tests, ASP.NET Core’s WebApplicationFactory<T> triggers ConfigureServices twice: once when Program.CreateHostBuilder builds the real host, and again when UseStartup<Startup> runs inside the test’s custom WebApplicationFactory. Without the guard, the second invocation would attempt to re-add the JWT Bearer scheme to an already-configured authentication builder, throwing "Scheme already exists: Bearer". The guard uses JwtSettings as a sentinel because it is the first thing registered in the method. If it is already present in the service collection, the entire configuration block has already run and there is nothing left to do.
This pattern is specific to the classic Startup-based hosting model. In Minimal API projects the problem does not arise because there is no Startup class to be double-invoked.

Build docs developers (and LLMs) love