TechsBCN.Platform.Application 0.0.37

dotnet add package TechsBCN.Platform.Application --version 0.0.37
                    
NuGet\Install-Package TechsBCN.Platform.Application -Version 0.0.37
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TechsBCN.Platform.Application" Version="0.0.37" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TechsBCN.Platform.Application" Version="0.0.37" />
                    
Directory.Packages.props
<PackageReference Include="TechsBCN.Platform.Application" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TechsBCN.Platform.Application --version 0.0.37
                    
#r "nuget: TechsBCN.Platform.Application, 0.0.37"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TechsBCN.Platform.Application@0.0.37
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TechsBCN.Platform.Application&version=0.0.37
                    
Install as a Cake Addin
#tool nuget:?package=TechsBCN.Platform.Application&version=0.0.37
                    
Install as a Cake Tool

TechsBCN.Platform.Application

TechsBCN.Platform.Application Es la capa de lógica de aplicación de Platform. Su objetivo es que los proyectos no tengan que implementar desde cero la gestión de usuarios, autenticación, tareas en background ni otras funcionalidades comunes, proporcionando los servicios base, contratos y DTOs que cada proyecto extiende o usa directamente.

Depende únicamente de TechsBCN.Platform.Domain.

Estructura

Application/
├── DTOs/
│   ├── Base.cs
│   ├── BasicItem.cs
│   ├── ListRequest.cs
│   ├── ListResponse.cs
│   ├── TaskMessage.cs
│   ├── User.cs
│   ├── Email/
│   │   ├── EmailData.cs
│   │   └── Attachment.cs
│   └── BlobStorage/
│       └── Blob.cs
├── Exceptions/
│   ├── TechsBcnException.cs
│   ├── EntityNotFoundException.cs
│   ├── DataValidationException.cs
│   └── MissingSetupException.cs
├── Repositories/
│   ├── IBaseRepository.cs
│   ├── IUserRepository.cs
│   ├── ITokenRepository.cs
│   ├── ITaskRepository.cs
│   ├── IEmailRepository.cs
│   ├── IMessagingRepository.cs
│   └── IBlobStorageRepository.cs
├── Services/
│   ├── Interfaces/
│   │   ├── ICrudService.cs
│   │   ├── IUserService.cs
│   │   ├── ISecurityService.cs
│   │   └── ITaskService.cs
│   └── Implementations/
│       ├── CrudService.cs
│       ├── UserService.cs
│       ├── SecurityService.cs
│       ├── TaskService.cs
│       ├── BaseTemplateService.cs
│       └── EmailBaseTemplateService.cs
└── Setup/
    └── MapperProfile.cs

DTOs (DTOs/)

Los DTOs son objetos simples cuya función es trasladar datos entre las distintas capas de la aplicación y actúan como contratos para conseguir desacoplamiento entre la capa de dominio, de infraestructura y de API. Se usan únicamente en la capa Application y no contienen lógica de negocio.

Platform define los DTOs base que los proyectos reutilizan directamente.

Base

DTO base del que extienden el resto de DTOs de Platform. El objetivo es que todos los DTOs tengan un Id sin definirlo en cada uno.

public abstract class Base
{
    public long Id { get; set; }
}

BasicItem

DTO que representa una referencia mínima a una entidad: solo su identificador y su nombre. El objetivo es proporcionar una estructura común para listas desplegables o referencias a entidades sin exponer todos sus datos. Extiende Base.

public class BasicItem : Base
{
    public string? Name { get; set; }
}

ListRequest

DTO que define un contrato estándar de filtrado y paginación. El objetivo es que todos los listados reutilicen la misma estructura de entrada sin duplicarla en cada proyecto.

public class ListRequest
{
    public string? Query { get; set; }
    public int? Offset { get; set; }
    public int? Limit { get; set; }
    public string? OrderBy { get; set; }
    public bool? OrderAsc { get; set; }
}

ListResponse<T>

DTO que define un contrato estándar de respuesta paginada. El objetivo es que todos los listados devuelvan la misma estructura sin duplicarla en cada proyecto. Solo se crea un DTO de listado propio cuando el flujo de negocio requiere campos adicionales, heredando de ListResponse<T> y añadiendo únicamente las propiedades necesarias para ese flujo.

