Correct TallyFlow architecture based on source code review in ~/src/

- Core is central and owns MongoDB (not Assets)
- Core: Go backend + Vue.js frontend (no Node.js)
- Verified against tallyflow-web, photomicroservice, tallyflow-iot, tallyflow_mobile, tallyflow-print
- Updated diagram, legend, cards, and description for accuracy
This commit is contained in:
TNB
2026-07-07 09:15:19 -04:00
parent 31d267a211
commit 0bbe00b75e
2 changed files with 108 additions and 106 deletions
+87 -92
View File
@@ -59,11 +59,11 @@
<div class="pulse-dot"></div> <div class="pulse-dot"></div>
<h1>TallyFlow Architecture</h1> <h1>TallyFlow Architecture</h1>
</div> </div>
<p class="subtitle">Manufacturing Asset Tracking &amp; RFID Workflow Platform</p> <p class="subtitle">Manufacturing Asset Tracking &amp; RFID Workflow Platform — Source-verified</p>
</div> </div>
<div class="diagram-container"> <div class="diagram-container">
<svg viewBox="0 0 1000 620"> <svg viewBox="0 0 1000 640">
<defs> <defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto"> <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#64748b" /> <polygon points="0 0, 10 3.5, 0 7" fill="#64748b" />
@@ -76,104 +76,99 @@
<!-- Background Grid --> <!-- Background Grid -->
<rect width="100%" height="100%" fill="url(#grid)" /> <rect width="100%" height="100%" fill="url(#grid)" />
<!-- Central Core Region --> <!-- Central Core Region (owns DB) -->
<rect x="180" y="60" width="640" height="480" rx="16" fill="rgba(6, 78, 59, 0.08)" stroke="#34d399" stroke-width="1.5" stroke-dasharray="6,3"/> <rect x="160" y="50" width="680" height="510" rx="16" fill="rgba(6, 78, 59, 0.08)" stroke="#34d399" stroke-width="2" stroke-dasharray="6,3"/>
<text x="200" y="85" fill="#34d399" font-size="11" font-weight="700">TallyFlow Core (Backend API + Web UI)</text> <text x="180" y="78" fill="#34d399" font-size="12" font-weight="700">TallyFlow Core (Central Hub)</text>
<!-- Core Component --> <!-- Core Backend (Go) -->
<rect x="220" y="120" width="160" height="70" rx="8" fill="rgba(6, 78, 59, 0.45)" stroke="#34d399" stroke-width="2"/> <rect x="200" y="110" width="180" height="80" rx="8" fill="rgba(6, 78, 59, 0.5)" stroke="#34d399" stroke-width="2"/>
<text x="300" y="150" fill="white" font-size="13" font-weight="700" text-anchor="middle">Core</text> <text x="290" y="140" fill="white" font-size="14" font-weight="700" text-anchor="middle">Core Backend</text>
<text x="300" y="168" fill="#94a3b8" font-size="9" text-anchor="middle">FastAPI / Node.js</text> <text x="290" y="158" fill="#94a3b8" font-size="10" text-anchor="middle">Go (Echo / gRPC)</text>
<text x="300" y="182" fill="#34d399" font-size="8" text-anchor="middle">REST + WebSocket</text> <text x="290" y="174" fill="#34d399" font-size="9" text-anchor="middle">REST + WebSocket API</text>
<!-- Assets (Mobile) Component - corrected --> <!-- Core Frontend (Vue.js) -->
<rect x="420" y="120" width="160" height="70" rx="8" fill="rgba(8, 51, 68, 0.45)" stroke="#22d3ee" stroke-width="2"/> <rect x="420" y="110" width="180" height="80" rx="8" fill="rgba(8, 51, 68, 0.5)" stroke="#22d3ee" stroke-width="2"/>
<text x="500" y="148" fill="white" font-size="13" font-weight="700" text-anchor="middle">Assets (Mobile)</text> <text x="510" y="140" fill="white" font-size="14" font-weight="700" text-anchor="middle">Core Web UI</text>
<text x="500" y="165" fill="#94a3b8" font-size="9" text-anchor="middle">Lightweight Inventory</text> <text x="510" y="158" fill="#94a3b8" font-size="10" text-anchor="middle">Vue.js 2 + Webpack</text>
<text x="500" y="180" fill="#22d3ee" font-size="8" text-anchor="middle">Seek &amp; Find • Enrollment</text> <text x="510" y="174" fill="#22d3ee" font-size="9" text-anchor="middle">Asset management, reporting</text>
<!-- Print Component - corrected to Zebra ZPL --> <!-- MongoDB (owned by Core) -->
<rect x="620" y="120" width="160" height="70" rx="8" fill="rgba(120, 53, 15, 0.4)" stroke="#fbbf24" stroke-width="2"/> <rect x="640" y="110" width="170" height="80" rx="8" fill="rgba(76, 29, 149, 0.45)" stroke="#a78bfa" stroke-width="2"/>
<text x="700" y="148" fill="white" font-size="13" font-weight="700" text-anchor="middle">Print</text> <text x="725" y="140" fill="white" font-size="14" font-weight="700" text-anchor="middle">MongoDB</text>
<text x="700" y="165" fill="#94a3b8" font-size="9" text-anchor="middle">Zebra (ZPL compatible)</text> <text x="725" y="158" fill="#94a3b8" font-size="10" text-anchor="middle">Asset registry</text>
<text x="700" y="180" fill="#fbbf24" font-size="8" text-anchor="middle">Label + RFID Encoding</text> <text x="725" y="174" fill="#a78bfa" font-size="9" text-anchor="middle">Tags • Photos • Zones • Events</text>
<!-- Capture (PhotoTag) - corrected --> <!-- Assets (Mobile) - Flutter -->
<rect x="80" y="280" width="140" height="90" rx="8" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="2"/> <rect x="80" y="290" width="150" height="95" rx="8" fill="rgba(8, 51, 68, 0.45)" stroke="#22d3ee" stroke-width="2"/>
<text x="150" y="308" fill="white" font-size="13" font-weight="700" text-anchor="middle">Capture</text> <text x="155" y="318" fill="white" font-size="13" font-weight="700" text-anchor="middle">Assets (Mobile)</text>
<text x="150" y="326" fill="#94a3b8" font-size="9" text-anchor="middle">(PhotoTag)</text> <text x="155" y="336" fill="#94a3b8" font-size="9" text-anchor="middle">Flutter</text>
<text x="150" y="342" fill="#fb7185" font-size="8" text-anchor="middle">Photo + Barcode assoc</text> <text x="155" y="352" fill="#22d3ee" font-size="8" text-anchor="middle">Seek &amp; Find • Inventory</text>
<text x="150" y="356" fill="#94a3b8" font-size="8" text-anchor="middle">QC Documentation</text> <text x="155" y="366" fill="#94a3b8" font-size="8" text-anchor="middle">Enrollment • Zones</text>
<!-- IoT / Fixed Readers + Sensors --> <!-- Capture (PhotoTag) - Go microservice -->
<rect x="780" y="280" width="140" height="90" rx="8" fill="rgba(251, 146, 60, 0.35)" stroke="#fb923c" stroke-width="2"/> <rect x="780" y="290" width="150" height="95" rx="8" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="2"/>
<text x="850" y="308" fill="white" font-size="13" font-weight="700" text-anchor="middle">IoT</text> <text x="855" y="318" fill="white" font-size="13" font-weight="700" text-anchor="middle">Capture</text>
<text x="850" y="326" fill="#94a3b8" font-size="9" text-anchor="middle">Fixed Readers + Sensors</text> <text x="855" y="336" fill="#94a3b8" font-size="9" text-anchor="middle">(PhotoTag)</text>
<text x="850" y="342" fill="#fb923c" font-size="8" text-anchor="middle">Impinj / Zebra</text> <text x="855" y="352" fill="#fb7185" font-size="8" text-anchor="middle">Go + Mongo</text>
<text x="850" y="356" fill="#94a3b8" font-size="8" text-anchor="middle">MQTT / HTTP / BLE</text> <text x="855" y="366" fill="#94a3b8" font-size="8" text-anchor="middle">Photo + Barcode QC</text>
<!-- Database Layer - corrected to MongoDB --> <!-- Print (Zebra ZPL) -->
<rect x="320" y="420" width="360" height="70" rx="8" fill="rgba(76, 29, 149, 0.35)" stroke="#a78bfa" stroke-width="1.5"/> <rect x="280" y="420" width="160" height="80" rx="8" fill="rgba(120, 53, 15, 0.4)" stroke="#fbbf24" stroke-width="2"/>
<text x="500" y="450" fill="white" font-size="12" font-weight="700" text-anchor="middle">MongoDB</text> <text x="360" y="448" fill="white" font-size="13" font-weight="700" text-anchor="middle">Print</text>
<text x="500" y="468" fill="#94a3b8" font-size="9" text-anchor="middle">Asset registry • Event store • Audit</text> <text x="360" y="466" fill="#94a3b8" font-size="9" text-anchor="middle">Flutter + Rust</text>
<text x="500" y="482" fill="#a78bfa" font-size="8" text-anchor="middle">Tags, photos, zones, history</text> <text x="360" y="482" fill="#fbbf24" font-size="8" text-anchor="middle">Zebra ZPL + RFID</text>
<!-- External Integrations / Excel --> <!-- IoT (Rust) -->
<rect x="80" y="420" width="120" height="60" rx="6" fill="rgba(30, 41, 59, 0.5)" stroke="#94a3b8" stroke-width="1.5"/> <rect x="560" y="420" width="160" height="80" rx="8" fill="rgba(251, 146, 60, 0.35)" stroke="#fb923c" stroke-width="2"/>
<text x="140" y="445" fill="white" font-size="11" font-weight="600" text-anchor="middle">Excel Import</text> <text x="640" y="448" fill="white" font-size="13" font-weight="700" text-anchor="middle">IoT</text>
<text x="140" y="462" fill="#94a3b8" font-size="8" text-anchor="middle">Bulk asset load</text> <text x="640" y="466" fill="#94a3b8" font-size="9" text-anchor="middle">Rust (tallyflow-iot)</text>
<text x="640" y="482" fill="#fb923c" font-size="8" text-anchor="middle">Fixed readers • Sensors</text>
<!-- Arrows / Flows --> <!-- Arrows -->
<!-- Capture → Core --> <!-- Assets (Mobile) → Core -->
<line x1="222" y1="325" x2="280" y2="190" stroke="#fb7185" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="232" y1="335" x2="280" y2="190" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="235" y="265" fill="#fb7185" font-size="9" font-weight="600">Photo + Data</text> <text x="240" y="270" fill="#22d3ee" font-size="9" font-weight="600">Sync / Enroll</text>
<!-- Core ↔ Assets (Mobile) --> <!-- Core Backend ↔ MongoDB -->
<line x1="382" y1="155" x2="418" y2="155" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="382" y1="150" x2="638" y2="150" stroke="#a78bfa" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="400" y="148" fill="#22d3ee" font-size="8">Sync</text> <text x="510" y="143" fill="#a78bfa" font-size="9">Owns &amp; Persists</text>
<!-- Core → Print --> <!-- Core → Print -->
<line x1="382" y1="155" x2="618" y2="155" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="360" y1="192" x2="360" y2="418" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="500" y="148" fill="#fbbf24" font-size="8">Print Jobs</text> <text x="375" y="310" fill="#fbbf24" font-size="9">Print Jobs</text>
<!-- IoT → Core --> <!-- IoT → Core -->
<line x1="778" y1="325" x2="720" y2="190" stroke="#fb923c" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="640" y1="418" x2="640" y2="192" stroke="#fb923c" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="765" y="265" fill="#fb923c" font-size="9" font-weight="600">Tag Events</text> <text x="655" y="310" fill="#fb923c" font-size="9">Tag Events</text>
<!-- Core ↔ MongoDB --> <!-- Capture → Core -->
<line x1="500" y1="192" x2="500" y2="418" stroke="#a78bfa" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="778" y1="335" x2="720" y2="190" stroke="#fb7185" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="515" y="310" fill="#a78bfa" font-size="9">Persist</text> <text x="765" y="270" fill="#fb7185" font-size="9" font-weight="600">Photos + Metadata</text>
<!-- Print ↔ MongoDB (via Core) -->
<line x1="700" y1="192" x2="680" y2="418" stroke="#fbbf24" stroke-width="1.5" stroke-dasharray="4,2" marker-end="url(#arrowhead)"/>
<!-- Capture ↔ Excel example flow -->
<line x1="140" y1="420" x2="140" y2="370" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="3,2" marker-end="url(#arrowhead)"/>
<!-- Legend --> <!-- Legend -->
<text x="80" y="560" fill="white" font-size="10" font-weight="700">Legend</text> <text x="80" y="570" fill="white" font-size="10" font-weight="700">Legend</text>
<rect x="80" y="572" width="18" height="11" rx="2" fill="rgba(6, 78, 59, 0.45)" stroke="#34d399" stroke-width="1"/> <rect x="80" y="582" width="18" height="11" rx="2" fill="rgba(6, 78, 59, 0.5)" stroke="#34d399" stroke-width="1"/>
<text x="105" y="581" fill="#94a3b8" font-size="8">Core Backend</text> <text x="105" y="591" fill="#94a3b8" font-size="8">Core (Go + Vue.js)</text>
<rect x="200" y="572" width="18" height="11" rx="2" fill="rgba(8, 51, 68, 0.45)" stroke="#22d3ee" stroke-width="1"/> <rect x="230" y="582" width="18" height="11" rx="2" fill="rgba(8, 51, 68, 0.45)" stroke="#22d3ee" stroke-width="1"/>
<text x="225" y="581" fill="#94a3b8" font-size="8">Assets (Mobile)</text> <text x="255" y="591" fill="#94a3b8" font-size="8">Assets (Flutter)</text>
<rect x="330" y="572" width="18" height="11" rx="2" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="1"/> <rect x="355" y="582" width="18" height="11" rx="2" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="1"/>
<text x="355" y="581" fill="#94a3b8" font-size="8">Capture (PhotoTag)</text> <text x="380" y="591" fill="#94a3b8" font-size="8">Capture (Go)</text>
<rect x="485" y="572" width="18" height="11" rx="2" fill="rgba(120, 53, 15, 0.4)" stroke="#fbbf24" stroke-width="1"/> <rect x="485" y="582" width="18" height="11" rx="2" fill="rgba(120, 53, 15, 0.4)" stroke="#fbbf24" stroke-width="1"/>
<text x="510" y="581" fill="#94a3b8" font-size="8">Print (Zebra ZPL)</text> <text x="510" y="591" fill="#94a3b8" font-size="8">Print (Flutter/Rust)</text>
<rect x="610" y="572" width="18" height="11" rx="2" fill="rgba(251, 146, 60, 0.35)" stroke="#fb923c" stroke-width="1"/> <rect x="615" y="582" width="18" height="11" rx="2" fill="rgba(251, 146, 60, 0.35)" stroke="#fb923c" stroke-width="1"/>
<text x="635" y="581" fill="#94a3b8" font-size="8">IoT / Edge</text> <text x="640" y="591" fill="#94a3b8" font-size="8">IoT (Rust)</text>
<rect x="720" y="572" width="18" height="11" rx="2" fill="rgba(76, 29, 149, 0.35)" stroke="#a78bfa" stroke-width="1"/> <rect x="720" y="582" width="18" height="11" rx="2" fill="rgba(76, 29, 149, 0.45)" stroke="#a78bfa" stroke-width="1"/>
<text x="745" y="581" fill="#94a3b8" font-size="8">MongoDB</text> <text x="745" y="591" fill="#94a3b8" font-size="8">MongoDB (Core-owned)</text>
<line x1="850" y1="577" x2="868" y2="577" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/> <line x1="870" y1="587" x2="888" y2="587" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<text x="875" y="581" fill="#94a3b8" font-size="8">Data Flow</text> <text x="895" y="591" fill="#94a3b8" font-size="8">API Flow</text>
</svg> </svg>
</div> </div>
@@ -181,13 +176,13 @@
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<div class="card-dot emerald"></div> <div class="card-dot emerald"></div>
<h3>Core</h3> <h3>Core (Central)</h3>
</div> </div>
<ul> <ul>
<li>Central orchestration &amp; API hub</li> <li>Go backend (API, business logic)</li>
<li>Web UI for asset management</li> <li>Vue.js 2 web frontend</li>
<li>Zone, category &amp; reporting config</li> <li>Owns MongoDB (assets, tags, photos, zones)</li>
<li>• Integration point for all modules</li> <li>• Integration hub for all modules</li>
</ul> </ul>
</div> </div>
<div class="card"> <div class="card">
@@ -196,10 +191,10 @@
<h3>Assets (Mobile)</h3> <h3>Assets (Mobile)</h3>
</div> </div>
<ul> <ul>
<li>Lightweight inventory &amp; tracking</li> <li>Flutter mobile app</li>
<li>Geiger-style seek &amp; find</li> <li>Lightweight inventory &amp; seek/find</li>
<li>Fast enrollment &amp; editing</li> <li>Enrollment, zone moves</li>
<li>Zone moves &amp; shipping workflows</li> <li>Syncs with Core API</li>
</ul> </ul>
</div> </div>
<div class="card"> <div class="card">
@@ -208,10 +203,10 @@
<h3>Capture (PhotoTag)</h3> <h3>Capture (PhotoTag)</h3>
</div> </div>
<ul> <ul>
<li>• Go microservice + Mongo</li>
<li>• Photo + barcode association</li> <li>• Photo + barcode association</li>
<li>• QC &amp; visual documentation</li> <li>• QC &amp; visual documentation</li>
<li>Work order / serial tagging</li> <li>Feeds Core via API</li>
<li>• Searchable photo records</li>
</ul> </ul>
</div> </div>
<div class="card"> <div class="card">
@@ -220,16 +215,16 @@
<h3>Print + IoT</h3> <h3>Print + IoT</h3>
</div> </div>
<ul> <ul>
<li>Zebra ZPL label printing &amp; encoding</li> <li>Print: Flutter + Rust (Zebra ZPL)</li>
<li>Fixed RFID readers (Impinj/Zebra)</li> <li>IoT: Rust (fixed readers, sensors)</li>
<li>IoT sensors (location, temp, shock)</li> <li>MQTT / HTTP / BLE ingestion</li>
<li>Excel bulk import support</li> <li>All route through Core</li>
</ul> </ul>
</div> </div>
</div> </div>
<p class="footer"> <p class="footer">
TallyFlow • Reducing manufacturing waste through intelligent RFID tracking • 2026 TallyFlow • Architecture verified against source in ~/src (tallyflow-web, photomicroservice, tallyflow-iot, tallyflow_mobile, tallyflow-print) • 2026
</p> </p>
</div> </div>
</body> </body>
+21 -14
View File
@@ -2,26 +2,33 @@
**Diagram:** [[TallyFlow-Architecture-Diagram.html]] **Diagram:** [[TallyFlow-Architecture-Diagram.html]]
## Overview ## Overview (Source-Verified)
TallyFlow is a modular asset tracking platform centered on **TallyFlow Core**, the central web backend and integration hub. All other components synchronize with Core for asset registration, configuration, reporting, and data consistency. TallyFlow's architecture is centered on **TallyFlow Core**, which serves as the single integration hub and owns the primary data store.
**TallyFlow Assets (Mobile)** is the primary field application for lightweight inventory management, geiger-counter-style seek & find, fast enrollment, zone movements, and shipping workflows. **Core** consists of:
- Go backend (API layer, business logic, using Echo/gRPC patterns)
- Vue.js 2 web frontend (tallyflow-web) for asset management, reporting, and configuration
- MongoDB database (owned by Core) storing the asset registry, tags, photo metadata, zones, events, and audit history
**TallyFlow Capture** (rebranded from PhotoTag) enables photo documentation by associating images with barcodes, work orders, serial numbers, and other metadata for quality control and visual record-keeping across incoming, in-process, and outgoing inspections. All other components communicate exclusively through Core's REST + WebSocket APIs.
**TallyFlow Print** handles on-demand Zebra ZPL-compatible label printing and RFID encoding, with optional automatic registration back into Core. **TallyFlow Assets (Mobile)** is a Flutter application focused on field operations: lightweight inventory, geiger-counter-style seek & find, fast enrollment, zone movements, and shipping workflows. It syncs with Core.
**IoT** provides real-time location, environmental sensor data (temperature, humidity, shock), and fixed RFID reader integration (Impinj, Zebra) via MQTT/HTTP/BLE, feeding live events into Core. **TallyFlow Capture** (PhotoTag) is implemented as a Go microservice (photomicroservice) that handles photo + barcode association for quality control and visual documentation. It stores photo metadata in its own MongoDB collections but registers assets and events back to Core.
Data is persisted in **MongoDB**, which stores the asset registry, tag events, photo metadata, zones, and full audit history. Bulk onboarding is supported via Excel import. All modules communicate through Cores REST + WebSocket APIs, enabling unified visibility and waste reduction across manufacturing operations. **TallyFlow Print** is a Flutter + Rust application that generates Zebra ZPL-compatible labels and RFID encodings, with optional auto-registration into Core.
**IoT** is a Rust-based service (tallyflow-iot) that ingests data from fixed RFID readers (Impinj, Zebra) and environmental sensors, pushing real-time tag events and sensor data to Core.
This structure was verified by inspecting the repositories in `~/src/` (tallyflow-web, photomicroservice, tallyflow-iot, tallyflow_mobile, tallyflow-print, etc.).
## Key Data Flows ## Key Data Flows
- **Assets (Mobile) ↔ Core**: Sync inventory updates, enrollment, and seek/find data - **Assets (Mobile) ↔ Core**: Enrollment, inventory updates, seek/find sync via API
- **Capture Core**: Photo + barcode metadata and QC records - **Capture Core**: Photo metadata and QC records
- **Print → Core**: Print job submission + optional auto-registration - **Print → Core**: Print jobs and optional asset registration
- **IoT → Core**: Real-time tag events and sensor data - **IoT → Core**: Real-time tag/sensor events
- **Core ↔ MongoDB**: Persistent storage of all asset, tag, photo, and event data - **Core ↔ MongoDB**: Core owns and directly manages all persistent data (assets, photos, zones, history)
- **Excel → Core**: Bulk asset and tag ingestion - **Core Web UI (Vue.js)**: Operator interface for the full system
This architecture allows flexible deployment — from mobile-only inventory tracking to full IoT + automated capture environments — while keeping Core as the single source of truth. Core is the only component that directly owns the database. All other modules are clients or data producers that route through Core.