For two whole modules, db-reservas has been a box with a label on it. It appeared in the network diagram inside snet-datos, the App Service application connected to it through a connection string, and the private endpoint pe-sql-reservas reserved its place. But nobody has decided yet what that box actually is on the inside, and that decision — made in a half-hour meeting or inherited from "what we were already using" — is the most expensive one to correct later.
Resizing a virtual machine takes five minutes. Changing an App Service plan, two clicks. Changing the database engine of a platform in production is a project that runs for months: you have to rewrite queries, migrate historical data, retrain the team and stop selling tickets during the cutover window. That is why this lesson deploys nothing: it builds the judgement. By the end you will have Contoso Airlines' complete data map, with a reasoned decision per system, and every one of those decisions will be a lesson in this module.
Cost warning: this lesson is purely about design and creates no resources, so it generates no bill. From the next one onwards it does: databases are among the most expensive resources in Azure and, unlike a virtual machine, many of them cannot be stopped. Always read the cost section before running an
az ... create.
Contents
- The decision that really fails in projects
- Relational versus NoSQL: which question each model answers well
- Managed (PaaS) versus installed on a VM (IaaS)
- Azure's data catalog as a decision table
- The criteria that decide
- Consistency versus latency: the CAP theorem in practical terms
- How a database is billed in the cloud
- Contoso Airlines' data map
- Common Mistakes and Tips
- Exercises
- Conclusion
- The decision that really fails in projects
In cloud migration projects that go wrong, the point of failure is rarely compute. It is the data. And almost always for one of these three reasons:
- It is chosen by fashion, not by question. "Let's use NoSQL because it scales" is an empty sentence if nobody has first written down what queries the application will make. NoSQL scales for the access patterns its design anticipated; for everything else it is worse than a relational database.
- It is chosen out of inertia. "We have always used SQL Server, so let's put SQL Server." Sometimes that is the right answer — compatibility is a legitimate and powerful criterion — but it must be a conclusion, not a starting point.
- A single database is chosen for everything. This is the most expensive mistake. A real platform has several systems with opposing needs, and forcing them all onto the same engine means none of them works well. Using the right store for each workload is called polyglot persistence, and it is exactly what Contoso is going to do.
The right question is not "which database is best?", but "what questions do I have to answer, how often, over how much data and in how much time?".
- Relational versus NoSQL: which question each model answers well
A relational model organizes data into tables of rows and columns with a fixed schema, relationships declared through foreign keys, and ACID transactions. Its superpower is the arbitrary query: you can join tables nobody ever expected to be joined, and the engine will find a way to resolve it.
NoSQL is not a model but a family of models that give up part of that in exchange for scale, schema flexibility or latency:
| Model | How it stores the data | Question it answers very well | Question it answers badly | Example at Contoso |
|---|---|---|---|---|
| Relational | Normalized tables with relationships | "How many passengers on a flexible fare flew from BCN to CDG in March, and how much did they pay?" | Anything demanding millions of writes per second with single-digit latency | Bookings, flights, passengers |
| Document | A complete JSON document with an identifier | "Give me the full record for this fare, with all its nested conditions" | "Cross-reference fares with bookings and with incidents" | Fare catalog |
| Key-value | Key → opaque value, in memory | "What is stored under this key, right now?" (sub-millisecond) | Any query over the content of the value | Search result cache |
| Graph | Nodes and edges with properties | "Which two-stop flight combinations connect Palma with Osaka?" | Massive aggregations over the whole set | Connection network (future) |
| Columnar | Data grouped by column | "Sum the revenue of 400 million rows by route and month" | Reading or modifying one specific row | Profitability analytics |
Two clarifications that avoid common misunderstandings:
- NoSQL does not mean "no schema", it means "no schema enforced by the engine". The schema still exists: it lives in the application code, which is a worse place to police it.
- NoSQL does not mean "no transactions". Cosmos DB has ACID transactions, but only within a single logical partition. The difference is in the scope, not in whether they exist.
- Managed (PaaS) versus installed on a VM (IaaS)
You can install PostgreSQL on one of the virtual machines you deployed in lesson 02-01. It will work. The question is what work you are accepting in exchange for what control, applying the shared responsibility model from module 1:
| Task | On a VM (IaaS) | Managed service (PaaS) |
|---|---|---|
| Operating system patching | Yours, every month | Azure's, during a maintenance window |
| Engine patches and versions | Yours | Azure's, with major versions you choose when to jump to |
| Backups | You configure, verify and restore them | Automated, with configurable retention |
| High availability | You build the cluster (Always On, Patroni…) | A checkbox and an SLA |
| Scaling compute | Resize and restart the VM | A live change, sometimes with no outage |
| Encryption at rest | You configure it | Enabled by default |
| Operating system access | Full | None |
| Arbitrary extensions or binaries | Anything | Only those on the allowlist |
| Third-party agents on the server | Yes | No |
| Hourly cost of the resource | Lower | Higher (it includes the work you are not doing) |
The practical rule is simple: use PaaS unless you have a specific, written reason not to. Legitimate reasons do exist — an old unsupported version, a third-party agent that insists on being installed on the server, SQL Server features that only exist in the full instance, or a licensing requirement — but they are a minority. The hourly cost of a VM looks lower until you add up the hours Marta Ríos spends patching servers on a Sunday.
- Azure's data catalog as a decision table
This is the complete map of what Azure offers for data. Read it as a decision table, not as a sales catalog:
| Service | Model | Choose it when… | Rule it out when… |
|---|---|---|---|
| Azure SQL Database | Relational PaaS | A new or modernized application on SQL Server; you want the maximum of automatic management | You need SQL Agent, CLR, distributed transactions or several databases with cross dependencies |
| SQL Managed Instance | Relational PaaS, almost 100% compatible | You are migrating a complete on-premises SQL Server without touching the code | The budget is tight: it is noticeably more expensive |
| SQL Server on a VM | Relational IaaS | You need operating system control, a specific version or your own licenses | You can avoid it: it is the option with the most operational work |
| Azure Cosmos DB | Distributed multi-model NoSQL | Global scale, single-digit millisecond latency, flexible schema, enormous volumes | Your queries are analytical or cross entities with no predictable pattern |
| Azure Database for MySQL | Relational PaaS | You are migrating open source applications (WordPress, Drupal, LAMP) | Your team already lives in the SQL Server ecosystem |
| Azure Database for PostgreSQL | Relational PaaS | You need advanced SQL, rich data types and extensions (PostGIS, pgvector) | You are after maximum compatibility with SQL Server |
| Azure Cache for Redis | In-memory key-value | Caching expensive results, sessions, rate limits | You intend to use it as the primary store: it is volatile memory |
| Table Storage | Key-value/table, very cheap | Massive, simple records accessed by key | You need queries or secondary indexes |
| Data Lake Storage Gen2 + Synapse | Analytical | Analyzing a massive history without punishing the operational database | The query is transactional and must answer in milliseconds |
Redis and Table Storage appear here because they complete the map, but they do not have their own lesson in this module: Table Storage was already covered in 02-04 and Redis will be used as a cache in module 6.
- The criteria that decide
When you have to justify a choice to a committee — and at Contoso you do, because Nuria Peña will show up in module 8 asking about the bill — it is worth scoring six criteria:
- Data model. Does the data have the shape of a table with relationships, of a self-contained document, of a key-value pair or of a graph? Write down three real queries the application will make and check whether the model answers them naturally.
- Consistency versus latency. We develop this in the next section.
- Volume and growth. Not today's: the one three years from now. Contoso holds around 40 GB of active bookings and grows by 12 GB a year; that fits comfortably in any option. The boarding event logs, by contrast, grow by 200 GB a year and rule out the relational database on their own.
- Read and write pattern. Are reads or writes dominant? Access by key or by complex query? Predictable peaks? The fare catalog is read thousands of times a minute and written twice a day: an ideal case for a document store with a cache.
- Compatibility with what already exists. Contoso's content portal is WordPress and it speaks MySQL. Rewriting it to speak something else adds no business value at all.
- Cost and team skills. An engine nobody knows how to operate is an incident waiting its turn. Diego Salas knows SQL Server and PostgreSQL; nobody on the team has ever touched Cassandra, which rules out that Cosmos DB API with no further discussion.
- Consistency versus latency: the CAP theorem in practical terms
The CAP theorem says that a distributed system cannot simultaneously guarantee consistency (C), availability (A) and tolerance to network partitions (P). Since the network can always split, the real choice is: when two data centers stop seeing each other, would you rather give out possibly stale data or deny service until you are certain?
Translated into Contoso's business, with two examples that lead to opposite answers:
- Free seats on a flight. If two servers disagree, two tickets get sold for the same seat and somebody is left on the ground, with compensation regulated by European law. Here you choose consistency: better an error than a seat sold twice.
- The number of loyalty points shown on a profile. If a passenger sees 12,400 points and the real balance is already 12,550 because yesterday's flight has just been settled, nothing happens: it will correct itself in a few seconds. Here you choose latency and availability.
The practical consequence is that the answer does not belong to the system but to the data: the same application can have data that demands strict consistency and data that tolerates delay, and that is why it ends up using two different stores. In lesson 03-03 you will see that Cosmos DB does not force you to choose once and for all: it offers five consistency levels, adjustable even per request.
- How a database is billed in the cloud
All of Azure's managed databases bill on the same four dimensions, even if each one calls them something different:
| Dimension | What you pay for | What drives it |
|---|---|---|
| Compute | vCores or units per hour | The service tier; it is the dominant line |
| Storage | GB provisioned or consumed per month | The volume of data and the indexes |
| Backups | GB of retention beyond what is included | Long retention and large databases |
| Data transfer out | GB that leave the region | Queries returning too many columns or crossing regions |
The distinction that saves the most money is provisioned versus serverless:
- Provisioned: you reserve a fixed capacity and pay for it 24 hours a day, whether it is used or not. It is the right thing for a sustained, predictable load, like
db-reservasin production. - Serverless: you pay for actual per-second consumption and, if the engine supports it, the database pauses after a while with no connections, and stops billing compute (storage is always still paid for). It is the right thing for development, testing and intermittent workloads.
The back-of-the-napkin math Contoso will do: the development environment is used about 45 hours a week out of the 168 there are. Paying provisioned means throwing away 73% of the spend. With serverless and auto-pause, that 73% disappears from the bill without anyone changing how they work.
And a warning that will be repeated throughout the module: a provisioned database cannot be "switched off" like a VM. There is no equivalent of the deallocated state from lesson 02-01. If you leave a test server created and forget about it, it keeps billing every hour until you delete it.
- Contoso Airlines' data map
Applying the six criteria to the airline's five systems, this is the result, and also the script for the rest of the module:
| System | Data | Service chosen | Deciding criterion | Lesson |
|---|---|---|---|---|
| Bookings and flights | Flights, passengers, bookings, payments | Azure SQL Database | ACID transactions and referential integrity: a seat cannot be sold twice | 03-02 |
| Fare catalog and profiles | Fares with nested conditions, passenger preferences | Azure Cosmos DB | Schema that varies per fare, massive reads by key and low latency | 03-03 |
| Content portal and blog | Legacy WordPress | Azure Database for MySQL | Compatibility: zero rewriting | 03-04 |
| Crew planning | Shifts, licenses, bases, routes | Azure Database for PostgreSQL | Complex queries and geospatial extensions | 03-05 |
| Profitability analytics | Sales and occupancy history | Data Lake + Synapse | Enormous volume and aggregations that must not touch the operational database | 03-06 |
This is polyglot persistence: five stores because there are five different questions. The complete diagram, with module 2's network in the background:
flowchart TB
subgraph clientes[Customers and staff]
WEB[Contoso Bookings<br/>App Service]
API[Availability API<br/>VMSS + App Service]
PANEL[Operations dashboard]
BLOG[Content portal<br/>WordPress]
TRIP[Crew planning]
end
subgraph operacional[Operational data - snet-datos]
SQL[(Azure SQL Database<br/>db-reservas)]
COSMOS[(Cosmos DB<br/>fares and profiles)]
MYSQL[(MySQL flexible<br/>legacy portal)]
PG[(PostgreSQL flexible<br/>crew planning)]
end
subgraph analitico[Analytics platform]
ADF[Azure Data Factory]
LAKE[(Data Lake Gen2<br/>bronce / plata / oro)]
SYN[Synapse Analytics]
PBI[Power BI]
end
WEB --> SQL
WEB --> COSMOS
API --> SQL
API --> COSMOS
PANEL --> SQL
BLOG --> MYSQL
TRIP --> PG
SQL -.nightly copy.-> ADF
COSMOS -.nightly copy.-> ADF
PG -.nightly copy.-> ADF
ADF --> LAKE --> SYN --> PBI
Look at the dashed arrows: analytics never queries the operational database directly. That is the principle behind section 1 of lesson 03-06.
Common Mistakes and Tips
- Choosing the engine before writing the queries. If you cannot list your application's five most frequent queries, you do not have enough information to decide. Write them down first, even in plain language.
- Using NoSQL to avoid designing the model. Schema flexibility does not eliminate design: it moves it into the code, where no engine validates anything. Six months later, four different shapes of the same document coexist.
- Putting analytical data in the operational database. A three-year profitability report fired at
db-reservasat eleven in the morning degrades ticket sales for every customer. Separate them from the start. - Forgetting that a database does not switch off. This is the mistake that shows up most in the first bills: three servers get created "to try things out" and they are still there a month later. Always apply the mandatory tags (
entorno,proyecto,centro-coste,propietario) so you can track down who is responsible for each resource. - Ignoring the team's skills. The theoretically optimal engine that nobody knows how to diagnose at three in the morning is worse than the correct engine everybody knows.
- Tip: document every decision with a one-page record (context, options, decision, consequences). When somebody asks two years from now why the fares are in Cosmos DB, that page will save a week of argument.
- Tip: if you are torn between relational and document and the volume is moderate, start relational. Adding a document store later is straightforward; rebuilding the referential integrity you never had is not.
Exercises
Exercise 1: classifying five new workloads
Contoso Airlines raises five additional needs. For each one, choose the data service and justify it with at least two of the six criteria:
- Storing every boarding pass scan event at the gates: around 90 million records a year, constant writing, later queried only by flight number and date.
- Caching the result of the search "BCN → LHR, 12 July" for 60 seconds so as not to hit the API on every keystroke from the user.
- Storing the signed PDF contracts with travel agencies, around 400 a year, searchable by agency name.
- A new system for recommending alternative routes when flights are cancelled, which needs to find paths between airports with a maximum of two stops.
- The real-time status of the 38 aircraft in the fleet, updated every 5 seconds and queried by the operations dashboard.
Exercise 2: PaaS or IaaS
Justify in each case whether Contoso should use a managed service or a virtual machine:
- An aircraft maintenance application that requires SQL Server 2016 with an auditing agent from an external vendor that installs as a Windows service.
- A new database for the loyalty program, with no legacy dependencies.
- A SQL Server instance with 14 databases that today talk to each other through cross-database queries and SQL Agent jobs.
Exercise 3: estimating and cutting the cost
The team proposes this development environment: a provisioned relational database with 4 vCores running all month (roughly €480 a month at approximate list price), 100 GB of storage and 35 days of backup retention.
- Which single-parameter change removes the most spend, knowing the team works 45 hours a week?
- Is 35 days of retention reasonable in development? What would you propose?
- What check would you run every Monday to avoid forgotten databases?
Solutions
Solution 1:
| Case | Service | Justification |
|---|---|---|
| 1. Scan events | Table Storage (or Cosmos DB if low latency is needed) | Enormous volume and growth with access by key (flight + date); there are no complex queries, so relational power adds nothing and its cost per GB is far higher |
| 2. Search cache | Azure Cache for Redis | Key-value access pattern with a 60-second lifetime and sub-millisecond latency; the data is regenerable, so durability is not a criterion |
| 3. PDF contracts | Blob Storage with metadata, plus an index in the relational database | Data model: these are files, not rows. Storing them as binaries in a database inflates the backups and drives up storage cost |
| 4. Alternative routes | Cosmos DB with the Gremlin API (graph) | The model is a network of nodes and edges; "paths with two stops" in SQL requires several nested joins and does not scale |
| 5. Fleet status | Cosmos DB or Redis | Constant writes by key, low-latency reads and tolerance for relaxed consistency: if the dashboard shows the position from 3 seconds ago, nothing happens |
Solution 2:
- A VM (IaaS), or at least it has to be seriously considered: the external agent needs to be installed on the operating system, and in PaaS there is no operating system to reach. Before giving up, it is worth checking whether the vendor has a PaaS-compatible version.
- PaaS, Azure SQL Database. There are no dependencies tying you down, and you get automated backups, patching and high availability with no operational work.
- SQL Managed Instance. This is exactly the case it exists for: it supports cross-database queries and SQL Agent — which Azure SQL Database does not offer — without giving up the managed model. Migrating to Azure SQL Database would force you to rewrite those 14 applications.
Solution 3:
- Move the tier to serverless with auto-pause. With 45 hours of use out of 168, compute is billed roughly 27% of the time: the saving is around 70% of the compute line, without changing anything in the team's day-to-day work. It is exactly the configuration Contoso will adopt for
db-reservasinrg-contoso-reservas-dev. - No, it is excessive. In development the data is synthetic and regenerable: 7 days is enough and it reduces the backup line. The 35 days make sense in production, where there is a legal obligation and irreplaceable data.
- List the resources without the mandatory tags, or with
entorno=dev, created more than a week ago, and go back to thepropietario. With Azure CLI it is solved withaz resource list --tag entorno=dev --query "[].{n:name, t:type}" -o table, taking advantage of the JMESPath filtering from lesson 01-06. In module 4 this will be automated with Azure Policy, and in module 8 with Cost Management.
Conclusion
This lesson has not created a single resource, and even so it is the one that can save Contoso Airlines the most money. You now know why choosing the data store is the least reversible decision in an architecture, and why it almost always fails for the same three reasons: fashion, inertia, or wanting a single engine for everything. You can tell the relational model apart from the four NoSQL families — document, key-value, graph and columnar — by the question each one answers well, not by its slogan. You have in a table what you gain and what you lose moving from a database installed on a VM to a managed service, and the criterion for knowing when IaaS is still justified. You know Azure's complete catalog of data services as a decision table, with the typical case for each one and, more useful still, the case in which it has to be ruled out.
You also have the six criteria for defending a choice to a committee — model, consistency versus latency, volume, access pattern, compatibility and team — a practical reading of the CAP theorem applied to two real pieces of airline data with opposite answers, and the four dimensions on which any managed database bills, with the distinction between provisioned and serverless that wipes out 73% of the development environment's spend in one move. And above all you have Contoso's data map: five systems, five stores, a written justification for each one.
That map is the script for the next five lessons, and we start with the heart of the business. In the next lesson, Azure SQL Database, you will finally deploy the logical server sql-contoso-reservas-pro and the db-reservas database: you will choose between the DTU and vCore purchasing models, understand why the "server" is not a machine, create the schema of flights, passengers and bookings with reasoned indexes, and connect the database to snet-datos through the private endpoint pe-sql-reservas with public access disabled, closing the gap you left open in module 2. And you will see how to recover the data when somebody runs a badly prepared migration on a Tuesday afternoon.
Azure Course
Module 1: Introduction to Azure
- What Is Azure?
- Service Models, Regions and Availability Zones
- Creating and Setting Up Your Azure Account
- A Tour of the Azure Portal
- Azure Resource Manager: Subscriptions, Resource Groups and Tags
- Azure CLI, PowerShell and Cloud Shell
Module 2: Core Azure Services
- Azure Virtual Machines
- Compute Scaling and High Availability
- Azure App Service
- Azure Storage: Blobs, Files, Queues and Tables
- Azure Networking: Virtual Networks, Subnets and NSGs
- Hybrid Connectivity and Global Delivery
Module 3: Azure Databases
- Choosing the Right Data Service
- Azure SQL Database
- Azure Cosmos DB
- Azure Database for MySQL
- Azure Database for PostgreSQL
- Data Analytics: Data Lake, Data Factory and Synapse
Module 4: Security in Azure
- Microsoft Entra ID and Identity Management
- RBAC and Managed Identities
- Azure Key Vault
- DDoS Protection and Web Application Firewall
- Microsoft Defender for Cloud
- Governance and Compliance with Azure Policy
Module 5: Azure DevOps
- Introduction to Azure DevOps
- Azure Repos
- Azure Pipelines: Continuous Integration
- Continuous Deployment with Environments and Approvals
- Azure Artifacts
- Infrastructure as Code with Bicep
Module 6: Advanced Azure Services
- Containers in Azure: Container Registry and Container Apps
- Azure Kubernetes Service (AKS)
- Azure Functions
- Azure Logic Apps
- Messaging and Events: Service Bus, Event Grid and Event Hubs
- Azure AI Services
Module 7: Monitoring and Management
- Azure Monitor: Metrics, Alerts and Dashboards
- Log Analytics and KQL Queries
- Application Insights
- Azure Automation and Runbooks
- Backup and Disaster Recovery
Module 8: Cost Management and Optimization
- Pricing Calculator and Cost Estimation
- Azure Cost Management: Analysis, Budgets and Alerts
- Reservations, Savings Plans and Azure Hybrid Benefit
- Azure Advisor
- Optimization Strategies and FinOps Culture
