Els cursos tècnics solen fallar pel mateix: cada lliçó ensenya una API aïllada amb un exemple de joguina, i en acabar saps escriure vint fragments que no has vist mai encaixar. Aquest curs funciona a l'inrevés. Tot el que aprenguis a partir d'aquí construirà una única aplicació real, mòdul a mòdul, fins a arribar a una API en producció.

Aquesta aplicació és Escena Viva. Ja n'has vist les dades a les lliçons anteriors; ara la formalitzarem: qui és, a qui serveix, quin és el seu model de domini, quines convencions adopta i —el més important— quina part d'ella construiràs a cadascun dels dotze mòduls. En acabar aquesta lliçó tindràs l'esquelet del projecte creat, el fitxer llavor dades/esdeveniments.json escrit i un mapa clar de la destinació.

Contingut

  1. El context de negoci
  2. Els actors i el que necessita cadascun
  3. El model de domini
  4. El fitxer llavor dades/esdeveniments.json
  5. L'estructura de carpetes del repositori
  6. Full de ruta: què construeix cada mòdul
  7. Convencions del projecte
  8. Nota sobre dades fictícies

  1. El context de negoci

Escena Viva és una petita empresa que gestiona la venda d'entrades de tres espais culturals d'una ciutat de mida mitjana:

Sala Aforament Perfil de programació
Teatro Almendra 420 localitats Concerts, teatre de sala, programació estable
Sala Bóveda 120 localitats Formats petits: monòlegs, cantautors, microteatre
Auditorio Ribera 900 localitats Grans esdeveniments i festivals

Fins ara, cada sala venia les entrades pel seu compte: una amb un full de càlcul, una altra per telèfon i la tercera amb un sistema heretat que ningú no sap mantenir. Escena Viva neix per unificar-ho tot en una sola plataforma.

Els tres esdeveniments amb què arrenquem —i que ja coneixes— són:

  • Concierto de Otoño (Teatro Almendra), 2 sessions a l'octubre del 2026.
  • Noche de Monólogos (Sala Bóveda), 3 sessions a l'octubre del 2026.
  • Festival de Jazz de Primavera (Auditorio Ribera), 2 sessions a l'abril del 2027.

Els requisits que marquen les decisions tècniques del projecte són aquests:

  1. Pics de concurrència brutals i molt curts. Quan s'obre la venda d'un festival, milers de persones hi entren alhora durant deu minuts. La resta del mes el trànsit és modest.
  2. L'aforament no es pot sobrevendre. És el requisit dur: dues persones no poden comprar la mateixa butaca.
  3. Els preus són diners de veritat. No admeten errors d'arrodoniment.
  4. Cal integrar-se amb tercers: passarel·la de pagament, enviament de correu, i en el futur un lector de codis a la porta.
  5. L'equip és petit. Dues persones desenvolupen i mantenen tot, així que la simplicitat i la reutilització de coneixement entre client i servidor tenen valor real.

Els punts 1, 4 i 5 són exactament les raons per les quals, a la primera lliçó, vam concloure que Node.js encaixa amb aquest negoci.

  1. Els actors i el que necessita cadascun

flowchart LR
    A["Assistent<br/>(compra entrades)"] --> P["Plataforma<br/>Escena Viva"]
    O["Organitzador<br/>(programa esdeveniments)"] --> P
    D["Administrador<br/>(opera el sistema)"] --> P
    P --> PG["Passarel·la de pagament"]
    P --> CO["Servei de correu"]
    P --> BD[("Base de dades<br/>escena_viva")]
Actor Qui és Què necessita fer
Assistent El públic que compra Consultar el catàleg, veure disponibilitat, comprar entrades, recuperar la seva comanda, descarregar les seves entrades
Organitzador La persona responsable d'una sala Crear i editar els seus esdeveniments i sessions, fixar aforament i preu, consultar vendes dels seus esdeveniments
Administrador L'equip d'Escena Viva Veure totes les vendes, generar informes, gestionar usuaris i organitzadors, anul·lar comandes

Aquesta separació no és decorativa: és la que justificarà el control d'accés basat en rols del Mòdul 8. Un organitzador del Teatro Almendra no ha de poder veure la recaptació de la Sala Bóveda.

  1. El model de domini

Aquestes són les entitats del sistema. Els noms de la columna «identificador» són els que farem servir literalment al codi, en català i sense accents ni ç.

