Module 1: From Flat Tables To Dimensional Models

Step 1: selecting Kiosko's business process

Description

With the full process now clear from the previous lesson's coffee shop, this lesson takes the first real step on Kiosko: select the business process. Kiosko has, inherited from foundations, two raw data streams — orders (point of sale) and events (the app's clickstream, with page_view/add_to_cart/purchase) — and each one could be the seed of a different business process. This lesson doesn't pick at random: it compares both candidates with an explicit criterion, and justifies why this guide starts with orders.

Connection to the module. This lesson resolves step 1 of Kimball's process for fact_orders, the table lessons 5 and 6 of this module are going to finish declaring. events isn't discarded — it comes back as the star of module 6, when you build the accumulating snapshot for the session funnel — it's just postponed for a concrete reason.

An analogy: the first day's menu, not the full menu

A new restaurant with an ambitious chef could, in theory, open with a thirty-dish menu on day one. In practice, no serious restaurant does that: it opens with two or three well-executed dishes, learns from real operations — what gets ordered most, what takes too long in the kitchen, which ingredient runs out fast — and expands the menu afterward, with the confidence of having gotten the first part right. Choosing which dish comes out first isn't a minor decision: it determines what the restaurant learns in its first weeks.

Kiosko has two candidate "dishes" for this guide's first dimensional model: sales orders (orders) and app browsing sessions (events). This lesson picks one to serve first, with explicit criteria — not because the other doesn't matter, but because a well-built dimensional model, learned thoroughly on one process, is the foundation the second one gets built on faster and with fewer mistakes.

Worked example: comparing the two candidate processes

Before choosing, it's worth putting the two candidates side by side, with an explicit criterion for each:

# business_process_candidates.py
CANDIDATES = [
    {
        "process": "Sales orders (orders)",
        "source": "Each store's point of sale, exported nightly",
        "data_ready_today": True,
        "measurable_event": "One point-in-time sale: product, quantity, price, instant",
        "already_modeled": "fact_orders, dim_store, dim_product (foundations M4/M8)",
    },
    {
        "process": "Browsing sessions (events)",
        "source": "Delivery app, real-time clickstream",
        "data_ready_today": False,
        "measurable_event": "page_view / add_to_cart / purchase, per session",
        "already_modeled": "No fact table yet -- only the raw JSON Lines format",
    },
]

print("=== Business process candidates for the first dimensional model ===\n")
for c in CANDIDATES:
    print(f"Process:            {c['process']}")
    print(f"Source:             {c['source']}")
    print(f"Data ready today:   {c['data_ready_today']}")
    print(f"Measurable event:   {c['measurable_event']}")
    print(f"Already modeled as: {c['already_modeled']}")
    print()

chosen = CANDIDATES[0]
print(f"Process chosen for this module: {chosen['process']}")

What to expect. Running python3 business_process_candidates.py, the output is exactly this:

=== Business process candidates for the first dimensional model ===

Process:            Sales orders (orders)
Source:             Each store's point of sale, exported nightly
Data ready today:   True
Measurable event:   One point-in-time sale: product, quantity, price, instant
Already modeled as: fact_orders, dim_store, dim_product (foundations M4/M8)

Process:            Browsing sessions (events)
Source:             Delivery app, real-time clickstream
Data ready today:   False
Measurable event:   page_view / add_to_cart / purchase, per session
Already modeled as: No fact table yet -- only the raw JSON Lines format

Process chosen for this module: Sales orders (orders)

The decision isn't "orders matters more than events" — in fact, the conversion funnel you're going to build on top of events in module 6 is one of the most valuable business pieces in this entire guide. The decision is one of sequence: orders already has a fact table built and verified (foundations left it ready), so formally declaring its grain is the most direct exercise possible for learning Kimball's process for the first time, without the added complication of building a fact table from scratch at the same time. events, on the other hand, is still raw JSON Lines with no fact table on top of it — building that table from scratch and learning to declare its grain at the same time would repeat the mistake lesson 3 of foundations' module 8 already warned about: teaching two new things at once dilutes both.

Diagram: two processes, two moments in this guide

flowchart TD
    subgraph Fuentes["Kiosko's sources (inherited from foundations)"]
        A["orders\n(point of sale)"]
        B["events\n(app clickstream)"]
    end

    A --> C["fact_orders\nALREADY BUILT (foundations)\nonly needs the grain formally declared"]
    B --> D["fact_sessions\nNOT built yet\n(accumulating snapshot, module 6)"]

    C --> E["This module (M1): fact_orders grain"]
    D --> F["Module 6: business process +\ngrain + dimensions + facts for fact_sessions\nfrom scratch"]

Going deeper: why "the data already exists" isn't the only criterion

It's tempting to think the criterion for choosing a business process is simply "do I already have the data?". It's a real criterion — and in Kiosko's case, it favors orders — but it's not the only one, and it's worth naming the other two the Kimball Group mentions: the business's real needs, and how well understood the process is by the team that's going to model it.

On business needs: Kiosko's store manager, in the brief that opened foundations, specifically asked for a sales report — not a browsing-behavior report. That, by itself, already tips the scale toward orders as the process with the greatest business urgency today. On how well understood the process is: orders is a simple, already-familiar process — a sale, a product, a price — while events introduces new concepts (sessions, milestones that advance without creating new rows) that deserve their own complete module, instead of being rushed and mixed in here.