public class ListResponse<T>
{
    public IEnumerable<T>? Items { get; set; }
    public int TotalCount { get; set; }
}

TaskMessage<TType>

DTO que permite consultar desde la API si una tarea en background está en curso, ha finalizado o ha fallado. La restricción where TType : Enum obliga a que el parámetro genérico sea siempre un enum, lo que garantiza que los tipos de tarea sean un conjunto cerrado y predecible. Extiende Base.

public class TaskMessage<TType> : Base where TType : Enum
{
    public Guid Reference { get; set; }
    public TType? Type { get; set; }
    public string? Details { get; set; }
    public DateTime CreatedOn { get; set; }
    public DateTime StartedOn { get; set; }
    public DateTime? CompletedOn { get; set; }
    public string? Errors { get; set; }
}

User

DTO que define la estructura base de usuario compartida en todos los proyectos para no reimplementarla cada vez. Se usa como contrato de UserService<TUser>.

public class User : Base
{
    public string Name { get; set; } = default!;
    public string Email { get; set; } = default!;
    public string Password { get; set; } = default!;
}

EmailData y Attachment

DTOs que definen los datos necesarios para enviar un email de forma que los servicios funcionen igual independientemente del proveedor de envío configurado. El campo Guid de Attachment se usa como identificador inline: cuando el email tiene imágenes incrustadas en el HTML, se referencia cada imagen con ${guid} en el BodyHtml y se adjunta con ese mismo Guid.

public class EmailData
{
    public string Subject { get; set; } = default!;
    public string Recipient { get; set; } = default!;
    public string Sender { get; set; } = default!;
    public string BodyHtml { get; set; } = default!;
    public string? Bcc { get; set; }
    public IEnumerable<Attachment>? Attachments { get; set; }
}

public class Attachment
{
    public string Name { get; set; } = default!;
    public byte[] Content { get; set; } = default!;
    public Guid Guid { get; set; } = Guid.Empty;
}

Servicios (Services/)

Las interfaces definidas en Services/Interfaces/ (ICrudService, IUserService, ISecurityService, ITaskService) son los contratos que los proyectos referencian para inyección de dependencias. Las implementaciones base están en Services/Implementations/ y se extienden desde el proyecto consumidor.

CrudService<TDTO, TEntity>

CrudService<TDTO, TEntity> es la clase base genérica para operaciones CRUD. Su objetivo es que los servicios del proyecto tengan Create, Read, Update y Delete sin implementarlo desde cero. Usa AutoMapper para el mapeo entre DTO y entidad.

Cuando una entidad sigue un patrón CRUD estándar, los servicios deben reutilizar CrudService. No aplica cuando el servicio implementa un proceso distinto de un CRUD, como autenticación, generación de documentos u orquestación de flujos.

Operaciones que proporciona
  • Create(TDTO dto) — mapea el DTO a entidad, la persiste y devuelve el Id generado.
  • Read(long id) — devuelve el DTO correspondiente al id.
  • Update(TDTO dto) — actualiza la entidad a partir del DTO.
  • Delete(long id) — elimina la entidad.
public class AddressService(
    IMapper Mapper,
    IAddressRepository AddressRepository
) : CrudService<DTOs.Address, Address>(Mapper, AddressRepository), IAddressService
{
    public new long Create(DTOs.Address dto)
    {
        EnsureValidAlias(dto.Alias);
        return base.Create(dto);
    }
}

SecurityService<TUser>

SecurityService<TUser> es el servicio base de autenticación y credenciales. Su objetivo es que los proyectos no reimplementen el hashing de contraseñas, la validación de complejidad ni la verificación de credenciales. El proyecto lo extiende añadiendo únicamente los métodos que necesite.

Operaciones que proporciona
  • CheckCredentials(string email, string password) — verifica credenciales y devuelve el usuario.
  • Hash(string rawData) — genera hash SHA-256 de una contraseña.
  • CheckPasswordComplexity(string password) — valida la complejidad mínima (6 caracteres, mayúsculas, minúsculas y dígito).
  • UpdateCredentials(long userId, string newPassword) — valida complejidad, hashea y persiste la nueva contraseña buscando el usuario por id. Lanza DataValidationException si la contraseña no cumple la complejidad y EntityNotFoundException si el usuario no existe.

