vina-bim-shop is a staged, runnable local data engineering platform for a Shopee-inspired Vietnamese marketplace. It demonstrates how a modern e-commerce platform can support both realtime operational analytics and batch-reconciled historical analytics through a Lambda-style architecture.
The platform is runnable locally, but it should be operated through staged Docker Compose profiles. A broad full-stack startup attempt can cause Docker instability and API inspection failures on constrained machines, so this evidence pass treats staged startup as the supported workflow rather than relying on a monolithic all startup.
- System Architecture
- Tech Stack
- Introduction
- Problem Definition
- Coursework Status
- Platform Implementation Overview
- Repository Structure
- Prerequisites
- Installation & Setup
- Configuration
- Known Limits
- Data
The detailed architecture view shows the project as a staged local platform: synthetic source contracts feed Kafka and local batch snapshots, Spark and Flink process the two analytical paths, Trino and Pinot expose different serving surfaces, and Airflow, Great Expectations, DataHub, and evidence artifacts make the run auditable.
Related diagrams:
- Detailed PlantUML architecture
- Runnable Lambda architecture
- Schema design overview
- Physical Bronze/Silver/Gold model
- Gold layer ERD DBML
| Layer | Technology | Purpose |
|---|---|---|
| Source generation | Python, Faker, pandas, PyArrow | Generate Vietnamese marketplace snapshots, event streams, and evidence. |
| Ingestion | Kafka KRaft, Schema Registry, Kafka Connect, Kafka UI | Run event-log ingestion, JSON Schema registration, Bronze landing, and topic inspection. |
| Lakehouse storage | MinIO, Hive Metastore, Postgres, Apache Iceberg | Store Bronze replay logs, Silver/Gold tables, checkpoints, and catalog metadata. |
| Batch processing | Apache Spark, dbt-DuckDB | Build reconciled Silver/Gold tables and maintain a local parity oracle. |
| Streaming processing | Apache Flink | Produce event-time realtime metrics, alerts, and derived Kafka topics. |
| Serving | Trino, Apache Pinot, DuckDB | Serve canonical Gold SQL, provisional realtime OLAP, and portable local analysis marts. |
| Orchestration and quality | Apache Airflow, Great Expectations | Trigger control-plane workflows and publish validation evidence. |
| Governance | DataHub, OpenSearch | Emit metadata, tags, glossary terms, quality assertions, and lineage evidence. |
| Runtime | Docker Compose profiles, uv, pytest |
Run the local stack, Python tooling, and verification suite. |
vina-bim-shop models the data platform needs of a Vietnamese e-commerce marketplace with customers, sellers, products, promotions, orders, payments, shipments, inventory, and user behavior events. The project is coursework-oriented, but it is intentionally built around realistic data engineering concerns: source contracts, replayable event logs, medallion lakehouse storage, quality gates, serving-layer truth policies, and evidence packaging.
The main design choice is a Lambda architecture because the platform needs two complementary analysis streams:
- Realtime analysis for low-latency operational views such as traffic bursts, payment issues, live conversion, and alert candidates.
- Batch analysis for reconciled executive KPIs, dimensional modeling, financial formulas, and repeatable historical reporting.
Kafka, Flink, and Pinot provide the fresh but provisional speed path. MinIO, Spark, Iceberg, Hive Metastore, Trino, and DuckDB provide the reconciled batch path. Airflow, Great Expectations, and DataHub make the platform inspectable for coursework evidence.
The platform answers a practical e-commerce data problem: how can a marketplace support live operational monitoring while still preserving a reliable historical source of truth for business reporting?
The source design deliberately includes both event envelopes and table-state exports:
| Source stream | Answers | Used by |
|---|---|---|
| Kafka-shaped JSON event envelopes | What happened now? | Flink realtime metrics, Pinot dashboards, replay logs, behavior analysis. |
| Periodic table-state snapshots | What state is reliable at checkpoint? | Spark batch reconciliation, Gold dimensions/facts, Trino SQL, DuckDB evidence. |
The overlap is intentional. Orders, payments, shipments, and customer behavior can appear in both streams because events provide immediacy while snapshots provide checkpointed source-of-record state.
The generator also injects intentional data errors and contract drift so the platform can demonstrate realistic handling instead of only happy-path data:
- duplicate order-item payloads and duplicate commerce-event envelopes
- late-arriving commerce events
- missing values in fields such as
shipping_method,brand, anddevice_type - schema evolution where older slices miss newer optional fields
- malformed event and snapshot examples routed through
dead_letter_events,raw_bad_events, andraw_bad_snapshots - traffic burst and schema-version signals in operational events
Sections 01 and 02 are fulfilled for the mini-coursework phase.
For the original Sections 01/02 submission boundary, Spark, Flink, Apache Pinot, and Trino are architectural target contracts, while dbt-DuckDB is the runnable local implementation. The broader repository now also includes the staged runnable platform implementation for ingestion, lakehouse, batch, streaming, serving, orchestration, and governance.
| View | Meaning |
|---|---|
Runnable locally |
dbt-DuckDB, the final dataset package, and the documented evidence artifacts can be reproduced on one machine. |
Architectural contract |
The staged Kafka, Spark, Flink, Pinot, Trino, Airflow, and DataHub stack documents the full target platform through service-level deliverables and evidence. |
Mini-coursework artifacts and evidence:
uv run python scripts/qa/finalize_sections_01_02.py- Final raw dataset zip
- Final dataset manifest
data/gold/vina_bim_shop.duckdbdata/gold/vina_bim_shop_executive.duckdb- Data generator deliverable
- Schema design deliverable and Data Dictionary
- Physical Gold model PlantUML
- Physical Gold model PNG
- Generator quality report
- dbt build report
The root README is the entrypoint. The deeper service-by-service explanations live in deliverables and evidence folders, so the descriptions below are intentionally concise.
| Stage | Profile or local path | Services and assets | Deep dive |
|---|---|---|---|
| Data generation | local Python | Synthetic snapshots, Kafka-shaped JSONL, bad records, quality evidence. | 01 data generator |
| Ingestion | ingestion |
Kafka KRaft, Schema Registry, Kafka Connect, Kafka UI. | 03 Kafka ingestion |
| Lakehouse | lakehouse |
MinIO, Hive Metastore, Trino, shared Postgres. | 04 lakehouse |
| Batch | batch plus dbt-DuckDB |
Spark master, worker, history server, Iceberg Gold, dbt parity. | 05 Spark batch |
| Streaming | streaming |
Flink JobManager, TaskManager, job submitter, derived Kafka topics. | 06 Flink streaming |
| Serving | serving plus Trino/DuckDB |
Apache Pinot realtime OLAP, Trino canonical SQL, DuckDB local marts. | 07 Pinot serving |
| Local analytics | local DuckDB files plus dbt | dbt-DuckDB parity oracle and DuckDB Executive Mart export. | 10 DuckDB/dbt local analytics |
| Orchestration | orchestration |
Airflow webserver, scheduler, init, GX Data Docs. | 08 Airflow + GX |
| Governance | governance |
DataHub GMS, frontend, actions, OpenSearch, metadata recipes. | 09 DataHub governance, DataHub evidence |
Data generation is the source-system simulator for the platform. It produces offline Parquet snapshots, Kafka-shaped JSONL topics, bad-record fixtures, and committed evidence that feed the batch, streaming, and local analytics paths.
Key docs:
The ingestion profile turns the generated event contracts into a real Kafka surface. Kafka topics, Schema Registry subjects, Kafka UI inspection, and Kafka Connect Bronze landing together make the source-event path replayable.
Key docs:
The lakehouse profile is the shared storage and SQL foundation for the platform. MinIO, Hive Metastore, shared Postgres, Iceberg, and Trino give Spark and Kafka Connect a common Bronze-to-Gold boundary.
Key docs:
Spark owns the reconciled Bronze-to-Silver-to-Gold batch path over MinIO and Iceberg. Trino serves Spark Gold as the canonical SQL surface, while dbt-DuckDB parity and Executive Mart exports provide local verification and packaging.
Key docs:
- Spark batch deliverable
- Spark batch evidence
- dbt parity report
- DuckDB/dbt local analytics deliverable
Flink consumes Kafka source topics with event-time logic and emits narrow serving contracts for metrics, corrections, and operational alerts. The streaming layer is intentionally fresh and provisional, not the final financial truth.
Key docs:
Serving is split by freshness and truth role. Pinot answers low-latency questions over Flink-derived topics, while Trino answers canonical SQL over Spark-written Gold tables and can export portable DuckDB copies.
Schema design is part of the serving implementation, not just documentation. The committed model artifacts are:
- Physical Bronze/Silver/Gold model
- Rendered physical Gold model
- Gold layer ERD DBML
- Schema design deliverable and Data Dictionary
Key docs:
DuckDB provides the portable local analytics layer in two forms: the dbt-DuckDB parity oracle rebuilt from generated raw data, and the Executive Mart exported from Trino Gold. These files support regression checks, offline inspection, and coursework evidence without requiring the full distributed stack to stay online.
Key docs:
- DuckDB/dbt local analytics deliverable
- dbt parity report
- Schema design deliverable and Data Dictionary
Airflow owns the control-plane workflows for staged local runs: bootstrap, batch windows, reconciliation, evidence packaging, and DataHub ingestion. Great Expectations applies the project quality policy and publishes GX Data Docs, while Flink runtime remains outside Airflow supervision in v1.
Key docs:
DataHub catalogs the same assets the platform actually produces: Kafka topics, MinIO prefixes, Trino/Iceberg tables, dbt models, Spark and Flink lineage, and GX assertions. Governance proof relies on GMS and GraphQL verification plus committed evidence artifacts, not only the frontend UI.
Key docs:
coursework/
|-- architecture/ # Domain contracts, PlantUML, DBML, and Excalidraw architecture assets
|-- compose/ # Domain Compose files included by the root compatibility entry point
|-- configs/ # Generator, scenario, and pipeline configuration
|-- data/ # Gitignored local raw and Gold outputs, with folder-intent .gitignore files
|-- deliverables/ # Official coursework writeups and service-level documentation
|-- evidence/ # Committed evidence packages, screenshots, manifests, reports, and query outputs
|-- infra/ # Docker images, service config, Airflow DAGs, and dbt project assets
|-- scripts/ # CLI entrypoints for generation, bootstrap, smoke tests, evidence, reset, and exports
|-- src/vina_bim_shop/ # Python packages for generation, Kafka, lakehouse, Flink, Pinot, quality, and governance
|-- tests/ # Unit and integration checks for contracts, runtime helpers, evidence, and docs
|-- docker-compose.yml # Root Docker Compose compatibility entry point
|-- pyproject.toml # Python project metadata and dependencies
`-- uv.lock # Locked Python dependency graph
See the Script inventory for the official command surface and the debug/manual/destructive helpers queued for later cleanup.
Official evidence capture commands on main generate machine-verifiable artifacts only;
committed screenshot files remain historical review evidence and are not regenerated by
the official capture scripts.
| Tool | Recommended version | Purpose |
|---|---|---|
| Docker Desktop / Docker Compose | Docker 24+ | Run staged service profiles. |
| Python | 3.12+ | Run generator, scripts, tests, dbt-DuckDB path. |
uv |
Current stable | Install and run the Python environment. |
| Git | Current stable | Clone and inspect the coursework repo. |
| DBeaver or DuckDB CLI | Optional | Inspect local DuckDB evidence files. |
| PlantUML / DBML tooling | Optional | Preview architecture and ERD files locally. |
The repo includes .env.example with local defaults. Copy it to .env if you want explicit environment settings.
This project is designed to demonstrate the platform end to end through staged local execution, with each subsystem being verifiable. It is not reliably optimized for a single frictionless full-stack-at-once startup on a constrained machine.
The repository ships a root Makefile that wraps the most common commands under a make + verb convention. Run make help at any time to see the full catalog. Recipes are shell-agnostic: they delegate to uv run python and scripts/ctl.py, whose compose lifecycle expands documented profile bundles and prebuilds shared images where needed.
| Target | What it does |
|---|---|
make install |
uv sync to install Python dependencies. |
make generate |
Run the data generator with default options (SCALE=medium, MODE=full, SEED=42). Override with make generate SCALE=smoke MODE=streaming SEED=7. |
make build-dbt |
Run dbt build against the local DuckDB profile. |
make test |
Run pytest. |
make finalize |
Produce the final Section 01/02 evidence package. |
make reset |
Run scripts/qa/reset_all.py (destructive; forwards RESET_FLAGS=..., e.g. --dry-run, --force, --force --clean-local-data). |
make up-<profile> / make down-<profile> |
Start or stop one of the supported compose profiles; dependent profiles expand to the documented staged bundles. |
The PowerShell snippets below remain the authoritative command surface; the Makefile is a thin, discoverable shortcut on top of them.
uv syncUse this path when you want reproducible Section 01/02 evidence without starting the distributed services.
uv run python scripts/generate/run_generator.py --scale medium --mode full --clean --seed 42
uv run dbt build --project-dir infra/analytics/dbt --profiles-dir infra/analytics/dbt
uv run pytestStart each stage in order. All services share the same Docker network.
docker compose --profile ingestion up -d
docker compose --profile lakehouse up -d
docker compose --profile batch up -d
docker compose --profile ingestion --profile lakehouse --profile streaming up -d
docker compose --profile ingestion --profile lakehouse --profile streaming --profile serving up -d
docker compose --profile orchestration up -d
docker compose --profile ingestion --profile lakehouse --profile governance up -dThe all profile exists for best-effort demos, but staged startup is the supported evidence workflow.
uv run python scripts/qa/finalize_sections_01_02.pyRun this after Spark Gold tables are available through Trino.
uv run python scripts/spark/export_executive_mart.py --duckdb-path data/gold/vina_bim_shop_executive.duckdb --evidence-root evidence/05_spark_batch# Preview what would be destroyed
uv run python scripts/qa/reset_all.py --dry-run
# Full reset with confirmation prompt
uv run python scripts/qa/reset_all.py
# Full reset without prompt
uv run python scripts/qa/reset_all.py --force
# Reset including gitignored local generated data
uv run python scripts/qa/reset_all.py --clean-local-dataThe reset script is a documented needs-review destructive helper. It stops services, removes Docker volumes, and optionally cleans gitignored local data such as data/raw/, infra/analytics/dbt/target/, and evidence/runtime/. It does not remove committed source files or committed evidence.
Local defaults are documented in .env.example. Most scripts work with defaults, but the variables below define the main configuration groups.
| Group | Variables | Default intent |
|---|---|---|
| Runtime | VBS_ENV, VBS_RANDOM_SEED, VBS_TAXONOMY_PATH |
Local execution with deterministic seed 42 and committed taxonomy snapshot. |
| Kafka | VBS_KAFKA_BOOTSTRAP_SERVERS, VBS_SCHEMA_REGISTRY_URL, VBS_KAFKA_CONNECT_URL, VBS_KAFKA_UI_URL |
Local Kafka services on ports 9092, 8081, 8083, and 8084. |
| MinIO | VBS_MINIO_ENDPOINT, VBS_MINIO_INTERNAL_ENDPOINT, VBS_MINIO_ROOT_USER, VBS_MINIO_ROOT_PASSWORD, VBS_MINIO_REGION |
Local object storage with internal Docker and external localhost endpoints. |
| Lakehouse catalog | VBS_LAKEHOUSE_POSTGRES_*, VBS_HIVE_METASTORE_*, VBS_TRINO_* |
Shared Postgres, Hive Metastore, and Trino local serving defaults. |
| Spark | VBS_SPARK_MASTER_URL, VBS_SPARK_MASTER_UI_URL, VBS_SPARK_HISTORY_URL |
Spark master, UI, and history server endpoints. |
| Flink | VBS_FLINK_UI_URL, VBS_FLINK_JOBMANAGER_URL, VBS_FLINK_MAX_RUNTIME_MINUTES, VBS_FLINK_DISABLE_AUTO_STOP |
Flink UI/runtime settings and local safety timeout. |
| Pinot | VBS_PINOT_CONTROLLER_URL, VBS_PINOT_BROKER_URL |
Pinot controller and broker endpoints for bootstrap and query scripts. |
| Orchestration | VBS_AIRFLOW_UI_URL, VBS_AIRFLOW_ADMIN_USERNAME, VBS_AIRFLOW_ADMIN_PASSWORD, VBS_GX_DOCS_URL |
Airflow and GX Data Docs local URLs. |
| Data roots | VBS_RAW_ROOT, VBS_BRONZE_*, VBS_*_BUCKET, VBS_DUCKDB_PATH |
Local raw output paths, MinIO bucket names, and DuckDB defaults. |
- The platform is runnable locally, but only if you follow staged profile startup and accept some UI/runtime constraints.
- A broad full-stack startup attempt is known to cause Docker instability and API inspection failures on constrained machines; staged profiles are the normal workflow for this evidence pass.
- The full
allprofile is high-resource and best-effort, not the primary acceptance path. - dbt-DuckDB is a local compatibility path; Spark/Iceberg/Trino is canonical for reconciled truth in the full platform evidence.
- The DuckDB Executive Mart is a Trino Gold snapshot export; regenerate it after each Spark Gold refresh.
- Pinot is fresh and provisional; Trino-served Gold tables are the official KPI source.
- Airflow orchestrates batch and control-plane work; it does not supervise long-running Flink jobs in v1.
- DataHub requires separate ingestion recipe runs after platform services are healthy.
- Local DataHub UI capture did not render the fuller graph view, even though GMS/GraphQL evidence supports metadata, lineage, tag, and assertion emission.
- Observability hardening, production security, CI/CD, and Section 03 drift scenarios are outside the current evidence boundary.
Generated local data lives in data/ and is intentionally gitignored except for nested .gitignore files that preserve folder intent.
| Path | Role | Git behavior |
|---|---|---|
data/raw/ |
Generator-managed raw Parquet snapshots, Kafka-topic JSONL files, and bad-record examples. | Gitignored local output. |
data/gold/vina_bim_shop.duckdb |
dbt-DuckDB parity oracle rebuilt from local raw inputs. | Gitignored local output. |
data/gold/vina_bim_shop_executive.duckdb |
DuckDB Executive Mart exported from Trino Gold. | Gitignored local output. |
evidence/final_dataset/vina_bim_shop_medium_raw.zip |
Submitted medium raw dataset package. | Committed evidence. |
evidence/final_dataset/final_dataset_manifest.json |
Manifest for the final dataset package. | Committed evidence. |
evidence/final_integration/ |
Health JSON, historical screenshots, row counts, query outputs, reconciliation, and lineage evidence. | Committed evidence. |
Runtime service profiles:
| Profile | Services |
|---|---|
ingestion |
Kafka KRaft, Schema Registry, Kafka Connect, Kafka UI |
lakehouse |
MinIO, Hive Metastore, Trino, shared Postgres |
batch |
Spark master, worker, history server |
streaming |
Flink JobManager, TaskManager, job submitter |
serving |
Pinot Zookeeper, controller, broker, server |
orchestration |
Airflow webserver, scheduler, init, GX Data Docs |
governance |
DataHub GMS, frontend, actions, OpenSearch |
all |
Best-effort startup of all profiles; staged startup remains the supported workflow |








