Skip to main content

System Architecture

SGRH follows a layered architecture (modular monolith) pattern with strict separation of concerns and dependency inversion. The system is built as a centralized backend exposing a REST API, consumed by decoupled Web and Desktop clients.

Architecture Overview

Architecture Type

  • Modular Monolith - Single deployable unit with clear module boundaries
  • Layered Architecture - Strict layer separation with dependency rules
  • Centralized Backend - Single REST API serving multiple clients
  • Decoupled Clients - Web and Desktop applications consuming the API
  • Contract-Based Infrastructure - Infrastructure implements domain contracts

Solution Structure

The SGRH solution consists of 7 main projects:
SGRH
├── SGRH.Domain          # Core business logic and entities
├── SGRH.Application     # Use cases and application logic
├── SGRH.Infrastructure  # External integrations (AWS S3, SES)
├── SGRH.Persistence     # Database access with EF Core
├── SGRH.Auth            # Authentication and authorization
├── SGRH.Api             # REST API endpoints
├── SGRH.Web             # Blazor web client
└── SGRH.Desktop         # .NET MAUI desktop client

Layer Details

1. Domain Layer (SGRH.Domain)

The core of the business - contains all business logic, entities, and contracts.

Key Characteristics

  • Zero external dependencies - Does not depend on any other project
  • Rich domain models - Entities with encapsulated business logic
  • Contract definitions - Interfaces for infrastructure implementations
  • Business rules - Invariants and validation logic
Structure:
SGRH.Domain/
├── Entities/
│   ├── Clientes/
│   │   └── Cliente.cs
│   ├── Reservas/
│   │   ├── Reserva.cs
│   │   ├── DetalleReserva.cs
│   │   └── ReservaServicioAdicional.cs
│   ├── Habitaciones/
│   │   ├── Habitacion.cs
│   │   ├── HabitacionHistorial.cs
│   │   └── CategoriaHabitacion.cs
│   └── Auditoria/
│       ├── AuditoriaEvento.cs
│       └── AuditoriaEventoDetalle.cs
├── Abstractions/
│   ├── Repositories/
│   │   ├── IAuditoriaRepository.cs
│   │   └── ...
│   ├── Email/
│   │   ├── IEmailSender.cs
│   │   └── IAdminNotifier.cs
│   └── Policies/
│       └── IReservaDomainPolicy.cs
├── Base/
│   └── EntityBase.cs
├── Common/
│   └── Guard.cs
├── Enums/
├── Exceptions/
└── Contracts/
Example: Customer Entity
SGRH.Domain/Entities/Clientes/Cliente.cs
public sealed class Cliente : EntityBase
{
    public int ClienteId { get; private set; }
    public string NationalId { get; private set; } = default!;
    public string NombreCliente { get; private set; } = default!;
    public string ApellidoCliente { get; private set; } = default!;
    public string Email { get; private set; } = default!;
    public string Telefono { get; private set; } = default!;

    public Cliente(
        string nationalId,
        string nombreCliente,
        string apellidoCliente,
        string email,
        string telefono)
    {
        Guard.AgainstNullOrWhiteSpace(nationalId, nameof(nationalId), 20);
        Guard.AgainstNullOrWhiteSpace(nombreCliente, nameof(nombreCliente), 100);
        Guard.AgainstNullOrWhiteSpace(apellidoCliente, nameof(apellidoCliente), 100);
        Guard.AgainstNullOrWhiteSpace(email, nameof(email), 100);
        Guard.AgainstNullOrWhiteSpace(telefono, nameof(telefono), 20);

        NationalId = nationalId;
        NombreCliente = nombreCliente;
        ApellidoCliente = apellidoCliente;
        Email = email;
        Telefono = telefono;
    }

    public void ActualizarDatos(
        string nombreCliente,
        string apellidoCliente,
        string email,
        string telefono)
    {
        // Validation and update logic
    }
}
Notice how the entity validates all inputs and exposes behavior through methods rather than exposing setters. This is a key principle of domain-driven design.

2. Application Layer (SGRH.Application)

Orchestrates use cases and application workflows.

Responsibilities

  • Defines application use cases
  • Orchestrates domain objects
  • Contains DTOs for data transfer
  • Validates application-level concerns
  • No direct database access
  • No external SDK dependencies
Dependencies: Only depends on SGRH.Domain Structure:
SGRH.Application/
├── UseCases/
│   ├── Clientes/
│   ├── Reservas/
│   ├── Habitaciones/
│   └── Reportes/
├── DTOs/
│   ├── Requests/
│   └── Responses/
└── Validators/

3. Infrastructure Layer (SGRH.Infrastructure)

Implements domain contracts for external services.

Implementations

  • Amazon S3 integration for file storage
  • Amazon SES integration for email notifications
  • External service adapters
  • Cloud service clients
Dependencies: SGRH.Domain Structure:
SGRH.Infrastructure/
├── StorageS3/
│   └── S3StorageService.cs
├── EmailSES/
│   └── SESEmailService.cs
└── DependencyInjection/
    └── InfrastructureServiceRegistration.cs

4. Persistence Layer (SGRH.Persistence)

Handles database access using Entity Framework Core.