Requiere un IUserRepository<TUser> que el proyecto implementa extendiendo la interfaz de Platform:

public interface IUserRepository : IUserRepository<User> { }
Patrón de extensión
public interface ISecurityService : ISecurityService<User>
{
    void ResetAccount(string token, string newPassword);
}

public class SecurityService(
    IUserRepository UserRepository,
    ITokenRepository TokenRepository,
    IConfiguration Configuration
) : TechsBCN.Platform.Application.Services.Implementations.SecurityService<User>(UserRepository), ISecurityService
{
    public void ResetAccount(string token, string newPassword)
    {
        var tokenEntity = TokenRepository.Get(t => t.Guid == token).FirstOrDefault()
            ?? throw new EntityNotFoundException($"Token [{token}] does not exist.");

        var expirationMinutes = Configuration.GetValue<int?>("Security:TokenExpirationMinutes") ?? 30;
        if (tokenEntity.CreatedOn.AddMinutes(expirationMinutes) < DateTime.UtcNow)
            throw new DataValidationException($"Token [{token}] is expired.");

        base.UpdateCredentials(Convert.ToInt64(tokenEntity.Content), newPassword);
        TokenRepository.Delete(tokenEntity);
        TokenRepository.Save();
    }
}

UserService<TUser>

UserService<TUser> es el servicio CRUD de usuarios. Su objetivo es que los proyectos no reimplementen las validaciones habituales de usuario: formato y unicidad de email, seguridad de contraseña y hash. El proyecto solo lo extiende para añadir la lógica específica que necesite.

Operaciones que proporciona
  • Create(DTOs.User dto) — valida nombre, email (formato y unicidad) y hace hash de la contraseña antes de persistir.
  • Update(DTOs.User dto) — valida nombre y email; si el email cambia comprueba unicidad; actualiza contraseña solo si se proporciona y cumple complejidad.
  • Read(long id) — heredado de CrudService.
  • Delete(long id) — heredado de CrudService.
  • UpdatePassword(long userId, string newPassword) — actualiza solo la contraseña con hash.

Requiere IUserRepository<TUser> e ISecurityService<TUser> del proyecto.

Patrón de extensión
public interface IUserService : IUserService<User> { }

public class UserService(
    IMapper Mapper,
    IUserRepository UserRepository,
    ISecurityService SecurityService
) : TechsBCN.Platform.Application.Services.Implementations.UserService<User>(Mapper, UserRepository, SecurityService), IUserService
{
}

TaskService<TTaskType>

TaskService<TTaskType> es el servicio de consulta de tareas en background. Su objetivo es que los proyectos puedan exponer el estado de sus tareas sin implementar esta lógica desde cero. Se usa directamente desde Platform o se extiende si el proyecto necesita lógica adicional.

Operaciones que proporciona
  • Read(Guid reference) — devuelve el TaskMessage de la tarea si existe; null si no se encuentra.

Hay que registrar ITaskRepository. Los proyectos que necesiten exponer el estado de tareas deben registrar también el servicio:

services.AddScoped<ITaskRepository<MyTaskType>, TechsBCN.Platform.Infrastructure.Repositories.TaskRepository<MyTaskType>>();
services.AddScoped<ITaskService<MyTaskType>, TechsBCN.Platform.Application.Services.Implementations.TaskService<MyTaskType>>();
Patrón de extensión
public interface ITaskService : TechsBCN.Platform.Application.Services.Interfaces.ITaskService<MyTaskType> { }

public class TaskService(
    IMapper Mapper,
    ITaskRepository TaskRepository
) : TechsBCN.Platform.Application.Services.Implementations.TaskService<MyTaskType>(Mapper, TaskRepository), ITaskService
{
}

BaseTemplateService

BaseTemplateService es la clase base para servicios de generación de documentos. Su objetivo es que los proyectos no reimplementen la lógica de sustitución de placeholders.

Una plantilla (template) es un fichero HTML con marcadores de posición llamados placeholders — cadenas con el formato ${NombrePropiedad} que se sustituyen por valores reales en tiempo de ejecución. Cada plantilla vive en su propio directorio y contiene un fichero template.html.

Operaciones que proporciona
  • FillTemplate(string template, object model) — sustituye los placeholders ${PropertyName} con los valores de las propiedades públicas del modelo.
  • ResolveTemplatePath(string templateRelativePath) — resuelve la ruta absoluta del directorio de la plantilla.