No single criterion, on its own, would have been enough. It was the combination of all three — available data, business urgency, and manageable conceptual complexity — that made it clear orders had to be this guide's first process. The next time you have to choose what to model first in a real project, those same three criteria — not just "what data do I have on hand" — are the ones worth putting on the table.

Common mistakes

Choosing the process that's easiest to code, not the one with the most business value. What happens: someone prioritizes modeling whatever is technically simplest to build, without asking what the business needs first. Why it happens: it's natural, when learning a new technique, to gravitate toward the example with the least technical friction. How to spot it: if your justification for choosing a business process doesn't mention any concrete business need (like Kiosko's store manager's brief), only technical ease, you're missing one of this lesson's three criteria. How to fix it: like the worked example did, compare candidates using all three criteria — available data, business urgency, manageable complexity — not just one.

Dismissing events as "unimportant" instead of "postponed for a reason." What happens: someone concludes, seeing this module choose orders, that the app's clickstream is secondary or low-value data for Kiosko. Why it happens: it's easy to confuse "not modeled first" with "doesn't matter." How to spot it: if your summary of this lesson says something like "events isn't that important," revisit the "Connection to the module" section at the start — events is, explicitly, module 6's entire centerpiece. How to fix it: remember that this guide's sequence is pedagogical, not a business-importance hierarchy — both processes end up modeled with the same level of rigor before the guide closes.

Trying to model both processes in a single fact table. What happens: someone, wanting to "be efficient," tries to design a single table that captures both sales and browsing events, mixing two completely different grains. Why it happens: having "one big table" seems simpler than two separate tables. How to spot it: if your design mixes orders columns (quantity, unit_price) with events columns (event_type, session_id) in the same row, with no real business relationship between them at that level of detail, you've already lost the grain of both processes at once. How to fix it: different business processes — with events that occur at different moments and different levels of detail — almost always need separate fact tables. You're going to build two completely separate fact tables in this guide (fact_orders and fact_sessions), and that's correct, not wasteful.

Exercises

Exercise 1 — Apply the three criteria to a hypothetical third candidate. Imagine Kiosko also had an inventory_movements.csv file (inventory movements: restocking and shrinkage) — a third candidate business process, with no data available yet and not requested in the store manager's brief. Using this lesson's three criteria, argue in 2-3 sentences why this candidate would, quite reasonably, fall outside this guide's scope.

See solution

inventory_movements would fail all three criteria at once: the data doesn't exist yet in any real Kiosko file (criterion 1), the manager never asked for it in her brief — her urgency was the sales report, not inventory control (criterion 2), and modeling it well would require understanding a new business process (restocking, shrinkage, minimum stock levels) that no earlier module in the guide prepared (criterion 3). All three criteria pointing in the same direction is exactly the evidence that makes leaving it out of scope easy, not arbitrary.

Exercise 2 — Argue the reverse order. Someone proposes this guide should have started with events (browsing sessions) instead of orders, because "the clickstream is the more modern data type and closer to what real tech companies use." Evaluate that argument using this lesson's three criteria: is it valid?

See solution

The argument has some truth to it — the clickstream is indeed a very common data type at real tech companies — but "resembles what modern companies use" isn't any of this lesson's three criteria: it doesn't resolve whether the data is already ready (it isn't — events is still raw JSON Lines, with no fact table), it doesn't resolve business urgency (the brief asked for sales, not browsing behavior), and it doesn't resolve manageable complexity (an accumulating snapshot is a more advanced concept than declaring the grain of an already-built table). "Resembles what modern companies use" is a criterion of fashion, not of method — exactly the kind of reasoning Kimball's process seeks to replace with explicit, verifiable criteria.

Exercise 3 — Declare fact_orders's business process in one sentence. Without looking at the worked example, write in a single sentence what business process you're going to model for the rest of this module, using the same format lesson 3 used for the coffee shop ("selling a drink").

See solution

A reasonable answer: "selling a product at a Kiosko store" — the same kind of specific, point-in-time, measurable event you already modeled without realizing it in foundations. Notice this sentence still says nothing about the grain — that's precisely step 2, and lesson 5 resolves it with an executed query; step 1 only answers "what business event," not "what does each row represent."

Summary and next step

In this lesson you resolved step 1 of Kimball's process for Kiosko: you compared the two candidate business processes — sales orders and browsing sessions — using three explicit criteria (available data, business urgency, manageable complexity), and chose selling a product at a Kiosko store as the process this module models first. events isn't discarded — it comes back, with the same rigor, in module 6.

Before moving on you should be able to: name the three criteria for choosing a business process, unaided; explain why "the data already exists" isn't the only valid criterion; and declare, in one sentence, the business process chosen for fact_orders.

With the business process already chosen, lesson 5 resolves step 2 — declaring the grain — and is the central lesson of the entire module: you're going to actually run, on DuckDB, the query that confirms with real numbers exactly what a row of fact_orders represents.

Resources

  • Kimball Group — "Four-Step Dimensional Design Process" — the source that frames step 1 as applied in this lesson. kimballgroup.com/.../four-4-step-design-process. In English.
  • Joe Reis & Matt Housley, Fundamentals of Data Engineering (O'Reilly, 2022) — the data generation framework (the chapter on data sources) that supports why orders and events are distinct business processes, with distinct lifecycles. oreilly.com/library/view/fundamentals-of-data/9781098108298. In English.
  • JSON Lines — the specification for the format events.jsonl uses, the raw format waiting its turn until module 6. jsonlines.org. In English.