BiblioTech ja té un domini madur, una arquitectura desacoblada, una API completa i una bateria de proves —unitàries i d'integració— que confirmen que tot funciona com s'espera. Queda un últim pas, el que converteix tota aquesta feina en quelcom que un usuari real pot fer servir: desplegar-lo. Aquesta lliçó publica l'API amb dotnet publish, l'empaqueta en un contenidor Docker senzill, configura el seu comportament de forma diferent segons l'entorn (desenvolupament enfront de producció), i menciona, sense entrar en el detall de cap plataforma concreta, com encaixa tot això en un pipeline d'integració i desplegament continus. És la darrera lliçó del curs: tanca no només el Mòdul 9, sinó el recorregut complet que va començar amb un simple "Hola Món" a la primera lliçó del Mòdul 1.

Contingut

  1. dotnet publish: de codi font a artefacte desplegable
  2. Contenidorització bàsica amb Docker
  3. Configuració per entorn: appsettings.json i variables d'entorn
  4. La cadena de connexió de producció
  5. Consideracions bàsiques de CI/CD
  6. BiblioTech, desplegada

  1. dotnet publish: de codi font a artefacte desplegable

Fins ara, cada lliçó del curs s'executava amb dotnet run, que compila i arrenca l'aplicació en un sol pas pensat per al desenvolupament. dotnet publish, en canvi, genera la versió a punt per desplegar de l'aplicació: els fitxers exactes que calen per executar-la en una altra màquina, sense necessitar el codi font ni l'SDK complet de .NET, només el runtime:

dotnet publish BiblioTech.Api -c Release -o ./publicat
  • -c Release: compila en mode Release en comptes de Debug (el que es fa servir per defecte en desenvolupament), amb optimitzacions activades i sense la informació addicional que facilita depurar pas a pas (Lliçó 4) però que no cal en producció.
  • -o ./publicat: la carpeta de sortida amb el resultat: els assemblats (.dll) de BiblioTech.Api, BiblioTech.Domini i BiblioTech.Persistencia, les seves dependències, i un executable d'entrada.

El resultat de ./publicat és exactament el que es copia a un servidor, o el que s'empaqueta dins d'un contenidor Docker a l'apartat següent: ja no cal el codi font, ni BiblioTech.Proves, ni cap projecte de desenvolupament per executar l'aplicació des d'allà.

  1. Contenidorització bàsica amb Docker

Docker empaqueta una aplicació juntament amb tot el que necessita per executar-se (el runtime de .NET, en aquest cas) en una unitat autocontinguda anomenada imatge, que s'executa de forma idèntica en qualsevol màquina amb Docker instal·lat, sense importar què més hi hagi instal·lat aquella màquina. Un Dockerfile senzill per a BiblioTech.Api:

# Etapa 1: compilar i publicar l'aplicacio amb l'SDK complet 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 /publicat

# Etapa 2: la imatge final nomes necessita el runtime, no l'SDK complet
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app

COPY --from=build /publicat .

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

Aquest Dockerfile fa servir una tècnica anomenada compilació multietapa (multi-stage build): la primera etapa (build) fa servir la imatge de l'SDK complet (més pesada) només per compilar i publicar; la segona etapa (final), la que realment es distribueix i s'executa, parteix d'una imatge molt més lleugera que només porta el runtime d'ASP.NET Core, sense les eines de compilació que ja no calen un cop generat /publicat. docker run -p 8080:8080 exposa el port 8080 del contenidor al port 8080 de la màquina amfitriona, permetent accedir a l'API exactament igual que si s'executés amb dotnet run en local.

  1. Configuració per entorn: appsettings.json i variables d'entorn

ASP.NET Core distingeix automàticament entre entorns (desenvolupament, producció...) i carrega la configuració corresponent combinant diversos fitxers i fonts, en un ordre concret de prioritat:

BiblioTech.Api/
├── appsettings.json               // configuracio base, comuna a tots els entorns
├── appsettings.Development.json   // nomes s'aplica a l'entorn "Development" (dotnet run local)
└── appsettings.Production.json    // nomes s'aplica a l'entorn "Production" (desplegat)
// appsettings.json (base)
{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}
// appsettings.Development.json: mes detall de logging en local
{
  "ConnectionStrings": {
    "BiblioTech": "Data Source=bibliotech-dev.db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Debug"
    }
  }
}
// appsettings.Production.json: sense cadena de connexio aqui (vegeu apartat 4)
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  }
}
Font de configuració Quan s'aplica Prioritat
appsettings.json Sempre, com a base Més baixa
appsettings.{Entorn}.json Només si ASPNETCORE_ENVIRONMENT coincideix amb {Entorn} Mitjana
Variables d'entorn Sempre que estiguin definides Alta
Arguments de línia d'ordres Només si es passen en executar La més alta

