BiblioTech ya tiene un dominio maduro, una arquitectura desacoplada, una API completa y una batería de pruebas —unitarias y de integración— que confirman que todo funciona como se espera. Queda un último paso, el que convierte todo ese trabajo en algo que un usuario real puede usar: desplegarlo. Esta lección publica la API con dotnet publish, la empaqueta en un contenedor Docker sencillo, configura su comportamiento de forma distinta según el entorno (desarrollo frente a producción), y menciona, sin entrar en el detalle de ninguna plataforma concreta, cómo encaja todo esto en un pipeline de integración y despliegue continuos. Es la última lección del curso: cierra no solo el Módulo 9, sino el recorrido completo que empezó con un simple "Hola Mundo" en la primera lección del Módulo 1.

Contenido

  1. dotnet publish: de código fuente a artefacto desplegable
  2. Contenedorización básica con Docker
  3. Configuración por entorno: appsettings.json y variables de entorno
  4. La cadena de conexión de producción
  5. Consideraciones básicas de CI/CD
  6. BiblioTech, desplegada

  1. dotnet publish: de código fuente a artefacto desplegable

Hasta ahora, cada lección del curso se ejecutó con dotnet run, que compila y arranca la aplicación en un solo paso pensado para el desarrollo. dotnet publish genera, en cambio, la versión lista para desplegar de la aplicación: los ficheros exactos que hacen falta para ejecutarla en otra máquina, sin necesitar el código fuente ni el SDK completo de .NET, solo el runtime:

dotnet publish BiblioTech.Api -c Release -o ./publicado
  • -c Release: compila en modo Release en vez de Debug (el que se usa por defecto en desarrollo), con optimizaciones activadas y sin la información adicional que facilita depurar paso a paso (Lección 4) pero que no hace falta en producción.
  • -o ./publicado: la carpeta de salida con el resultado: los ensamblados (.dll) de BiblioTech.Api, BiblioTech.Dominio y BiblioTech.Persistencia, sus dependencias, y un ejecutable de entrada.

El resultado de ./publicado es exactamente lo que se copia a un servidor, o lo que se empaqueta dentro de un contenedor Docker en el siguiente apartado: ya no hace falta el código fuente, ni BiblioTech.Pruebas, ni ningún proyecto de desarrollo para ejecutar la aplicación desde ahí.

  1. Contenedorización básica con Docker

Docker empaqueta una aplicación junto con todo lo que necesita para ejecutarse (el runtime de .NET, en este caso) en una unidad autocontenida llamada imagen, que se ejecuta de forma idéntica en cualquier máquina con Docker instalado, sin importar qué más tenga instalado esa máquina. Un Dockerfile sencillo para BiblioTech.Api:

# Etapa 1: compilar y publicar la aplicacion con el SDK completo de .NET
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /origen

COPY . .
RUN dotnet restore BiblioTech.Api
RUN dotnet publish BiblioTech.Api -c Release -o /publicado

# Etapa 2: la imagen final solo necesita el runtime, no el SDK completo
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app

COPY --from=build /publicado .

EXPOSE 8080
ENTRYPOINT ["dotnet", "BiblioTech.Api.dll"]
docker build -t bibliotech-api .
docker run -p 8080:8080 bibliotech-api

Este Dockerfile usa una técnica llamada compilación multietapa (multi-stage build): la primera etapa (build) usa la imagen del SDK completo (más pesada) solo para compilar y publicar; la segunda etapa (final), la que realmente se distribuye y ejecuta, parte de una imagen mucho más ligera que solo trae el runtime de ASP.NET Core, sin las herramientas de compilación que ya no hacen falta una vez generado /publicado. docker run -p 8080:8080 expone el puerto 8080 del contenedor en el puerto 8080 de la máquina anfitriona, permitiendo acceder a la API exactamente igual que si se ejecutara con dotnet run en local.

  1. Configuración por entorno: appsettings.json y variables de entorno

ASP.NET Core distingue automáticamente entre entornos (desarrollo, producción...) y carga la configuración correspondiente combinando varios ficheros y fuentes, en un orden concreto de prioridad:

BiblioTech.Api/
├── appsettings.json               // configuracion base, comun a todos los entornos
├── appsettings.Development.json   // solo se aplica en el entorno "Development" (dotnet run local)
└── appsettings.Production.json    // solo se aplica en el entorno "Production" (desplegado)
// appsettings.json (base)
{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}
// appsettings.Development.json: mas detalle de logging en local
{
  "ConnectionStrings": {
    "BiblioTech": "Data Source=bibliotech-dev.db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Debug"
    }
  }
}
// appsettings.Production.json: sin cadena de conexion aqui (ver apartado 4)
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  }
}
Fuente de configuración Cuándo se aplica Prioridad
appsettings.json Siempre, como base Más baja
appsettings.{Entorno}.json Solo si ASPNETCORE_ENVIRONMENT coincide con {Entorno} Media
Variables de entorno Siempre que estén definidas Alta
Argumentos de línea de comandos Solo si se pasan al ejecutar La más alta

