Technical courses usually fail for the same reason: each lesson teaches an isolated API with a toy example, and by the end you know how to write twenty fragments you have never seen fit together. This course works the other way round. Everything you learn from here on will build a single real application, module by module, until it reaches production as an API.
That application is Escena Viva. You have already seen its data in the previous lessons; now we are going to formalize it: who it is, who it serves, what its domain model is, what conventions it adopts and — most importantly — which part of it you will build in each of the twelve modules. By the end of this lesson you will have the project skeleton created, the seed file data/events.json written, and a clear map of the destination.
Contents
- The business context
- The actors and what each of them needs
- The domain model
- The seed file
data/events.json - The repository folder structure
- Road map: what each module builds
- Project conventions
- A note on fictional data
- The Business Context
Escena Viva is a small company that handles ticket sales for three cultural spaces in a medium-sized city:
| Venue | Capacity | Programming profile |
|---|---|---|
| Teatro Almendra | 420 seats | Concerts, studio theater, a stable program |
| Sala Bóveda | 120 seats | Small formats: stand-up, singer-songwriters, micro-theater |
| Auditorio Ribera | 900 seats | Large events and festivals |
Until now, each venue sold its own tickets: one with a spreadsheet, another over the phone, and the third with a legacy system nobody knows how to maintain. Escena Viva exists to unify all of it in a single platform.
The three events we start with — the ones you already know — are:
- Concierto de Otoño (Teatro Almendra), 2 sessions in October 2026.
- Noche de Monólogos (Sala Bóveda), 3 sessions in October 2026.
- Festival de Jazz de Primavera (Auditorio Ribera), 2 sessions in April 2027.
The requirements that drive the project's technical decisions are these:
- Brutal, very short concurrency spikes. When a festival goes on sale, thousands of people arrive at once for ten minutes. For the rest of the month the traffic is modest.
- Capacity cannot be oversold. That is the hard requirement: two people cannot buy the same seat.
- Prices are real money. They tolerate no rounding errors.
- We have to integrate with third parties: a payment gateway, an email service, and in the future a code scanner at the door.
- The team is small. Two people develop and maintain everything, so simplicity and reusing knowledge between client and server have real value.
Points 1, 4 and 5 are exactly why, in the first lesson, we concluded that Node.js is a good fit for this business.
- The Actors and What Each of Them Needs
flowchart LR
A["Attendee<br/>(buys tickets)"] --> P["Escena Viva<br/>platform"]
O["Organizer<br/>(programs events)"] --> P
D["Administrator<br/>(operates the system)"] --> P
P --> PG["Payment gateway"]
P --> CO["Email service"]
P --> BD[("Database<br/>escena_viva")]
| Actor | Who they are | What they need to do |
|---|---|---|
| Attendee | The public who buy | Browse the catalog, see availability, buy tickets, retrieve their order, download their tickets |
| Organizer | The person responsible for a venue | Create and edit their events and sessions, set capacity and price, review sales for their events |
| Administrator | The Escena Viva team | See every sale, generate reports, manage users and organizers, cancel orders |
This separation is not decorative: it is what will justify the role-based access control in Module 8. An organizer at the Teatro Almendra must not be able to see the Sala Bóveda's revenue.
- The Domain Model
These are the system's entities. The names in the "identifier" column are the ones we will use literally in the code, in English and in plain ASCII.
| Entity | Identifier | What it represents | Main attributes |
|---|---|---|---|
| Event | event |
A scheduled show | id, title, venue, organizer, category, durationMinutes, description |
| Session | session |
One specific showing of an event at a date and time | id, eventId, dateTime, capacity, sold, priceCents |
| Ticket | ticket |
An individual right of admission | code, sessionId, orderId, status |
| Order | order |
One purchase: several tickets from one or more sessions | id, userId, date, lines, totalCents, status |
| User | user |
An account on the platform | id, email, name, role, passwordHash |
| Organizer | organizer |
Whoever programs events at a venue | id, name, venues, contact |
| Venue | venue |
The physical space | id, name, maxCapacity, address |
| Capacity | capacity |
A session's capacity (not a table of its own) | An integer inside session |
| Price | priceCents |
An amount in cents (not a table of its own) | An integer inside session |
| Catalog | catalog |
The collection of published events | An array of event |
And this is how they relate:
erDiagram
VENUE ||--o{ EVENT : "hosts"
ORGANIZER ||--o{ EVENT : "programs"
EVENT ||--|{ SESSION : "has"
SESSION ||--o{ TICKET : "generates"
ORDER ||--|{ TICKET : "groups"
USER ||--o{ ORDER : "places"
USER }o--|| ORGANIZER : "may be"
VENUE {
string id
string name
int maxCapacity
}
EVENT {
string id
string title
string venue
string category
int durationMinutes
}
SESSION {
string id
string dateTime
int capacity
int sold
int priceCents
}
TICKET {
string code
string sessionId
string status
}
ORDER {
string id
string date
int totalCents
string status
}
USER {
string id
string email
string role
}
Three modeling decisions worth understanding right away:
- Capacity lives on the session, not on the event. The Teatro Almendra has 420 seats, but a particular session may only put 380 on sale if there is a press area held back. The venue supplies the maximum; the session, the reality.
- A ticket is individual and has its own code. An order of 4 tickets generates 4
ticketrecords, each with its own unique code, because each one is validated separately at the door. soldis a denormalized counter on the session. Strictly it could be worked out by counting tickets, but showing availability in the catalog has to be instantaneous. In Module 7 we will see how to keep that counter consistent using transactions.
Statuses
| Entity | Possible statuses | Notes |
|---|---|---|
order |
pending → paid → issued, or cancelled |
pending holds the capacity for 10 minutes |
ticket |
valid → used, or cancelled |
used is set by the scanner at the door |
event |
draft → published → finished |
Only published ones appear in the public catalog |
- The Seed File
data/events.json
data/events.jsonUntil Module 7, when the escena_viva database arrives, this file is the data source for the whole application. Create data/events.json with exactly this content:
[
{
"id": "evt-001",
"title": "Concierto de Otono",
"venue": "Teatro Almendra",
"organizer": "org-almendra",
"category": "concert",
"durationMinutes": 95,
"status": "published",
"description": "Chamber symphonic repertoire to open the season.",
"sessions": [
{
"id": "ses-001-1",
"dateTime": "2026-10-03T20:00:00",
"capacity": 420,
"sold": 180,
"priceCents": 2500
},
{
"id": "ses-001-2",
"dateTime": "2026-10-04T19:00:00",
"capacity": 420,
"sold": 96,
"priceCents": 2200
}
]
},
{
"id": "evt-002",
"title": "Noche de Monologos",
"venue": "Sala Boveda",
"organizer": "org-boveda",
"category": "comedy",
"durationMinutes": 80,
"status": "published",
"description": "Four comedians, a short format and the audience right up close.",
"sessions": [
{
"id": "ses-002-1",
"dateTime": "2026-10-10T21:30:00",
"capacity": 120,
"sold": 118,
"priceCents": 1800
},
{
"id": "ses-002-2",
"dateTime": "2026-10-11T21:30:00",
"capacity": 120,
"sold": 45,
"priceCents": 1800
},
{
"id": "ses-002-3",
"dateTime": "2026-10-17T21:30:00",
"capacity": 120,
"sold": 12,
"priceCents": 1500
}
]
},
{
"id": "evt-003",
"title": "Festival de Jazz de Primavera",
"venue": "Auditorio Ribera",
"organizer": "org-ribera",
"category": "festival",
"durationMinutes": 240,
"status": "published",
"description": "Three stages and twelve line-ups across two days.",
"sessions": [
{
"id": "ses-003-1",
"dateTime": "2027-04-17T19:00:00",
"capacity": 900,
"sold": 640,
"priceCents": 3800
},
{
"id": "ses-003-2",
"dateTime": "2027-04-18T19:00:00",
"capacity": 900,
"sold": 720,
"priceCents": 4200
}
]
}
]A field-by-field commentary
JSON does not allow comments, so here they are instead:
| Field | Type | Why it is like this |
|---|---|---|
id (event) |
"evt-NNN" |
A readable prefix: reading a record tells you what it is without checking the context |
title |
text | What the public sees |
venue |
text | For now the name; in Module 7 it becomes a reference to the venue table |
organizer |
"org-xxx" |
A reference to the organizer; it underpins the role control of Module 8 |
category |
text | Used to filter and group the catalog |
durationMinutes |
integer | In minutes, never as text like "1h 35min": data gets calculated, not read |
status |
text | draft, published or finished |
description |
text | Short marketing copy |
sessions |
array | Nested inside the event: it is the natural shape of a JSON document and the one we will use with MongoDB |
sessions[].id |
"ses-NNN-M" |
It includes the event number: readable and traceable at a glance |
dateTime |
ISO 8601 | A YYYY-MM-DDTHH:mm:ss string: sortable as text and stable through JSON |
capacity |
integer | Seats on sale for this session |
sold |
integer | A counter of tickets already sold |
priceCents |
integer | 2500 = 25.00 EUR. Never decimals |
Check that the file is valid JSON before moving on:
node -p "require('./data/events.json').length"
# 3
node -e "require('./data/events.json').forEach(e => console.log(e.id, '-', e.title))"
# evt-001 - Concierto de Otono
# evt-002 - Noche de Monologos
# evt-003 - Festival de Jazz de PrimaveraIf the command fails with
SyntaxError, there is an extra comma, an unclosed quote, or you have used single quotes. In JSON every key and every string goes in double quotes, and a trailing comma after the last element is not allowed.
We still do not know how to read this file from code with fs — that is Module 3 — but it is already in place and already the reference. From there on, src/catalog-data.js will stop carrying the data hard-coded inside it.
- The Repository Folder Structure
This is the target structure, the one the project will have at the end of the course. Do not create all of it now: it will grow with each module.
escena-viva/ ├── .env # Local secrets (NEVER in the repository) - Module 11 ├── .gitignore ├── .nvmrc # The project's Node version ├── package.json # Metadata and dependencies - Module 5 ├── data/ │ ├── events.json # The catalog seed │ └── sales.csv # Sales history for reports - Module 3 ├── reports/ # Generated output (ignored by git) ├── src/ │ ├── catalog.js # Entry point for the console catalog │ ├── catalog-data.js # Access to the catalog data │ ├── domain/ # Event, Session, Order classes - Module 2 │ ├── server/ # HTTP server and routes - Modules 4 and 6 │ ├── middleware/ # Express middleware - Module 6 │ ├── models/ # Database models - Module 7 │ ├── auth/ # Authentication and roles - Module 8 │ └── utils/ # Formatting, validation, helpers └── tests/ # Automated tests - Module 9
Organizing principles we will follow:
- One responsibility per folder. If you do not know where to put a file, it probably does two things.
src/holds only code;data/only data;reports/only generated results. Generated results are never version-controlled.- File names in lower case with hyphens (
catalog-data.js), never with spaces or capitals. Linux distinguishes upper from lower case even when your laptop does not, and that has broken more deployments than anyone can count.
- Road Map: What Each Module Builds
This table is the map of the course. Keep it: every time you start a module you will know which piece of Escena Viva you are adding.
| Module | Topic | Delivery in Escena Viva |
|---|---|---|
| 1 | Introduction | The environment installed, the project skeleton, the console catalog, the data/events.json seed |
| 2 | Core concepts | Domain classes (Event, Session) split into modules; asynchronous loading of the catalog; an EventEmitter that raises the alarm when a session sells out |
| 3 | File system and I/O | Really reading data/events.json with fs; generating reports in reports/; processing data/sales.csv with streams without loading it into memory |
| 4 | HTTP and web servers | Our first server: GET /events, GET /events/:id, creating orders with a JSON POST, and consuming an external exchange-rate API |
| 5 | NPM | The project's package.json, dependencies, the npm start and npm run report scripts, and publishing our own formatting utility package |
| 6 | Express | Migrating the server to Express: catalog and order routes, logging middleware, purchase validation and centralized error handling |
| 7 | Databases | Replacing the JSON with the escena_viva database: models, CRUD for events and sessions, availability queries, and transactions so capacity is never oversold |
| 8 | Authentication | Attendee registration and sign-in, password hashing, JWT sessions, and roles: attendee, organizer and administrator |
| 9 | Testing and debugging | Unit tests for the capacity and price calculations, integration tests for the purchase API, coverage, and debugging a real failure |
| 10 | Advanced topics | Surviving the on-sale spike: cluster, PDF generation in worker threads, catalog caching and an email queue with Redis, and a well-designed REST API |
| 11 | Deployment and DevOps | Per-environment configuration, structured logging, PM2, a Docker image of the platform, deployment and a continuous integration pipeline |
| 12 | Real-world projects | Extensions on what we have learned, and the wrap-up: from project to production |
flowchart LR
M1["M1-2<br/>In-memory data<br/>and console"] --> M3["M3<br/>JSON and CSV<br/>files"]
M3 --> M4["M4-6<br/>HTTP API<br/>and Express"]
M4 --> M7["M7-8<br/>Database<br/>and users"]
M7 --> M9["M9-10<br/>Testing and<br/>performance"]
M9 --> M11["M11-12<br/>Deployment<br/>and production"]
- Project Conventions
These rules apply to all the code in the course. Written down once here, we will not debate them again.
7.1 Identifiers in English
Variables, functions, classes, properties and file names are written in English, with no accents or non-ASCII characters: priceCents, availableTickets, calculateOccupancy, catalog-data.js.
The exceptions are the ones the environment imposes: language keywords (const, class, return), Node and browser APIs (readFile, createServer, map), and package names (express, mongoose).
Why no accented characters? Because an identifier such as sesión is valid JavaScript but turns into a source of bugs the moment it travels through a URL, a file name or an HTTP header. The same applies to our data: the venue and event names keep their Spanish spelling but drop the accents (Sala Boveda, Concierto de Otono).
Style: camelCase for variables and functions, PascalCase for classes, UPPERCASE_WITH_UNDERSCORES for global constants.
7.2 Dates in ISO 8601 format
Every date is stored as a YYYY-MM-DDTHH:mm:ss string.
const dateTime = '2026-10-03T20:00:00'; // Good
const date = '03/10/2026 20:00'; // Bad: ambiguous and not sortableThree reasons:
- It sorts correctly as text, with no conversion to
Date. - It is unambiguous:
03/10/2026is 3 October in Spain and 10 March in the United States. - It survives the trip through JSON. A
Dateobject becomes text when serialized and does not come back as aDatewhen deserialized; if you store text already, there are no surprises.
Formatting it as 03/10/2026 20:00 is the presentation layer's job, never storage's.
7.3 Prices as whole cents
Every monetary amount is stored as a whole number of cents. 2500 means 25.00 EUR.
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(19.99 * 3); // 59.97000000000001
console.log(1999 * 3); // 5997 cents, exact
console.log((5997 / 100).toFixed(2)); // '59.97'JavaScript's number type is a double-precision float (IEEE 754) and cannot represent 0.1 exactly. With money, that error accumulates and ends up as an accounting discrepancy. Working with integers the problem disappears: you only divide by 100 at the very last moment, in order to display.
Naming convention: every monetary field carries the suffix Cents (priceCents, totalCents, discountCents). That way confusion is impossible.
7.4 Ticket codes
Every ticket has a code in this format:
EV-2026-000123 │ │ └── 6-digit sequence, zero-padded on the left │ └─────── Year of issue └────────── Escena Viva prefix
function generateTicketCode(year, sequence) {
// padStart pads with zeros up to 6 characters: 123 -> '000123'
return `EV-${year}-${String(sequence).padStart(6, '0')}`;
}
console.log(generateTicketCode(2026, 123)); // EV-2026-000123Why this format rather than a long random identifier?
- It can be read out over the phone. Someone can dictate it to customer service without mistakes.
- It is sortable and its year is visible at a glance.
- It is short enough to print under a barcode.
The trade-off is that it is predictable: if you know one code you can guess the next. That is why the code is never proof of ownership; validation at the door also checks the ticket's status in the database. It is a conscious decision, of the kind you take constantly on a real project.
7.5 Other conventions
| Convention | Rule |
|---|---|
| File encoding | UTF-8 without BOM |
| Line endings | \n (LF), on Windows too |
| Indentation | 2 spaces |
| Quotes | Single in JavaScript, double mandatory in JSON |
| Semicolons | Yes, always |
| Language of the comments | English |
| Format of JSON files | JSON.stringify(data, null, 2) |
Data on stdout, diagnostics on stderr |
Always |
- A Note on Fictional Data
All the data in this course is made up. The venues, the events, the organizers, the email addresses and the orders that will appear from Module 7 onwards correspond to no real people or companies.
This is not a formality: it is a professional practice you should carry over into your own work.
- Never use real customer data in development, in testing or in demos. Not even "just this once", not even a copy of the production database on your laptop.
- Development databases are populated with synthetic data, generated or anonymized. In Module 7 we will see how to write seeds for exactly that.
- Personal data is regulated. Names, email addresses, phone numbers and payment details carry legal obligations around processing and minimization.
- Never push secrets to the repository: payment gateway keys, database passwords or tokens. They go in environment variables (Module 11), and that is why
.gitignorehas included.envsince the very first lesson.
When we register the user [email protected] in Module 8, notice the .test domain: it is reserved by standard for testing and will never correspond to a real mailbox.
Common Mistakes and Tips
Mistake 1: storing prices as decimals "because it is more convenient". It is, for about two weeks. Then comes the first one-cent discrepancy in a report covering 3,000 orders, and there is no way to work out where it came from.
Mistake 2: using local date formats in the data.
03/10/2026 forces you to know who wrote it in order to interpret it. ISO 8601 in storage, local format only for display.
Mistake 3: putting capacity on the event instead of on the session. It works until the first session with reduced capacity, and then you have to migrate data.
Mistake 4: leaving the JSON file as one giant line.
Every change shows up in version control as "the whole line modified" and reviews become impossible. null, 2 always.
Mistake 5: mixing languages in identifiers.
getEventoById, listarEvents, precioPrice. Pick one — English in this course — and be systematic.
Tip 1: go back to the road map at the start of each module. Knowing which piece you are building completely changes the way you study an API.
Tip 2: use git from day one. A git commit at the end of each lesson gives you a point of return and a history of your own learning.
Tip 3: do not run ahead. It is tempting to set up the Express server right now. Each module introduces the concepts in a deliberate order; skipping it usually ends in code that works without you understanding why.
Tip 4: when in doubt, look at the domain model. A good share of coding decisions answer themselves once you look at the entity table.
Exercises
Exercise 1: prepare the project skeleton
Leave the project ready for Module 2. It must satisfy:
- The
escena-viva/folder containssrc/,data/andreports/. data/events.jsonexists with the three events and is valid JSON.reports/is under version control even though it is empty (hint:.gitkeep)..gitignoreexcludesnode_modules/,.env,*.logand the generated contents ofreports/without excluding the folder..nvmrccontains the LTS version you installed.- There is a
README.mdwith the project name, the three events and the commands to get started. - A first git commit with all of the above.
Write out the full sequence of commands and the contents of each file.
Exercise 2: extend the seed with a fourth event
Add a fourth event to data/events.json with these characteristics:
- Title:
Cuentos al Anochecer - Venue: Teatro Almendra (organizer
org-almendra) - Category:
family, duration 55 minutes, statuspublished - Three sessions on Saturdays 7, 14 and 21 November 2026 at 18:00
- Capacity 200 in each session (the Teatro Almendra holds back part of the stalls for this format)
- Tickets sold: 200, 145 and 30 respectively
- Price: 1200 cents for the first two and 900 for the third
Respect the project's id, date and price formats. Then verify with node -p commands that:
- The file is still valid JSON and has 4 events.
- The catalog now has 10 sessions.
- The total capacity of the Teatro Almendra (adding up all its events) is correct.
- There is exactly one sold-out session.
Exercise 3: apply the conventions
A colleague has written this snippet. Rewrite it respecting all the project conventions and explain each change.
var Sesiones = [
{ID: "S1", fecha: "10/10/2026 21:30", aforo: 120, vendidas: 118, precio: 18.00},
{ID: "S2", fecha: "11/10/2026 21:30", aforo: 120, vendidas: 45, precio: 18.00}
];
function getTotalRevenue(sesiones){
var total = 0
for(var i=0;i<sesiones.length;i++){
total = total + (sesiones[i].vendidas * sesiones[i].precio)
}
return total
}
console.log("Revenue: " + getTotalRevenue(Sesiones))Solutions
Solution 1
# 1. Folder structure
mkdir -p escena-viva/src escena-viva/data escena-viva/reports
cd escena-viva
# 3. Keep "reports" in the repository even while it is empty
touch reports/.gitkeep
# 5. The project's Node version
node -v > .nvmrc.gitignore (point 4). The key is the ! exception, so the contents are ignored but the folder is kept:
node_modules/ .env *.log # We ignore what is generated in reports, but keep the folder reports/* !reports/.gitkeep
README.md (point 6):
# Escena Viva
Ticketing platform for cultural events at the Teatro Almendra,
the Sala Boveda and the Auditorio Ribera.
## Events on sale
- Concierto de Otono (Teatro Almendra)
- Noche de Monologos (Sala Boveda)
- Festival de Jazz de Primavera (Auditorio Ribera)
## Requirements
- Node.js: the version given in `.nvmrc`
## Getting started
nvm use
node src/catalog.js
## Structure
- `src/` source code
- `data/` starting data (events.json)
- `reports/` generated output (not version-controlled)Verification and first commit (points 2 and 7):
node -p "require('./data/events.json').length" # 3
git init
git add .
git commit -m "Escena Viva project skeleton with seed catalog"
git log --onelineSolution 2
The block to insert at the end of the array in data/events.json (remember the comma after the previous event's closing brace):
{
"id": "evt-004",
"title": "Cuentos al Anochecer",
"venue": "Teatro Almendra",
"organizer": "org-almendra",
"category": "family",
"durationMinutes": 55,
"status": "published",
"description": "Oral storytelling for family audiences as the evening falls.",
"sessions": [
{
"id": "ses-004-1",
"dateTime": "2026-11-07T18:00:00",
"capacity": 200,
"sold": 200,
"priceCents": 1200
},
{
"id": "ses-004-2",
"dateTime": "2026-11-14T18:00:00",
"capacity": 200,
"sold": 145,
"priceCents": 1200
},
{
"id": "ses-004-3",
"dateTime": "2026-11-21T18:00:00",
"capacity": 200,
"sold": 30,
"priceCents": 900
}
]
}Verifications:
# 1. Valid JSON and number of events
node -p "require('./data/events.json').length"
# 4
# 2. Total sessions
node -p "require('./data/events.json').reduce((t, e) => t + e.sessions.length, 0)"
# 10
# 3. Total capacity of the Teatro Almendra: 840 (evt-001) + 600 (evt-004) = 1440
node -p "require('./data/events.json').filter(e => e.venue === 'Teatro Almendra').flatMap(e => e.sessions).reduce((t, s) => t + s.capacity, 0)"
# 1440
# 4. Sold-out sessions
node -p "require('./data/events.json').flatMap(e => e.sessions).filter(s => s.sold >= s.capacity).map(s => s.id)"
# [ 'ses-004-1' ]A note on point 4: ses-002-1 has 118 of 120 sold, so it is not sold out. The only sold-out one is the first session of the new event.
Solution 3
// src/utils/revenue.js
// Calculating the revenue of a set of sessions.
const sessions = [
{ id: 'ses-002-1', dateTime: '2026-10-10T21:30:00', capacity: 120, sold: 118, priceCents: 1800 },
{ id: 'ses-002-2', dateTime: '2026-10-11T21:30:00', capacity: 120, sold: 45, priceCents: 1800 }
];
// Returns the total revenue in cents (an integer).
function calculateRevenueCents(sessions) {
return sessions.reduce(
(total, session) => total + session.sold * session.priceCents,
0
);
}
const revenueCents = calculateRevenueCents(sessions);
console.log(`Revenue: ${(revenueCents / 100).toFixed(2)} EUR`);
// Revenue: 2934.00 EURThe changes applied and why:
| Change | Reason |
|---|---|
var → const |
Block scope and no accidental reassignments |
Sesiones → sessions |
Identifiers in English and camelCase (PascalCase is reserved for classes) |
ID, fecha, aforo, vendidas, precio → id, dateTime, capacity, sold, priceCents |
Domain model names, in English |
"S1" → 'ses-002-1' |
The project's identifier format, with a prefix and traceability |
"10/10/2026 21:30" → '2026-10-10T21:30:00' |
ISO 8601: unambiguous and sortable as text |
precio: 18.00 → priceCents: 1800 |
Money as whole cents; the suffix makes it explicit |
getTotalRevenue → calculateRevenueCents |
English and carrying the suffix that states the unit returned |
for loop with an index → reduce |
More declarative and with no mutable variable |
Concatenation with + → a template literal |
Readability |
| Double quotes → single | The project's JavaScript convention (double quotes are left for JSON) |
| Semicolons added | Project convention |
| Comments in English added | Project convention |
Checking the calculation: 118 × 1800 + 45 × 1800 = 212,400 + 81,000 = 293,400 cents = 2,934.00 EUR. With decimal prices the result would have been the same in this particular case, but a price of 18.99 and a few hundred operations are enough for phantom cents to start showing up.
Conclusion
Escena Viva is no longer a backdrop: it is a project with a context, actors, a domain model, data and rules of its own. You have seen who uses the platform — attendee, organizer and administrator — and how that separation anticipates role-based control; you have formalized the entities event, session, ticket, order, user, organizer and venue, with their relationships and their statuses; you have written the seed file data/events.json that will be the data source until the database arrives; and you have settled the conventions that will not be debated again: identifiers in English with no accents, ISO 8601 dates, prices as whole cents, ticket codes like EV-2026-000123, JSON with null, 2 and always fictional data.
Above all, you have the road map. You know that in Module 3 the catalog will stop being hard-coded and start being read from disk, that in Module 4 it will surface over HTTP, that in Module 7 it will move into a database with transactions that make overselling impossible, and that in Module 10 you will learn to withstand the traffic spike of a festival going on sale.
That closes Module 1. You have the environment installed with an LTS version managed through .nvmrc, you know how to run and debug a script, you handle the REPL as a laboratory, you have command of the modern JavaScript we will be using, and the project is standing with its data seed in place.
What you still do not know is how Node.js works on the inside, and that is the missing piece for writing real server code. In Module 2: Core Concepts we open the box: Node's architecture, the event loop and its phases, callbacks, promises and async/await, events with EventEmitter, and the module system with require and module.exports. There, at last, the array of events hard-coded in src/catalog-data.js will become a reusable module, the Event and Session classes will be born in src/domain/, and the waiter who never waits from the first lesson will stop being an analogy and turn into the code you write.
Node.js Course: From Beginner to Advanced
Module 1: Introduction to Node.js
- What Is Node.js?
- Installing and Setting Up the Environment
- Your First Node.js Program
- The Node.js REPL
- Modern JavaScript for Node.js
- The Course Project: the Escena Viva Platform
Module 2: Core Concepts
- Node.js Architecture
- The Event Loop
- Callbacks and Asynchronous Programming
- Promises and async/await
- Events and EventEmitter
- CommonJS Modules and require()
- ES Modules and Interoperability
Module 3: File System and I/O
- Reading and Writing Files
- The fs Module in Depth
- Cross-Platform Paths with the path Module
- Working with Streams
- Transform Streams and pipeline
- Buffers and Binary Data
Module 4: HTTP and Web Servers
- Creating a Simple HTTP Server
- Handling Requests and Responses
- Manual Routing
- Serving Static Files
- Receiving Data: Request Bodies and JSON
- Consuming External APIs from Node.js
Module 5: NPM and Package Management
- Introduction to NPM and package.json
- Installing and Using Packages
- Semantic Versioning and package-lock
- npm Scripts and Project Automation
- Creating and Publishing Packages
- Dependency Security and Maintenance
Module 6: The Express.js Framework
- Introduction to Express.js
- Setting Up an Express Application
- Routing in Express
- Middleware
- Essential Third-Party Middleware
- Input Data Validation
- Error Handling
Module 7: Databases and ORMs
- Introduction to Databases
- Using MongoDB with Mongoose
- CRUD Operations
- Relationships, Population and Advanced Queries
- Using SQL Databases with Sequelize
- Migrations, Transactions and Seed Data
Module 8: Authentication and Authorization
- Introduction to Authentication
- User Registration and Password Hashing
- Sessions and Cookies with Passport.js
- Authentication with JWT
- Role-Based Access Control
- API Security Best Practices
Module 9: Testing and Debugging
- Introduction to Testing
- Unit Testing with Mocha and Chai
- Test Doubles with Sinon
- Integration Testing
- Coverage and Test Automation
- Debugging Node.js Applications
Module 10: Advanced Topics
- The Cluster Module
- Worker Threads
- Caching and Job Queues with Redis
- Performance Optimization
- Building RESTful APIs
- GraphQL with Node.js
Module 11: Deployment and DevOps
- Configuration and Environment Variables
- Logging and Monitoring in Production
- Using PM2 for Process Management
- Packaging with Docker
- Deploying to Heroku and Other PaaS
- Continuous Integration and Deployment
