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

  1. The decision that really fails in projects
  2. Relational versus NoSQL: which question each model answers well
  3. Managed (PaaS) versus installed on a VM (IaaS)
  4. Azure's data catalog as a decision table
  5. The criteria that decide
  6. Consistency versus latency: the CAP theorem in practical terms
  7. How a database is billed in the cloud
  8. Contoso Airlines' data map
  9. Common Mistakes and Tips
  10. Exercises
  11. Conclusion

  1. 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?".

  1. 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.

  1. 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.

  1. 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.

  1. 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:

  1. 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.
  2. Consistency versus latency. We develop this in the next section.
  3. 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.
  4. 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.
  5. 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.
  6. 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.

  1. 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.

  1. 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-reservas in 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.

  1. 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-reservas at 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:

  1. 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.
  2. 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.
  3. Storing the signed PDF contracts with travel agencies, around 400 a year, searchable by agency name.
  4. A new system for recommending alternative routes when flights are cancelled, which needs to find paths between airports with a maximum of two stops.
  5. 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:

  1. An aircraft maintenance application that requires SQL Server 2016 with an auditing agent from an external vendor that installs as a Windows service.
  2. A new database for the loyalty program, with no legacy dependencies.
  3. 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.

  1. Which single-parameter change removes the most spend, knowing the team works 45 hours a week?
  2. Is 35 days of retention reasonable in development? What would you propose?
  3. 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:

  1. 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.
  2. PaaS, Azure SQL Database. There are no dependencies tying you down, and you get automated backups, patching and high availability with no operational work.
  3. 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:

  1. 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-reservas in rg-contoso-reservas-dev.
  2. 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.
  3. List the resources without the mandatory tags, or with entorno=dev, created more than a week ago, and go back to the propietario. With Azure CLI it is solved with az 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

Module 2: Core Azure Services

Module 3: Azure Databases

Module 4: Security in Azure

Module 5: Azure DevOps

Module 6: Advanced Azure Services

Module 7: Monitoring and Management

Module 8: Cost Management and Optimization

Module 9: Case Studies and Best Practices

© Copyright 2026. All rights reserved