Data Access

  • Entity Framework Core 8.0
  • MySQL provider (note: configured for SQL Server in code)
  • Repository implementations
  • Database context configuration
  • Migrations management
Dependencies: SGRH.Domain Key Component:
SGRH.Persistence/Context/SGRHDbContext.cs
public class SGRHDbContext : DbContext
{
    public SGRHDbContext(DbContextOptions<SGRHDbContext> options)
        : base(options)
    {
    }

    // DbSets for all entities
    public DbSet<Cliente> Clientes { get; set; }
    public DbSet<Reserva> Reservas { get; set; }
    public DbSet<Habitacion> Habitaciones { get; set; }
    // ... more entities
}

5. Auth Module (SGRH.Auth)

Handles authentication and authorization.

Security Features

  • JWT token generation and validation
  • Authorization policies
  • Authentication services
  • Role-based access control
Structure:
SGRH.Auth/
├── Services/
│   └── TokenService.cs
├── Policies/
└── Extensions/

6. API Layer (SGRH.Api)

The entry point exposing REST endpoints.

API Features

  • REST controllers
  • Swagger/OpenAPI documentation
  • Security configuration
  • Dependency injection setup
  • Request/response handling
Dependencies: SGRH.Application, SGRH.Infrastructure, SGRH.Persistence, SGRH.Auth Project Configuration:
SGRH.Api/SGRH.Api.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="8.0.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\SGRH.Application\SGRH.Application.csproj" />
    <ProjectReference Include="..\SGRH.Auth\SGRH.Auth.csproj" />
    <ProjectReference Include="..\SGRH.Infrastructure\SGRH.Infrastructure.csproj" />
    <ProjectReference Include="..\SGRH.Persistence\SGRH.Persistence.csproj" />
  </ItemGroup>
</Project>
Controllers:
SGRH.Api/Controllers/
├── ClientesController.cs
├── ReservasController.cs
├── HabitacionesController.cs
├── ReportesController.cs
└── AuditoriaController.cs
Program.cs Bootstrap:
SGRH.Api/Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Database configuration
builder.Services.AddDbContext<SGRHDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

var app = builder.Build();

// Configure middleware pipeline
if (!app.Environment.IsDevelopment())
{
    app.UseHttpsRedirection();
}

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();
app.MapControllers();

app.Run();

7. Client Applications

Web Client (SGRH.Web)

Blazor Server

  • Server-side Blazor application
  • Consumes REST API via HTTP
  • No business logic - pure presentation
  • Real-time UI updates

Desktop Client (SGRH.Desktop)

.NET MAUI Blazor Hybrid

  • Cross-platform desktop application
  • Blazor UI in native container
  • Role-based interfaces (Admin/Receptionist)
  • Consumes REST API via HTTP
  • No business logic - pure presentation

Dependency Flow

The architecture enforces strict dependency rules:
Critical Rule: The Domain layer has zero dependencies. All other layers depend on the domain, never the reverse. This is the essence of dependency inversion.

Key Architectural Patterns

Dependency Inversion

The domain defines interfaces, infrastructure implements them:
SGRH.Domain/Abstractions/Email/IEmailSender.cs
public interface IEmailSender
{
    Task<EmailSendResult> SendEmailAsync(EmailMessage message);
}
SGRH.Infrastructure/EmailSES/SESEmailService.cs
public class SESEmailService : IEmailSender
{
    public async Task<EmailSendResult> SendEmailAsync(EmailMessage message)
    {
        // AWS SES implementation
    }
}

Domain Policies

Complex business rules are encapsulated in policy objects:
SGRH.Domain/Abstractions/Policies/IReservaDomainPolicy.cs
public interface IReservaDomainPolicy
{
    void EnsureHabitacionDisponible(int habitacionId, DateTime entrada, 
        DateTime salida, int? excludeReservaId);
    
    void EnsureHabitacionNoEnMantenimiento(int habitacionId, 
        DateTime entrada, DateTime salida);
    
    decimal GetTarifaAplicada(int habitacionId, DateTime fechaEntrada);
    
    int GetTemporadaId(DateTime fecha);
}
Policies are injected into domain entities to enforce complex rules:
SGRH.Domain/Entities/Reservas/Reserva.cs
public void AgregarHabitacion(int habitacionId, IReservaDomainPolicy policy)
{
    Guard.AgainstNull(policy, nameof(policy));
    EnsureEditable();
    
    policy.EnsureHabitacionDisponible(
        habitacionId, FechaEntrada, FechaSalida,
        ReservaId == 0 ? null : ReservaId);
    
    policy.EnsureHabitacionNoEnMantenimiento(
        habitacionId, FechaEntrada, FechaSalida);
    
    var tarifa = policy.GetTarifaAplicada(habitacionId, FechaEntrada);
    
    _habitaciones.Add(new DetalleReserva(ReservaId, habitacionId, tarifa));
}

Benefits of This Architecture

Testability

Each layer can be tested in isolation with mocked dependencies

Maintainability

Clear boundaries make changes predictable and contained

Flexibility

Easy to swap implementations (e.g., change from SQL Server to MySQL)

Domain Focus

Business logic is isolated and independent of technical concerns

Next Steps

Quick Start

Set up and run SGRH on your local machine

API Reference

Explore the REST API endpoints

Build docs developers (and LLMs) love