---
module: platform.data-architecture
title: Data Architecture — Executors, Kafka, Record Stores
tab: Overview
order: 10
audience: dev
---

> **DRAFT · internal.** Written 2026-07-01 to stop the most expensive recurring mistake:
> assuming "where data lives." Verify against code before relying on any specific path.

## The core pattern

HiveIQ services are **single-responsibility and decoupled via Kafka producer/consumer.** The rule
that matters:

1. An **executor service does one job and stays clean** — it does *not* keep long-term history.
2. On **completion or failure**, the executor **produces a Kafka event** with the result.
3. The **owning / record-keeping service consumes** the event and **stores the record**.
4. ⇒ **Query history from the record store; send commands to the executor.**

## Worked example — fleet tasks (the 2026-06-30 bug)

- **Fleet = executor.** It runs download/install/etc. It keeps **no queryable history** — the
  standalone `/fleet/tasks` and `/fleet/tasks/paginated` endpoints are **empty by design** once a
  job finishes. An empty executor endpoint is **NOT** a bug or a stale instance.
- On done/fail, fleet **publishes to Kafka**.
- **Devices consumes and stores** the device-scoped task record, served at
  `GET /api/atms/{id}/fleet-tasks` (`DeviceFleetTask`). **This is the source of truth for a
  device's task history.** (Task History and the SW Deploy tab must read *this*, not the fleet
  executor.)
- The **install action is the exception** — a *command*, sent directly to the fleet executor
  (`fleetAPI.createTask`).

## Kafka vs direct API — judgment, not dogma

- **Direct API** when you need **live/real-time state** (in-flight progress) or a **synchronous
  command** (create a task), or immediate consistency.
- **Kafka** for **durable history, cross-service mirrors, eventual propagation.**
- One feature can legitimately read history from the record store *and* hit a direct API for live
  progress. Match the mechanism to the need.

## Do NOT
- Do not say a service's data "lives in" whichever service happens to *serve* it. Ownership
  (execution) and record-keeping (storage) can be different services.
- Do not treat an empty executor endpoint as missing data.
- When unsure where data lives: check the code / ask, don't assert.
