IoT events are facts: model them like service, not like logs
Telemetry tables named after pipelines invite people to treat events like disposable logs. Reports then reinvent grain, matching, and captions in DAX. Volume pushes Import mode until refresh becomes the weekly outage.
Name the business concept (events). Enrich with the same matching logic used by service-device facts. Land them in gold for Direct Lake. Captions, query folders, and descriptions are part of the semantic handoff. Watermark shortcuts and clean views are part of the bronze-to-gold story, not footnotes.

Figure 1. IoT events are facts. Land them as modeled destinations, not as log piles that only Import mode can fake into a report. Source: Microsoft Learn: Eventstreams overview.
I learned this when event volumes that were "fine in the lab" met Import refresh windows and mismatched device keys versus service facts. The lake had data. The semantic model had a log with aspirations.
The core idea
High-volume device events are facts. Model them with business names, shared enrichment, and a storage mode that fits the cardinality.
Direct Lake is the default posture for large event facts when Delta layout cooperates. Import is a trap at this grain. Enrichment must align with service-device matching so reports join cleanly instead of arguing across pages. Bronze watermarks and clean views feed gold; they do not replace a fact contract.
A model that stays explainable
1. Name the business concept, not the pipeline table
Call the gold object what analysts say: device events, measuring-point events, alarms. Pipeline names (raw_telemetry_v3_compact) belong in lineage docs, not in the semantic field list. Language shapes whether people model a fact or a dump.
2. Share matching logic with service-device facts
If service facts resolve devices to contracts or site keys with a module, events use the same module. Divergent matching produces event counts that disagree with service pages. Shared logic is a platform rule, not a courtesy.
3. Prefer Direct Lake for volume
Wide history and high ingest rates punish Import refresh and capacity. Shape Delta for Direct Lake: sensible types, stable keys, avoid forcing SQL endpoint views in front when they reintroduce latency. Document when a hybrid Import dimension plus Direct Lake fact is intentional.
4. Captions, folders, descriptions ship with the fact
Event payload columns are cryptic. Human-facing captions and query folders are how the fact becomes usable. Include descriptions for units and enums. This is the same semantic handoff discipline as thinner gold tables, just more urgent at volume.
5. Bronze watermarks and cleans are part of the story
Shortcuts, watermark advances, and thin clean views explain freshness and late arrivals. Document them next to the gold fact contract so on-call knows whether a gap is ingest lag or a semantic filter. Do not leave watermark behavior as notebook tribal knowledge.
6. Test joins on known devices across facts
Acceptance includes: a device with service rows and event rows reconciles under the shared keys. Spot-check unmatched event devices the same way you spot-check unmatched service devices. Volume is not an excuse to skip grain tests; sample deliberately.
Failure modes I design against
Log mindset. No grain statement; every report invents one.
Import by habit. Refresh windows become the product constraint.
Unique event matching. Service page says 100 devices; events say 93.
Technical names in the model. Adoption dies; DAX forks multiply.
Undocumented watermarks. Freshness debates never end.
Skipping unmatched-event review. Silent drop of orphan devices.
Import mode as a documented exception
Sometimes a small event subset truly belongs in Import for a specialized model. Write that exception with row-count bounds and a revisit date. Exceptions without bounds become the new default. Direct Lake remains the posture for the main event fact that feeds estate-wide reporting. Clarity here prevents a future engineer from "simplifying" everything back to Import during an unrelated refactor.
Trade-offs
Direct Lake needs Delta hygiene and framing awareness. Import feels familiar and fails at scale. Shared enrichment modules require package discipline across notebooks. Caption work on wide event tables takes time; skipping it costs every consumer hour. Keeping bronze watermark docs current is operational overhead that prevents false incident escalations.
What I would put on an ADR
- Device events are gold facts with a written grain.
- Enrichment/matching shared with service-device facts.
- Direct Lake (or documented exception) for high-volume event facts.
- Captions, folders, and descriptions required at publish.
- Bronze watermark and clean-view behavior documented beside the fact.
- Cross-fact join tests on fixture devices including unmatched cases.
Primary references: Direct Lake overview, lakehouse medallion guidance, and Power BI modeling docs for large facts. Pair storage mode choice with the enrichment contract so volume does not excuse semantic drift.
Volume without a contract is just a faster mess
Direct Lake will not save a fact with unclear grain or divergent device matching. It will only help you serve the wrong joins at higher concurrency. Get the name, the shared enrichment, and the semantic handoff right first. Then choose Direct Lake so refresh and query capacity match the cardinality you already decided to trust.
Measure early: row growth per day, refresh duration under the chosen mode, and join correctness on a fixture pack. Numbers beat opinions when someone proposes falling back to Import "just for this sprint."
Capacity planning for Direct Lake still needs owners; mode choice without monitoring is just optimism.
Closing
Events are not logs with a report on top. They are facts that must join the rest of the estate.
Name them, enrich them like service, put them on Direct Lake, and hand off captions with the table. Volume then becomes a capacity story you can plan, not a weekly surprise refresh fire.