Aquesta jerarquia permet, per exemple, tenir valors de desenvolupament còmodes per defecte a appsettings.Development.json (una base SQLite local), mentre que en producció una variable d'entorn sobreescriu aquest mateix valor sense tocar cap fitxer desplegat —just el que resol el requisit no funcional de seguretat de la Lliçó 2 (no deixar dades sensibles al codi font ni als fitxers de configuració que es versionen).

  1. La cadena de connexió de producció

La cadena de connexió de la base de dades de producció (Mòdul 5, Entity Framework Core sobre SQLite, decisió de la Lliçó 2) és exactament el tipus de dada que mai ha d'escriure's directament a appsettings.Production.json ni a cap fitxer que es pugi al control de versions. En comptes d'això, es defineix com a variable d'entorn al servidor o al mateix contenidor Docker:

export ConnectionStrings__BiblioTech="Data Source=/dades/bibliotech-produccio.db"

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

El doble guionet baix (__) a ConnectionStrings__BiblioTech és la forma en què ASP.NET Core tradueix la jerarquia d'una variable d'entorn a la mateixa estructura imbricada que tindria en JSON ("ConnectionStrings": { "BiblioTech": "..." }). El codi de Program.cs que ja es va escriure a 09-03 (builder.Configuration.GetConnectionString("BiblioTech")) no necessita cap canvi: llegeix sempre de la mateixa forma, sense importar si el valor final ve d'un fitxer JSON de desenvolupament o d'una variable d'entorn de producció. Aquesta és la mateixa idea de fons que va motivar IRepositoriBiblioteca al Mòdul 8: el codi que consumeix un valor no hauria de necessitar saber d'on ve exactament aquest valor.

  1. Consideracions bàsiques de CI/CD

CI/CD (Continuous Integration/Continuous Deployment, integració i desplegament continus) és la pràctica d'automatitzar, mitjançant un pipeline, els passos que van des d'un canvi de codi fins al seu desplegament, en comptes d'executar-los a mà cada vegada. Un pipeline típic per a BiblioTech, sense lligar-se a cap plataforma concreta (GitHub Actions, GitLab CI, Azure DevOps i altres ofereixen totes variants del mateix), encadenaria aquests passos:

flowchart LR
    A["Canvi de codi<br/>(git push)"] --> B["Build<br/>dotnet build"]
    B --> C["Test<br/>dotnet test<br/>(unitaries + integracio, Llico 4)"]
    C --> D{"Tot en verd?"}
    D -->|"Si"| E["Publish<br/>dotnet publish + docker build"]
    D -->|"No"| F["El pipeline s'atura<br/>sense desplegar res trencat"]
    E --> G["Desplegament<br/>(plataforma concreta, fora de l'abast d'aquesta llico)"]

El valor central d'aquest pipeline no és l'automatització en si mateixa, sinó la garantia que aporta: cap canvi arriba a producció sense haver passat, automàticament i sense intervenció manual, per exactament les mateixes proves que es van executar a la Lliçó 4. El checklist de qualitat d'aquella lliçó deixa de dependre que algú se'n recordi d'executar-lo a mà abans de cada desplegament.

  1. BiblioTech, desplegada

Amb això, BiblioTech completa el seu recorregut: una imatge Docker publicable amb docker build, configurada de forma diferent en desenvolupament i en producció sense canviar codi, amb la seva cadena de connexió de producció fora del codi font, i amb un camí clar (encara que no implementat en detall en aquest curs) cap a un pipeline de CI/CD que verifiqui automàticament cada canvi abans de desplegar-lo. El sistema que va començar com un exercici de consola al Mòdul 1 acaba sent una aplicació real, amb una API HTTP, persistència en base de dades, proves automatitzades, i un procés de desplegament reproduïble.

Errors Comuns i Consells

  • Escriure la cadena de connexió de producció directament a appsettings.Production.json: aquest fitxer sol versionar-se juntament amb la resta del codi font; qualsevol dada sensible de producció ha d'arribar per variable d'entorn o per un gestor de secrets, mai escrita allà.
  • Publicar en mode Debug en comptes de Release: perd les optimitzacions del compilador i publica informació addicional pensada només per depurar en desenvolupament (Lliçó 4), no per executar-se en producció.
  • Fer servir una única etapa de Docker amb l'SDK complet per a la imatge final: funciona, però genera una imatge molt més pesada del necessari, amb eines de compilació que ja no es fan servir un cop publicada l'aplicació; la compilació multietapa de l'apartat 2 resol això sense esforç addicional.
  • Consell: abans de donar per completat el desplegament, confirma explícitament els tres punts d'aquesta lliçó per separat: que dotnet publish genera l'artefacte correcte, que la imatge Docker arrenca i respon en local amb docker run, i que la configuració de producció no conté cap dada sensible escrita directament en un fitxer versionat.