EmailDataBaseTemplateService

EmailDataBaseTemplateService es la clase base para servicios de generación de emails. Hereda de BaseTemplateService y su objetivo es que los proyectos no reimplementen la incrustación de imágenes ni la orquestación del flujo de generación de emails.

Los datos de cada plantilla se definen en un DTO propio que extiende EmailData, de modo que Subject y Recipient los establece quien llama al servicio, y el mismo objeto sirve de modelo para FillTemplate. Expone GenerateEmailData para orquestar la generación completa: resuelve la ruta, lee el template, aplica el reemplazo e incrusta internamente las imágenes como adjuntos inline.

Patrón de extensión
public class AccountRecoveryEmailData : EmailData
{
    public string AccountRecoveryLink { get; set; } = default!;
}

public interface INotificationTemplateService
{
    EmailData GetAccountRecoveryEmail(AccountRecoveryEmailData data);
}

public class NotificationTemplateService : EmailDataBaseTemplateService, INotificationTemplateService
{
    public EmailData GetAccountRecoveryEmail(AccountRecoveryEmailData data)
    {
        return GenerateEmailData(data, "Templates/account-recovery");
    }
}

Interfaces (Services/Interfaces/)

Su objetivo es separar el contrato de la implementación para que el resto del proyecto dependa de lo que hace un servicio, no de cómo lo hace. Así se puede cambiar o extender la implementación sin modificar el código que la consume. Cuando el servicio gestiona una entidad con operaciones CRUD, la interfaz extiende ICrudService<TDTO, TEntity>. Los métodos adicionales al CRUD se definen directamente en la interfaz del proyecto.

public interface IEntityService : ICrudService<DTOs.Entity, Entity>
{
    /// <summary>Returns a paginated list of entities.</summary>
    /// <param name="request">Filter and pagination parameters.</param>
    EntityListResponse List(ListRequest request);

    /// <summary>Enqueues a synchronization task for the entity.</summary>
    /// <param name="id">Entity identifier.</param>
    /// <returns>Reference of the enqueued task.</returns>
    Guid EnqueueSync(long id);
}

Los métodos se documentan con XML docs: <summary>, <param> por cada parámetro y <returns> cuando el método devuelve un valor.

Interfaces de repositorios (Repositories/)

Los repositorios definen los contratos de acceso a datos que implementa la capa Infrastructure. Los servicios dependen de estos contratos, no de implementaciones concretas, lo que permite cambiar la implementación sin afectar a la lógica de negocio.

Los repositorios de entidades heredan de IBaseRepository<TEntity>. Si no necesitan métodos adicionales al contrato base, el cuerpo de la interfaz queda vacío. Los repositorios de integraciones externas definen su propio contrato según las operaciones que expone el sistema externo.

ITokenRepository e IUserRepository<TUser>

Su objetivo es que SecurityService y UserService puedan funcionar en cualquier proyecto sin que cada uno reimplemente el acceso a tokens y usuarios. El proyecto extiende IUserRepository<TUser> con su propio tipo y añade solo las consultas específicas que necesite.

public interface ITokenRepository<TTokenType> : IBaseRepository<Token<TTokenType>>;
public interface IUserRepository<TUser> : IBaseRepository<TUser> where TUser : User;
public interface IUserRepository : IUserRepository<User> { }

IEmailRepository

IEmailRepository es el contrato de envío de email. Su objetivo es que los servicios puedan enviar emails sin saber cómo se envían. La implementación concreta usa Microsoft Graph y se registra con services.AddScoped<IEmailRepository, MicrosoftGraphEmailRepository>().

  • SendAsync(EmailData emailData) — envía el email. Los adjuntos inline se referencian en BodyHtml mediante ${guid}.
public class MyService(IEmailRepository EmailRepository)
{
    public async Task Notify(string recipient)
    {
        await EmailRepository.SendAsync(new EmailData
        {
            Subject = "Notification",
            Recipient = recipient,
            BodyHtml = "<p>Hello</p>"
        });
    }
}

IBlobStorageRepository