Esta jerarquía permite, por ejemplo, tener valores de desarrollo cómodos por defecto en appsettings.Development.json (una base SQLite local), mientras que en producción una variable de entorno sobrescribe ese mismo valor sin tocar ningún fichero desplegado —justo lo que resuelve el requisito no funcional de seguridad de la Lección 2 (no dejar datos sensibles en el código fuente ni en los ficheros de configuración que se versionan).

  1. La cadena de conexión de producción

La cadena de conexión de la base de datos de producción (Módulo 5, Entity Framework Core sobre SQLite, decisión de la Lección 2) es exactamente el tipo de dato que nunca debe escribirse directamente en appsettings.Production.json ni en ningún fichero que se suba al control de versiones. En su lugar, se define como variable de entorno en el servidor o en el propio contenedor Docker:

export ConnectionStrings__BiblioTech="Data Source=/datos/bibliotech-produccion.db"

docker run -p 8080:8080 \
  -e ConnectionStrings__BiblioTech="Data Source=/datos/bibliotech-produccion.db" \
  -e ASPNETCORE_ENVIRONMENT=Production \
  bibliotech-api

El doble guion bajo (__) en ConnectionStrings__BiblioTech es la forma en que ASP.NET Core traduce la jerarquía de una variable de entorno a la misma estructura anidada que tendría en JSON ("ConnectionStrings": { "BiblioTech": "..." }). El código de Program.cs que ya se escribió en 09-03 (builder.Configuration.GetConnectionString("BiblioTech")) no necesita ningún cambio: lee siempre de la misma forma, sin importar si el valor final vino de un fichero JSON de desarrollo o de una variable de entorno de producción. Esta es la misma idea de fondo que motivó IRepositorioBiblioteca en el Módulo 8: el código que consume un valor no debería necesitar saber de dónde viene exactamente ese valor.

  1. Consideraciones básicas de CI/CD

CI/CD (Continuous Integration/Continuous Deployment, integración y despliegue continuos) es la práctica de automatizar, mediante un pipeline, los pasos que van desde un cambio de código hasta su despliegue, en vez de ejecutarlos a mano cada vez. Un pipeline típico para BiblioTech, sin atarse a ninguna plataforma concreta (GitHub Actions, GitLab CI, Azure DevOps y otras ofrecen todas variantes de lo mismo), encadenaría estos pasos:

flowchart LR
    A["Cambio de codigo<br/>(git push)"] --> B["Build<br/>dotnet build"]
    B --> C["Test<br/>dotnet test<br/>(unitarias + integracion, Leccion 4)"]
    C --> D{"Todo en verde?"}
    D -->|"Si"| E["Publish<br/>dotnet publish + docker build"]
    D -->|"No"| F["Pipeline se detiene<br/>sin desplegar nada roto"]
    E --> G["Despliegue<br/>(plataforma concreta, fuera del alcance de esta leccion)"]

El valor central de este pipeline no es la automatización en sí misma, sino la garantía que aporta: ningún cambio llega a producción sin haber pasado, automáticamente y sin intervención manual, por exactamente las mismas pruebas que se ejecutaron en la Lección 4. El checklist de calidad de esa lección deja de depender de que alguien se acuerde de ejecutarlo a mano antes de cada despliegue.

  1. BiblioTech, desplegada

Con esto, BiblioTech completa su recorrido: una imagen Docker publicable con docker build, configurada de forma distinta en desarrollo y en producción sin cambiar código, con su cadena de conexión de producción fuera del código fuente, y con un camino claro (aunque no implementado en detalle en este curso) hacia un pipeline de CI/CD que verifique automáticamente cada cambio antes de desplegarlo. El sistema que empezó como un ejercicio de consola en el Módulo 1 termina siendo una aplicación real, con una API HTTP, persistencia en base de datos, pruebas automatizadas, y un proceso de despliegue reproducible.

Errores Comunes y Consejos

  • Escribir la cadena de conexión de producción directamente en appsettings.Production.json: ese fichero suele versionarse junto con el resto del código fuente; cualquier dato sensible de producción debe llegar por variable de entorno o por un gestor de secretos, nunca escrito ahí.
  • Publicar en modo Debug en vez de Release: pierde las optimizaciones del compilador y publica información adicional pensada solo para depurar en desarrollo (Lección 4), no para ejecutarse en producción.
  • Usar una única etapa de Docker con el SDK completo para la imagen final: funciona, pero genera una imagen mucho más pesada de lo necesario, con herramientas de compilación que ya no se usan una vez publicada la aplicación; la compilación multietapa del apartado 2 resuelve esto sin esfuerzo extra.
  • Consejo: antes de dar por completado el despliegue, confirma explícitamente los tres puntos de esta lección por separado: que dotnet publish genera el artefacto correcto, que la imagen Docker arranca y responde en local con docker run, y que la configuración de producción no contiene ningún dato sensible escrito directamente en un fichero versionado.