Entitat Identificador Què representa Atributs principals
Esdeveniment esdeveniment Un espectacle programat id, titol, sala, organitzador, categoria, duracioMinuts, descripcio
Sessió sessio Un passi concret d'un esdeveniment en data i hora id, esdevenimentId, dataHora, aforament, venudes, preuCentims
Entrada entrada Un dret d'admissió individual codi, sessioId, comandaId, estat
Comanda comanda Una compra: diverses entrades d'una o més sessions id, usuariId, data, linies, totalCentims, estat
Usuari usuari Un compte de la plataforma id, email, nom, rol, hashContrasenya
Organitzador organitzador Qui programa esdeveniments en una sala id, nom, sales, contacte
Sala sala L'espai físic id, nom, aforamentMaxim, adreca
Aforament aforament Capacitat d'una sessió (no és taula pròpia) Enter dins de sessio
Preu preuCentims Import en cèntims (no és taula pròpia) Enter dins de sessio
Catàleg cataleg La col·lecció d'esdeveniments publicats Array d'esdeveniment

I així es relacionen:

erDiagram
    SALA ||--o{ ESDEVENIMENT : "acull"
    ORGANITZADOR ||--o{ ESDEVENIMENT : "programa"
    ESDEVENIMENT ||--|{ SESSIO : "te"
    SESSIO ||--o{ ENTRADA : "genera"
    COMANDA ||--|{ ENTRADA : "agrupa"
    USUARI ||--o{ COMANDA : "fa"
    USUARI }o--|| ORGANITZADOR : "pot ser"

    SALA {
        string id
        string nom
        int aforamentMaxim
    }
    ESDEVENIMENT {
        string id
        string titol
        string sala
        string categoria
        int duracioMinuts
    }
    SESSIO {
        string id
        string dataHora
        int aforament
        int venudes
        int preuCentims
    }
    ENTRADA {
        string codi
        string sessioId
        string estat
    }
    COMANDA {
        string id
        string data
        int totalCentims
        string estat
    }
    USUARI {
        string id
        string email
        string rol
    }

Tres decisions de modelatge que convé entendre des d'ara:

  • L'aforament viu a la sessió, no a l'esdeveniment. El Teatro Almendra té 420 localitats, però una sessió concreta en pot treure a la venda només 380 si hi ha una zona reservada per a premsa. La sala aporta el màxim; la sessió, la realitat.
  • L'entrada és individual i té codi propi. Una comanda de 4 entrades genera 4 registres entrada, cadascun amb el seu codi únic, perquè cadascun es valida per separat a la porta.
  • venudes és un comptador desnormalitzat a la sessió. En rigor es podria calcular comptant entrades, però mostrar la disponibilitat al catàleg ha de ser instantani. Al Mòdul 7 veurem com mantenir aquest comptador coherent amb transaccions.

Estats

Entitat Estats possibles Notes
comanda pendent → pagat → emes, o anullat pendent reserva l'aforament durant 10 minuts
entrada valida → usada, o anullada usada la marca el lector de la porta
esdeveniment esborrany → publicat → finalitzat Només els publicat apareixen al catàleg públic

  1. El fitxer llavor dades/esdeveniments.json

Fins al Mòdul 7, quan aparegui la base de dades escena_viva, aquest fitxer és la font de dades de tota l'aplicació. Crea dades/esdeveniments.json amb exactament aquest contingut:

[
  {
    "id": "evt-001",
    "titol": "Concierto de Otono",
    "sala": "Teatro Almendra",
    "organitzador": "org-almendra",
    "categoria": "concert",
    "duracioMinuts": 95,
    "estat": "publicat",
    "descripcio": "Repertori simfonic de cambra per obrir la temporada.",
    "sessions": [
      {
        "id": "ses-001-1",
        "dataHora": "2026-10-03T20:00:00",
        "aforament": 420,
        "venudes": 180,
        "preuCentims": 2500
      },
      {
        "id": "ses-001-2",
        "dataHora": "2026-10-04T19:00:00",
        "aforament": 420,
        "venudes": 96,
        "preuCentims": 2200
      }
    ]
  },
  {
    "id": "evt-002",
    "titol": "Noche de Monologos",
    "sala": "Sala Boveda",
    "organitzador": "org-boveda",
    "categoria": "humor",
    "duracioMinuts": 80,
    "estat": "publicat",
    "descripcio": "Quatre comics, format curt i public molt a prop.",
    "sessions": [
      {
        "id": "ses-002-1",
        "dataHora": "2026-10-10T21:30:00",
        "aforament": 120,
        "venudes": 118,
        "preuCentims": 1800
      },
      {
        "id": "ses-002-2",
        "dataHora": "2026-10-11T21:30:00",
        "aforament": 120,
        "venudes": 45,
        "preuCentims": 1800
      },
      {
        "id": "ses-002-3",
        "dataHora": "2026-10-17T21:30:00",
        "aforament": 120,
        "venudes": 12,
        "preuCentims": 1500
      }
    ]
  },
  {
    "id": "evt-003",
    "titol": "Festival de Jazz de Primavera",
    "sala": "Auditorio Ribera",
    "organitzador": "org-ribera",
    "categoria": "festival",
    "duracioMinuts": 240,
    "estat": "publicat",
    "descripcio": "Tres escenaris i dotze formacions al llarg de dues jornades.",
    "sessions": [
      {
        "id": "ses-003-1",
        "dataHora": "2027-04-17T19:00:00",
        "aforament": 900,
        "venudes": 640,
        "preuCentims": 3800
      },
      {
        "id": "ses-003-2",
        "dataHora": "2027-04-18T19:00:00",
        "aforament": 900,
        "venudes": 720,
        "preuCentims": 4200
      }
    ]
  }
]

Comentari camp a camp

JSON no admet comentaris, així que els posem aquí:

Camp Tipus Per què és així
id (esdeveniment) "evt-NNN" Prefix llegible: en llegir un registre saps de què és sense mirar el context
titol text El que veu el públic
sala text De moment el nom; al Mòdul 7 passarà a ser una referència a la taula sala
organitzador "org-xxx" Referència a l'organitzador; sosté el control per rols del Mòdul 8
categoria text Serveix per filtrar i agrupar el catàleg
duracioMinuts enter En minuts, mai en text tipus "1h 35min": les dades es calculen, no es llegeixen
estat text esborrany, publicat o finalitzat
descripcio text Text comercial breu
sessions array Imbricades dins de l'esdeveniment: és la forma natural del document JSON i la que farem servir amb MongoDB
sessions[].id "ses-NNN-M" Inclou el número de l'esdeveniment: llegible i traçable d'un cop d'ull
dataHora ISO 8601 Cadena AAAA-MM-DDTHH:mm:ss: ordenable com a text i estable en passar per JSON
aforament enter Localitats a la venda en aquesta sessió
venudes enter Comptador d'entrades ja venudes
preuCentims enter 2500 = 25,00 EUR. Mai decimals

Comprova que el fitxer és JSON vàlid abans de continuar:

node -p "require('./dades/esdeveniments.json').length"
# 3

node -e "require('./dades/esdeveniments.json').forEach(e => console.log(e.id, '-', e.titol))"
# evt-001 - Concierto de Otono
# evt-002 - Noche de Monologos
# evt-003 - Festival de Jazz de Primavera

Si l'ordre falla amb SyntaxError, hi ha una coma de més, una cometa sense tancar o has fet servir cometes simples. En JSON totes les claus i totes les cadenes van entre cometes dobles, i no s'admet coma després de l'últim element.

Encara no sabem llegir aquest fitxer des del codi amb fs —això és el Mòdul 3—, però ja és al seu lloc i ja és la referència. A partir d'aquí, src/cataleg-dades.js deixarà de tenir les dades incrustades.

  1. L'estructura de carpetes del repositori

Aquesta és l'estructura objectiu, la que tindrà el projecte al final del curs. No la creïs sencera ara: creixerà amb cada mòdul.

escena-viva/
├── .env                     # Secrets locals (MAI al repositori) - Mòdul 11
├── .gitignore
├── .nvmrc                   # Versió de Node del projecte
├── package.json             # Metadades i dependències - Mòdul 5
├── dades/
│   ├── esdeveniments.json   # Llavor del catàleg
│   └── vendes.csv           # Històric de vendes per a informes - Mòdul 3
├── informes/                # Sortides generades (ignorades per git)
├── src/
│   ├── cataleg.js           # Punt d'entrada del catàleg per consola
│   ├── cataleg-dades.js     # Accés a les dades del catàleg
│   ├── domini/              # Classes Esdeveniment, Sessio, Comanda - Mòdul 2
│   ├── servidor/            # Servidor HTTP i rutes - Mòduls 4 i 6
│   ├── middleware/          # Middleware d'Express - Mòdul 6
│   ├── models/              # Models de base de dades - Mòdul 7
│   ├── auth/                # Autenticació i rols - Mòdul 8
│   └── utils/               # Formatatge, validacions, ajudants
└── proves/                  # Proves automatitzades - Mòdul 9

Principis d'organització que seguirem:

  • Una responsabilitat per carpeta. Si no saps on posar un fitxer, probablement fa dues coses.
  • src/ conté només codi; dades/ només dades; informes/ només resultats generats. Els resultats generats no es versionen mai.
  • Noms de fitxer en minúscules i amb guions (cataleg-dades.js), mai amb espais ni majúscules. Linux distingeix majúscules i minúscules encara que el teu portàtil no ho faci, i això ha trencat més desplegaments dels que es poden comptar.

  1. Full de ruta: què construeix cada mòdul

Aquesta taula és el mapa del curs. Guarda-la: cada vegada que comencis un mòdul sabràs quina peça d'Escena Viva estàs afegint.

Mòdul Tema Lliurament a Escena Viva
1 Introducció Entorn instal·lat, esquelet del projecte, catàleg per consola, llavor dades/esdeveniments.json
2 Conceptes bàsics Classes de domini (Esdeveniment, Sessio) separades en mòduls; càrrega asíncrona del catàleg; un EventEmitter que avisa quan una sessió s'exhaureix
3 Sistema de fitxers i E/S Lectura real de dades/esdeveniments.json amb fs; generació d'informes a informes/; processament de dades/vendes.csv amb streams sense carregar-lo en memòria
4 HTTP i servidors web Primer servidor propi: GET /esdeveniments, GET /esdeveniments/:id, alta de comandes per POST amb JSON, i consum d'una API externa de tipus de canvi
5 NPM package.json del projecte, dependències, scripts npm start i npm run informe, i publicació d'un paquet propi d'utilitats de format
6 Express Migració del servidor a Express: rutes del catàleg i de comandes, middleware de registre, validació de la compra i gestió centralitzada d'errors
7 Bases de dades Substitució del JSON per la base de dades escena_viva: models, CRUD d'esdeveniments i sessions, consultes de disponibilitat, i transaccions perquè l'aforament no es sobrevengui mai
8 Autenticació Registre i accés d'assistents, hash de contrasenyes, sessió amb JWT, i rols: assistent, organitzador i administrador
9 Proves i depuració Proves unitàries del càlcul d'aforament i preus, proves d'integració de l'API de compra, cobertura i depuració d'una errada real
10 Temes avançats Aguantar el pic d'obertura de venda: cluster, generació de PDF en worker threads, memòria cau de catàleg i cua de correus amb Redis, i l'API REST ben dissenyada
11 Desplegament i DevOps Configuració per entorn, registre estructurat, PM2, imatge Docker de la plataforma, desplegament i canonada d'integració contínua
12 Projectes del món real Extensions sobre allò après i tancament: de projecte a producció
flowchart LR
    M1["M1-2<br/>Dades en memòria<br/>i consola"] --> M3["M3<br/>Fitxers<br/>JSON i CSV"]
    M3 --> M4["M4-6<br/>API HTTP<br/>i Express"]
    M4 --> M7["M7-8<br/>Base de dades<br/>i usuaris"]
    M7 --> M9["M9-10<br/>Proves i<br/>rendiment"]
    M9 --> M11["M11-12<br/>Desplegament<br/>i producció"]

  1. Convencions del projecte

Aquestes regles s'apliquen a tot el codi del curs. Escrites una vegada aquí, no les tornarem a discutir.

7.1 Identificadors en català

Variables, funcions, classes, propietats i noms de fitxer van en català, sense accents ni ç: preuCentims, entradesLliures, calcularOcupacio, cataleg-dades.js.

Les excepcions són les que imposa l'entorn: paraules clau del llenguatge (const, class, return), APIs de Node i del navegador (readFile, createServer, map), i noms de paquets (express, mongoose).

Per què sense accents? Perquè un identificador sessió és vàlid en JavaScript però es converteix en una font d'errors tan bon punt viatja per una URL, un nom de fitxer o una capçalera HTTP. Val més evitar-ho d'arrel.

Estil: camelCase per a variables i funcions, PascalCase per a classes, MAJUSCULES_AMB_GUIO_BAIX per a constants globals.

7.2 Dates en format ISO 8601

Tota data es desa com a cadena AAAA-MM-DDTHH:mm:ss.

const dataHora = '2026-10-03T20:00:00';   // Be
const data = '03/10/2026 20:00';          // Malament: ambigu i no ordenable

Tres raons:

  1. S'ordena correctament com a text, sense convertir a Date.
  2. No és ambigu: 03/10/2026 és 3 d'octubre a Catalunya i 10 de març als Estats Units.
  3. Sobreviu al viatge per JSON. Un objecte Date es converteix en text en serialitzar i no torna a ser Date en deserialitzar; si ja deses text, no hi ha sorpreses.

El formatatge a 03/10/2026 20:00 és responsabilitat de la capa de presentació, mai de l'emmagatzematge.

7.3 Preus en cèntims com a enter

Tot import monetari es desa com a nombre enter de cèntims. 2500 significa 25,00 EUR.

console.log(0.1 + 0.2);              // 0.30000000000000004
console.log(19.99 * 3);              // 59.97000000000001

console.log(1999 * 3);               // 5997  centims, exacte
console.log((5997 / 100).toFixed(2)); // '59.97'

El tipus number de JavaScript és un flotant de doble precisió (IEEE 754) i no pot representar exactament 0,1. Amb diners, aquest error s'acumula i acaba en un desquadrament comptable. Treballant amb enters el problema desapareix: només es divideix entre 100 en l'últim moment, per mostrar.

Convenció de noms: qualsevol camp monetari porta el sufix Centims (preuCentims, totalCentims, descompteCentims). Així és impossible confondre's.

7.4 Codis d'entrada

Cada entrada té un codi amb el format:

EV-2026-000123
│  │    └── Sequencia de 6 digits amb zeros a l'esquerra
│  └─────── Any d'emissio
└────────── Prefix d'Escena Viva
function generarCodiEntrada(any, sequencia) {
  // padStart omple amb zeros fins a 6 caracters: 123 -> '000123'
  return `EV-${any}-${String(sequencia).padStart(6, '0')}`;
}

console.log(generarCodiEntrada(2026, 123));   // EV-2026-000123

Per què aquest format i no un identificador aleatori llarg?

  • És llegible per telèfon. Algú el pot dictar a atenció al client sense errors.
  • És ordenable i el seu any és visible a simple vista.
  • És curt per imprimir-lo sota un codi de barres.

La contrapartida és que és predictible: si coneixes un codi pots endevinar el següent. Per això el codi no és mai la prova de propietat; la validació a la porta comprova a més l'estat de l'entrada a la base de dades. És una decisió conscient, del tipus que es pren constantment en un projecte real.

7.5 Altres convencions

Convenció Regla
Codificació de fitxers UTF-8 sense BOM
Finals de línia \n (LF), també a Windows
Indentació 2 espais
Cometes Simples en JavaScript, dobles obligatòries en JSON
Punt i coma Sí, sempre
Idioma dels comentaris Català
Format dels fitxers JSON JSON.stringify(dades, null, 2)
Dades per stdout, diagnòstics per stderr Sempre

  1. Nota sobre dades fictícies

Totes les dades d'aquest curs són inventades. Les sales, els esdeveniments, els organitzadors, els correus i les comandes que apareixeran a partir del Mòdul 7 no corresponen a persones ni empreses reals.

Això no és un formalisme: és una pràctica professional que has de traslladar a la teva feina.

  • No facis servir mai dades reals de clients en desenvolupament, en proves ni en demostracions. Ni tan sols «només aquesta vegada», ni tan sols una còpia de la base de dades de producció al teu portàtil.
  • Les bases de dades de desenvolupament es poblen amb dades sintètiques, generades o anonimitzades. Al Mòdul 7 veurem com escriure llavors per a això.
  • Les dades personals estan regulades. Noms, correus, telèfons i dades de pagament tenen obligacions legals de tractament i minimització.
  • No pugis mai secrets al repositori: claus de la passarel·la de pagament, contrasenyes de base de dades o tokens. Van en variables d'entorn (Mòdul 11) i per això el .gitignore inclou .env des de la primera lliçó.

Quan al Mòdul 8 registrem l'usuari [email protected], fixa't en el domini .test: està reservat per norma per a proves i no correspondrà mai a una bústia real.

Errors Comuns i Consells

Error 1: desar els preus com a decimals «perquè és més còmode». Ho és durant dues setmanes. Després arriba el primer desquadrament d'un cèntim en un informe de 3.000 comandes i no hi ha manera de saber d'on surt.

Error 2: fer servir formats de data locals a les dades. 03/10/2026 obliga a saber qui ho va escriure per interpretar-ho. ISO 8601 a l'emmagatzematge, format local només en mostrar.

Error 3: posar l'aforament a l'esdeveniment en comptes de a la sessió. Funciona fins a la primera sessió amb aforament reduït, i llavors cal migrar dades.

Error 4: deixar el fitxer JSON amb una única línia gegant. Cada canvi apareix al control de versions com «tota la línia modificada» i les revisions es tornen impossibles. null, 2 sempre.

Error 5: barrejar idiomes als identificadors. getEsdevenimentById, llistarEvents, preuPrice. Tria'n un —en aquest curs, el català— i sigues sistemàtic.

Consell 1: torna al full de ruta en començar cada mòdul. Saber quina peça estàs construint canvia completament la manera d'estudiar una API.

Consell 2: fes servir git des del primer dia. Un git commit en acabar cada lliçó et dona un punt de retorn i un històric del teu propi aprenentatge.

Consell 3: no avancis feina. És temptador muntar ja el servidor Express. Cada mòdul introdueix els conceptes en un ordre pensat; saltar-se'l acaba sovint en codi que funciona sense entendre per què.

Consell 4: quan dubtis, mira el model de domini. Bona part de les decisions de codi es responen soles mirant la taula d'entitats.

Exercicis

Exercici 1: preparar l'esquelet del projecte

Deixa el projecte llest per al Mòdul 2. Ha de complir:

  1. La carpeta escena-viva/ conté src/, dades/ i informes/.
  2. dades/esdeveniments.json existeix amb els tres esdeveniments i és JSON vàlid.
  3. informes/ està sota control de versions encara que sigui buida (pista: .gitkeep).
  4. .gitignore exclou node_modules/, .env, *.log i el contingut generat d'informes/ sense excloure la carpeta.
  5. .nvmrc conté la versió LTS instal·lada.
  6. Existeix un README.md amb el nom del projecte, els tres esdeveniments i les ordres per arrencar.
  7. Un primer commit de git amb tot l'anterior.

Escriu la seqüència completa d'ordres i el contingut de cada fitxer.

Exercici 2: ampliar la llavor amb un quart esdeveniment

Afegeix a dades/esdeveniments.json un quart esdeveniment amb aquestes característiques:

  • Títol: Cuentos al Anochecer
  • Sala: Teatro Almendra (organitzador org-almendra)
  • Categoria: familiar, durada 55 minuts, estat publicat
  • Tres sessions els dissabtes 7, 14 i 21 de novembre del 2026 a les 18:00
  • Aforament 200 a cada sessió (el Teatro Almendra reserva part de la platea per a aquest format)
  • Entrades venudes: 200, 145 i 30 respectivament
  • Preu: 1200 cèntims les dues primeres i 900 la tercera

Respecta els formats d'id, de data i de preu del projecte. Després, verifica amb ordres node -p que:

  1. El fitxer continua sent JSON vàlid i té 4 esdeveniments.
  2. El catàleg té ara 10 sessions.
  3. L'aforament total del Teatro Almendra (sumant tots els seus esdeveniments) és correcte.
  4. Hi ha exactament una sessió exhaurida.

Exercici 3: aplicar les convencions

Un company ha escrit aquest fragment. Reescriu-lo respectant totes les convencions del projecte i explica cada canvi.

var Sessions = [
  {ID: "S1", date: "10/10/2026 21:30", capacity: 120, sold: 118, price: 18.00},
  {ID: "S2", date: "11/10/2026 21:30", capacity: 120, sold: 45, price: 18.00}
];

function getTotalRevenue(sessions){
  var total = 0
  for(var i=0;i<sessions.length;i++){
    total = total + (sessions[i].sold * sessions[i].price)
  }
  return total
}

console.log("Revenue: " + getTotalRevenue(Sessions))

Solucions

Solució 1

# 1. Estructura de carpetes
mkdir -p escena-viva/src escena-viva/dades escena-viva/informes
cd escena-viva

# 3. Mantenir "informes" al repositori encara que estigui buida
touch informes/.gitkeep

# 5. Versio de Node del projecte
node -v > .nvmrc

.gitignore (punt 4). La clau és l'excepció amb !, per ignorar el contingut però conservar la carpeta:

node_modules/
.env
*.log

# Ignorem el que es genera a informes i mantenim la carpeta
informes/*
!informes/.gitkeep

README.md (punt 6):

# Escena Viva

Plataforma de venda d'entrades per a esdeveniments culturals del Teatro Almendra,
la Sala Boveda i l'Auditorio Ribera.

## Esdeveniments en cartell

- Concierto de Otono (Teatro Almendra)
- Noche de Monologos (Sala Boveda)
- Festival de Jazz de Primavera (Auditorio Ribera)

## Requisits

- Node.js: la versio indicada a `.nvmrc`

## Posada en marxa

    nvm use
    node src/cataleg.js

## Estructura

- `src/`      codi font
- `dades/`    dades de partida (esdeveniments.json)
- `informes/` sortides generades (no es versionen)

Verificació i primer commit (punts 2 i 7):

node -p "require('./dades/esdeveniments.json').length"   # 3

git init
git add .
git commit -m "Esquelet del projecte Escena Viva amb cataleg llavor"
git log --oneline

Solució 2

Bloc que cal inserir al final de l'array de dades/esdeveniments.json (recorda la coma després de la clau de tancament de l'esdeveniment anterior):

  {
    "id": "evt-004",
    "titol": "Cuentos al Anochecer",
    "sala": "Teatro Almendra",
    "organitzador": "org-almendra",
    "categoria": "familiar",
    "duracioMinuts": 55,
    "estat": "publicat",
    "descripcio": "Narracio oral per a public familiar a la caiguda de la tarda.",
    "sessions": [
      {
        "id": "ses-004-1",
        "dataHora": "2026-11-07T18:00:00",
        "aforament": 200,
        "venudes": 200,
        "preuCentims": 1200
      },
      {
        "id": "ses-004-2",
        "dataHora": "2026-11-14T18:00:00",
        "aforament": 200,
        "venudes": 145,
        "preuCentims": 1200
      },
      {
        "id": "ses-004-3",
        "dataHora": "2026-11-21T18:00:00",
        "aforament": 200,
        "venudes": 30,
        "preuCentims": 900
      }
    ]
  }

Verificacions:

# 1. JSON valid i nombre d'esdeveniments
node -p "require('./dades/esdeveniments.json').length"
# 4

# 2. Total de sessions
node -p "require('./dades/esdeveniments.json').reduce((t, e) => t + e.sessions.length, 0)"
# 10

# 3. Aforament total del Teatro Almendra: 840 (evt-001) + 600 (evt-004) = 1440
node -p "require('./dades/esdeveniments.json').filter(e => e.sala === 'Teatro Almendra').flatMap(e => e.sessions).reduce((t, s) => t + s.aforament, 0)"
# 1440

# 4. Sessions exhaurides
node -p "require('./dades/esdeveniments.json').flatMap(e => e.sessions).filter(s => s.venudes >= s.aforament).map(s => s.id)"
# [ 'ses-004-1' ]

Nota sobre el punt 4: ses-002-1 té 118 de 120 venudes, així que no està exhaurida. L'única exhaurida és la primera sessió de l'esdeveniment nou.

Solució 3

// src/utils/recaptacio.js
// Calcul de la recaptacio d'un conjunt de sessions.

const sessions = [
  { id: 'ses-002-1', dataHora: '2026-10-10T21:30:00', aforament: 120, venudes: 118, preuCentims: 1800 },
  { id: 'ses-002-2', dataHora: '2026-10-11T21:30:00', aforament: 120, venudes: 45,  preuCentims: 1800 }
];

// Retorna la recaptacio total en centims (enter).
function calcularRecaptacioCentims(sessions) {
  return sessions.reduce(
    (total, sessio) => total + sessio.venudes * sessio.preuCentims,
    0
  );
}

const totalCentims = calcularRecaptacioCentims(sessions);

console.log(`Recaptacio: ${(totalCentims / 100).toFixed(2)} EUR`);
// Recaptacio: 2934.00 EUR

Canvis aplicats i el seu motiu:

Canvi Motiu
var → const Àmbit de bloc i sense reassignacions accidentals
Sessions → sessions Identificadors en català i camelCase (PascalCase es reserva per a classes)
ID, date, capacity, sold, price → id, dataHora, aforament, venudes, preuCentims Noms del model de domini, en català
"S1" → 'ses-002-1' Format d'identificador del projecte, amb prefix i traçabilitat
"10/10/2026 21:30" → '2026-10-10T21:30:00' ISO 8601: sense ambigüitat i ordenable com a text
price: 18.00 → preuCentims: 1800 Diners com a enter de cèntims; el sufix ho fa explícit
getTotalRevenue → calcularRecaptacioCentims Català i amb el sufix que indica la unitat retornada
Bucle for amb índex → reduce Més declaratiu i sense variable mutable
Concatenació amb + → plantilla de cadena Llegibilitat
Cometes dobles → simples Convenció de JavaScript del projecte (les dobles queden per a JSON)
Punt i coma afegits Convenció del projecte
Comentaris en català afegits Convenció del projecte

Comprovació del càlcul: 118 × 1800 + 45 × 1800 = 212.400 + 81.000 = 293.400 cèntims = 2.934,00 EUR. Amb preus decimals el resultat hauria estat el mateix en aquest cas concret, però n'hi ha prou amb un preu de 18,99 i uns centenars d'operacions perquè comencin a aparèixer cèntims fantasma.

Conclusió

Escena Viva ja no és un decorat: és un projecte amb context, actors, model de domini, dades i regles pròpies. Has vist qui fa servir la plataforma —assistent, organitzador i administrador— i com aquesta separació anticipa el control per rols; has formalitzat les entitats esdeveniment, sessio, entrada, comanda, usuari, organitzador i sala, amb les seves relacions i els seus estats; has escrit el fitxer llavor dades/esdeveniments.json que serà la font de dades fins que aparegui la base de dades escena_viva; i has fixat les convencions que no es tornaran a discutir: identificadors en català sense accents, dates ISO 8601, preus en cèntims com a enter, codis d'entrada EV-2026-000123, JSON amb null, 2 i dades sempre fictícies.

Sobretot, tens el full de ruta. Saps que al Mòdul 3 el catàleg deixarà d'estar incrustat al codi per llegir-se de disc, que al Mòdul 4 es traurà el nas per HTTP, que al Mòdul 7 es mudarà a una base de dades amb transaccions que impedeixin sobrevendre l'aforament, i que al Mòdul 10 aprendràs a sostenir el pic de trànsit de l'obertura de venda d'un festival.

Amb això tanquem el Mòdul 1. Tens l'entorn instal·lat amb una versió LTS gestionada per .nvmrc, saps executar i depurar un script, manejes el REPL com a laboratori, domines el JavaScript modern que farem servir i tens el projecte dret amb la seva llavor de dades.

El que encara no saps és com funciona Node.js per dins, i és la peça que falta per escriure codi de servidor de debò. Al Mòdul 2: Conceptes Bàsics obrirem la caixa: l'arquitectura de Node, el bucle d'esdeveniments i les seves fases, els callbacks, les promeses i async/await, els esdeveniments amb EventEmitter i el sistema de mòduls amb require i module.exports. Allà, per fi, l'array d'esdeveniments incrustat a src/cataleg-dades.js es convertirà en un mòdul reutilitzable, naixeran les classes Esdeveniment i Sessio a src/domini/, i el cambrer que no espera de la primera lliçó deixarà de ser una analogia per convertir-se en el codi que escrius.

Curs de Node.js: De Principiant a Avançat

Mòdul 1: Introducció a Node.js

Mòdul 2: Conceptes Bàsics

Mòdul 3: Sistema de Fitxers i E/S

Mòdul 4: HTTP i Servidors Web

Mòdul 5: NPM i Gestió de Paquets

Mòdul 6: Framework Express.js

Mòdul 7: Bases de Dades i ORMs

Mòdul 8: Autenticació i Autorització

Mòdul 9: Proves i Depuració

Mòdul 10: Temes Avançats

Mòdul 11: Desplegament i DevOps

Mòdul 12: Projectes del Món Real

© Copyright 2026. Tots els drets reservats