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
dotnet publish: de codi font a artefacte desplegable- Contenidorització bàsica amb Docker
- Configuració per entorn:
appsettings.jsoni variables d'entorn - La cadena de connexió de producció
- Consideracions bàsiques de CI/CD
- BiblioTech, desplegada
dotnet publish: de codi font a artefacte desplegable
dotnet publish: de codi font a artefacte desplegableFins 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:
-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) deBiblioTech.Api,BiblioTech.DominiiBiblioTech.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à.
- 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"]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.
- Configuració per entorn:
appsettings.json i variables d'entorn
appsettings.json i variables d'entornASP.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.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).
- 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-apiEl 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.
- 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.
- 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
Debugen comptes deRelease: 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 publishgenera l'artefacte correcte, que la imatge Docker arrenca i respon en local ambdocker run, i que la configuració de producció no conté cap dada sensible escrita directament en un fitxer versionat.
Exercicis
-
Afegeix al
Dockerfilede l'apartat 2 una variable d'entornASPNETCORE_ENVIRONMENT=Productionamb 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 executardocker run. -
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#
- Introducció al C#
- Configuració de l'Entorn de Desenvolupament
- Programa Hola Món
- Sintaxi i Estructura Bàsica
- Variables i Tipus de Dades
- Arrays i Cadenes de Text
Mòdul 2: Estructures de Control
Mòdul 3: Programació Orientada a Objectes
- Classes i Objectes
- Mètodes
- Constructors i Destructors
- Herència
- Polimorfisme
- Encapsulació
- Abstracció
- Structs i Records: Tipus per Valor i per Referència
Mòdul 4: Conceptes Avançats de C#
- Interfícies
- Delegats i Esdeveniments
- Pattern Matching i Característiques Modernes de C#
- Genèrics
- Col·leccions
- LINQ (Consulta Integrada en el Llenguatge)
- Programació Asíncrona
Mòdul 5: Treballant amb Dades
- Entrada/Sortida de Fitxers
- Serialització
- Connectivitat amb Bases de Dades
- Entity Framework
- Treball amb JSON i Consum d'APIs REST
Mòdul 6: Temes Avançats
- Reflexió
- Atributs
- Programació Dinàmica
- Gestió de Memòria i Recollida d'Escombraries
- Multifil i Programació Paral·lela
Mòdul 7: Construcció d'Aplicacions
Mòdul 8: Bones Pràctiques i Patrons de Disseny
- Estàndards de Codificació i Bones Pràctiques
- Patrons de Disseny
- Injecció de Dependències i Inversió de Control
- Proves Unitàries
- Revisió i Refactorització de Codi