Exercicis

  1. Afegeix al Dockerfile de l'apartat 2 una variable d'entorn ASPNETCORE_ENVIRONMENT=Production amb la instrucció ENV, de manera que qualsevol contenidor construït a partir d'aquesta imatge arrenqui en mode producció per defecte, tret que s'indiqui el contrari en executar docker run.

  2. Descriu, amb les teves pròpies paraules i sense necessitat de configurar cap plataforma real, els quatre passos del pipeline de CI/CD de l'apartat 5 aplicats a un canvi concret: afegir l'endpoint GET /socios/{id} que vas escriure a l'exercici 1 de la lliçó anterior.

Solucions

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

ENV ASPNETCORE_ENVIRONMENT=Production

COPY --from=build /publicat .

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

(1) Build: en fer git push amb el nou endpoint, el pipeline executa dotnet build sobre tota la solució, confirmant que compila sense errors. (2) Test: executa dotnet test, incloent-hi tant les proves unitàries del Mòdul 8 com les proves d'integració de la Lliçó 4 —si l'endpoint nou no tingués cap prova d'integració pròpia, seria bona pràctica afegir-ne una abans d'aquest pas. (3) Tot en verd: si algun test falla, el pipeline s'atura aquí i el canvi no avança. (4) Publish i desplegament: només si el pas anterior ha estat reeixit, s'executa dotnet publish i docker build per generar la nova imatge, que després es desplega segons la plataforma triada.

Conclusió

Aquest és el final del curs de Programació en C#. El recorregut va començar al Mòdul 1 amb la instal·lació de l'entorn de desenvolupament i un primer programa que escrivia "Hola Món" per consola; va continuar amb les estructures de control i la gestió d'excepcions del Mòdul 2; va construir, al Mòdul 3, el primer model d'objectes real de BiblioTech —MaterialBibliotecari, Llibre, Revista, Soci, Prestec— amb classes, herència, polimorfisme i encapsulament; el va ampliar al Mòdul 4 amb interfícies, genèrics, col·leccions, LINQ i programació asíncrona; li va donar memòria persistent al Mòdul 5, amb quatre mecanismes de persistència diferents i una primera connexió a serveis externs; va explorar, al Mòdul 6, les capacitats més internes de .NET —reflexió, atributs, gestió de memòria i concurrència—; li va posar cara, al Mòdul 7, amb cinc tecnologies d'interfície diferents, d'escriptori, web i mòbil; i va madurar el seu disseny al Mòdul 8, amb estàndards de codificació, patrons de disseny, injecció de dependències, proves unitàries i refactorització disciplinada.

Aquest últim mòdul no ha fet més que reunir tot aquest camí en un únic sistema complet: uns requisits clars, una arquitectura triada amb criteri, una API que exposa el domini construït durant nou mòduls, una bateria de proves que dona suport a cada canvi, i un procés de desplegament reproduïble que porta aquest codi, de forma fiable, fins on algú de veritat el pugui fer servir.

De "Hola Món" a una aplicació desplegada en producció: aquest és el trajecte complet d'aquest curs, i és també, en essència, el trajecte de qualsevol projecte real de programari. El que sosté aquest trajecte no és haver memoritzat la sintaxi de C#, sinó haver practicat, una vegada i una altra sobre el mateix domini, a prendre decisions de disseny, a desacoblar responsabilitats, a confiar en les proves abans que en la sort, i a pensar en qui farà servir el codi tant com en qui l'escriu. Aquestes són les eines que continuaran sent útils molt després que qualsevol detall concret de sintaxi quedi obsolet. Enhorabona per haver arribat fins aquí: BiblioTech ha quedat acabada, però el que has après per construir-la no té per què aturar-se ara. El següent projecte en C# ja depèn només de tu.

Curs de Programació en C#

Mòdul 1: Introducció al C#

Mòdul 2: Estructures de Control

Mòdul 3: Programació Orientada a Objectes

Mòdul 4: Conceptes Avançats de C#

Mòdul 5: Treballant amb Dades

Mòdul 6: Temes Avançats

Mòdul 7: Construcció d'Aplicacions

Mòdul 8: Bones Pràctiques i Patrons de Disseny

Mòdul 9: Projecte Final

© Copyright 2026. Tots els drets reservats