Ejercicios

  1. Añade al Dockerfile del apartado 2 una variable de entorno ASPNETCORE_ENVIRONMENT=Production con la instrucción ENV, de forma que cualquier contenedor construido a partir de esta imagen arranque en modo producción por defecto, salvo que se indique lo contrario al ejecutar docker run.

  2. Describe, en tus propias palabras y sin necesidad de configurar ninguna plataforma real, los cuatro pasos del pipeline de CI/CD del apartado 5 aplicados a un cambio concreto: añadir el endpoint GET /socios/{id} que escribiste en el ejercicio 1 de la lección anterior.

Soluciones

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app

ENV ASPNETCORE_ENVIRONMENT=Production

COPY --from=build /publicado .

EXPOSE 8080
ENTRYPOINT ["dotnet", "BiblioTech.Api.dll"]

(1) Build: al hacer git push con el nuevo endpoint, el pipeline ejecuta dotnet build sobre toda la solución, confirmando que compila sin errores. (2) Test: ejecuta dotnet test, incluyendo tanto las pruebas unitarias del Módulo 8 como las pruebas de integración de la Lección 4 —si el endpoint nuevo no tuviera ninguna prueba de integración propia, sería buena práctica añadir una antes de este paso. (3) Todo en verde: si algún test falla, el pipeline se detiene aquí y el cambio no avanza. (4) Publish y despliegue: solo si el paso anterior fue exitoso, se ejecuta dotnet publish y docker build para generar la nueva imagen, que después se despliega según la plataforma elegida.

Conclusión

Este es el final del curso de Programación en C#. El recorrido empezó en el Módulo 1 con la instalación del entorno de desarrollo y un primer programa que escribía "Hola Mundo" por consola; continuó por las estructuras de control y el manejo de excepciones del Módulo 2; construyó, en el Módulo 3, el primer modelo de objetos real de BiblioTech —MaterialBibliotecario, Libro, Revista, Socio, Prestamo— con clases, herencia, polimorfismo y encapsulamiento; lo amplió en el Módulo 4 con interfaces, genéricos, colecciones, LINQ y programación asíncrona; le dio memoria persistente en el Módulo 5, con cuatro mecanismos de persistencia distintos y una primera conexión a servicios externos; exploró, en el Módulo 6, las capacidades más internas de .NET —reflexión, atributos, gestión de memoria y concurrencia—; le puso cara, en el Módulo 7, con cinco tecnologías de interfaz distintas, de escritorio, web y móvil; y maduró su diseño en el Módulo 8, con estándares de codificación, patrones de diseño, inyección de dependencias, pruebas unitarias y refactorización disciplinada.

Este último módulo no ha hecho más que reunir todo ese camino en un único sistema completo: unos requisitos claros, una arquitectura elegida con criterio, una API que expone el dominio construido durante nueve módulos, una batería de pruebas que respalda cada cambio, y un proceso de despliegue reproducible que lleva ese código, de forma fiable, hasta donde puede usarlo alguien de verdad.

De "Hola Mundo" a una aplicación desplegada en producción: ese es el trayecto completo de este curso, y es también, en esencia, el trayecto de cualquier proyecto real de software. Lo que sostiene ese trayecto no es haber memorizado la sintaxis de C#, sino haber practicado, una y otra vez sobre el mismo dominio, a tomar decisiones de diseño, a desacoplar responsabilidades, a confiar en las pruebas antes que en la suerte, y a pensar en quien usará el código tanto como en quien lo escribe. Esas son las herramientas que seguirán siendo útiles mucho después de que cualquier detalle concreto de sintaxis quede obsoleto. Enhorabuena por haber llegado hasta aquí: BiblioTech ha quedado terminada, pero lo aprendido para construirla no tiene por qué detenerse ahora. El siguiente proyecto en C# ya depende solo de ti.

Curso de Programación en C#

Módulo 1: Introducción a C#

Módulo 2: Estructuras de Control

Módulo 3: Programación Orientada a Objetos

Módulo 4: Conceptos Avanzados de C#

Módulo 5: Trabajando con Datos

Módulo 6: Temas Avanzados

Módulo 7: Construcción de Aplicaciones

Módulo 8: Mejores Prácticas y Patrones de Diseño

Módulo 9: Proyecto Final

© Copyright 2026. Todos los derechos reservados