IBlobStorageRepository es el contrato de almacenamiento de ficheros. Su objetivo es que los servicios puedan subir y descargar blobs sin saber dónde se guardan. La implementación concreta usa Azure Blob Storage y se registra con services.AddScoped<IBlobStorageRepository, AzureBlobStorageRepository>().

  • UploadAsync(Blob blob) — sube el blob y devuelve el Guid asignado.
  • DownloadAsync(Guid guid) — devuelve el Blob correspondiente al identificador.
public class MyService(IBlobStorageRepository BlobStorageRepository)
{
    public async Task<Guid> Store(byte[] content) =>
        await BlobStorageRepository.UploadAsync(new Blob
        {
            Name = "document.pdf",
            Content = content
        });
}

Excepciones (Exceptions/)

Platform define la jerarquía base de excepciones para que todos los proyectos comuniquen errores de forma consistente. ErrorMiddleware las mapea automáticamente a los códigos HTTP correspondientes sin necesidad de capturarlas manualmente en los controllers.

TechsBcnException es la clase base de todas las excepciones de Platform. Sus subclases directas son:

  • EntityNotFoundException — se lanza cuando no se encuentra una entidad por su identificador. Se traduce a HTTP 404.
  • DataValidationException — se lanza cuando los datos de entrada no cumplen las reglas de negocio (email inválido, contraseña débil…). Se traduce a HTTP 400.
  • MissingSetupException — se lanza al arrancar la aplicación cuando falta una clave de configuración obligatoria. Se traduce a HTTP 500.

Los proyectos pueden crear sus propios tipos heredando de estas subclases cuando necesitan distinguir errores específicos:

public class MyEntityNotFoundException(string message) : EntityNotFoundException(message) { }
public class MyValidationException(string message) : DataValidationException(message) { }

El nombre del tipo es lo que ErrorMiddleware devuelve en ApiError.Id, así que heredar de la subclase adecuada determina tanto el código HTTP como el identificador que recibe el cliente para traducir el error. Heredar directamente de TechsBcnException conserva el identificador pero se traduce a HTTP 500.

Setup (Setup/)

MapperProfile

Un perfil de AutoMapper es una clase que declara qué tipo se convierte en qué otro tipo. Al registrarlo, AutoMapper sabe cómo transformar un Task de dominio en un TaskMessage DTO, por ejemplo, sin que el código tenga que hacerlo manualmente.

MapperProfile centraliza los mappings entre entidades de dominio y DTOs de Platform. Los proyectos lo incluyen al configurar AutoMapper para no tener que redefinir los mappings base:

  • TaskTaskMessage
  • Domain.UserDTOs.User
services.AddAutoMapper(cfg =>
{
    cfg.AddProfile(new TechsBCN.Platform.Application.Setup.MapperProfile());
    cfg.AddProfile(new MyProject.Application.Setup.MapperProfile());
    cfg.AddProfile(new MyProject.Infrastructure.Persistence.Setup.MapperProfile());
    mapperConfiguration?.Invoke(cfg);
});

El MapperProfile del proyecto se organiza por #region por entidad. Cuando el DTO y la entidad tienen el mismo nombre se usan aliases de namespace para disambiguar. Los mapeos con lógica personalizada usan ForMember. ReverseMap() siempre y cuando sea bidireccional y no requiera configuración específica distinta en cada sentido.

public class MapperProfile : Profile
{
    public MapperProfile()
    {
        #region Entity

        CreateMap<DTOs.Entity, Domain.Entities.Entity>();
        CreateMap<Domain.Entities.Entity, DTOs.Entity>();

        #endregion Entity
    }
}
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on TechsBCN.Platform.Application:

Package Downloads
TechsBCN.Platform.Infrastructure

Platform base project for Infrastructure to be used in TechsBCN standard projects

TechsBCN.Platform.API

Platform base project for API to be used in TechsBCN standard projects

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.37 38 9/18/2026
0.0.36 128 9/11/2026
0.0.35 188 8/25/2026
0.0.34 108 8/25/2026
0.0.33 112 8/25/2026
0.0.32 182 7/31/2026
0.0.31 508 7/10/2026
0.0.30 324 6/30/2026
0.0.29 150 6/18/2026
0.0.27 143 6/12/2026
0.0.26 133 6/12/2026
0.0.25 136 6/4/2026
0.0.24 133 6/1/2026
0.0.23 124 6/1/2026
0.0.22 234 5/27/2026