Refine TallyFlow architecture to exactly 5 components

- MongoDB nested inside Core (no longer 6th component)
- IoT → Core connection is dashed (optional)
- Print includes Print UI + Print API note
- Capture shown as native app (not Go/Vue)
- Core Web UI and Print UI as differentiated sub-boxes
- Strict focus on the 5 components: Core, Assets, Print, Capture, IoT
This commit is contained in:
TNB
2026-07-07 09:22:29 -04:00
parent 0bbe00b75e
commit 4497fabf22
2 changed files with 136 additions and 110 deletions
+105 -89
View File
@@ -59,15 +59,18 @@
<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 — Source-verified</p> <p class="subtitle">5 Core Components • Source-verified from ~/src</p>
</div> </div>
<div class="diagram-container"> <div class="diagram-container">
<svg viewBox="0 0 1000 640"> <svg viewBox="0 0 1000 620">
<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" />
</marker> </marker>
<marker id="arrowhead-dashed" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#fb923c" />
</marker>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse"> <pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/> <path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/>
</pattern> </pattern>
@@ -76,99 +79,100 @@
<!-- Background Grid --> <!-- Background Grid -->
<rect width="100%" height="100%" fill="url(#grid)" /> <rect width="100%" height="100%" fill="url(#grid)" />
<!-- Central Core Region (owns DB) --> <!-- ========== CORE (Central, owns DB) ========== -->
<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"/> <rect x="200" y="60" width="600" height="280" rx="14" fill="rgba(6, 78, 59, 0.1)" stroke="#34d399" stroke-width="2.5"/>
<text x="180" y="78" fill="#34d399" font-size="12" font-weight="700">TallyFlow Core (Central Hub)</text> <text x="220" y="85" fill="#34d399" font-size="13" font-weight="700">TallyFlow Core (Central Hub)</text>
<!-- Core Backend (Go) --> <!-- Core Backend (Go) -->
<rect x="200" y="110" width="180" height="80" rx="8" fill="rgba(6, 78, 59, 0.5)" stroke="#34d399" stroke-width="2"/> <rect x="230" y="105" width="200" height="70" rx="8" fill="rgba(6, 78, 59, 0.55)" stroke="#34d399" stroke-width="2"/>
<text x="290" y="140" fill="white" font-size="14" font-weight="700" text-anchor="middle">Core Backend</text> <text x="330" y="132" fill="white" font-size="13" font-weight="700" text-anchor="middle">Core Backend</text>
<text x="290" y="158" fill="#94a3b8" font-size="10" text-anchor="middle">Go (Echo / gRPC)</text> <text x="330" y="150" fill="#94a3b8" font-size="10" text-anchor="middle">Go (Echo / gRPC)</text>
<text x="290" y="174" fill="#34d399" font-size="9" text-anchor="middle">REST + WebSocket API</text> <text x="330" y="166" fill="#34d399" font-size="9" text-anchor="middle">REST + WebSocket API</text>
<!-- Core Frontend (Vue.js) --> <!-- Core Web UI (Vue.js) - sub-box -->
<rect x="420" y="110" width="180" height="80" rx="8" fill="rgba(8, 51, 68, 0.5)" stroke="#22d3ee" stroke-width="2"/> <rect x="460" y="105" width="200" height="70" rx="8" fill="rgba(8, 51, 68, 0.55)" stroke="#22d3ee" stroke-width="2"/>
<text x="510" y="140" fill="white" font-size="14" font-weight="700" text-anchor="middle">Core Web UI</text> <text x="560" y="132" fill="white" font-size="13" font-weight="700" text-anchor="middle">Core Web UI</text>
<text x="510" y="158" fill="#94a3b8" font-size="10" text-anchor="middle">Vue.js 2 + Webpack</text> <text x="560" y="150" fill="#94a3b8" font-size="10" text-anchor="middle">Vue.js 2</text>
<text x="510" y="174" fill="#22d3ee" font-size="9" text-anchor="middle">Asset management, reporting</text> <text x="560" y="166" fill="#22d3ee" font-size="9" text-anchor="middle">Asset mgmt, reporting, config</text>
<!-- MongoDB (owned by Core) --> <!-- MongoDB (nested inside Core) -->
<rect x="640" y="110" width="170" height="80" rx="8" fill="rgba(76, 29, 149, 0.45)" stroke="#a78bfa" stroke-width="2"/> <rect x="680" y="105" width="100" height="70" rx="6" fill="rgba(76, 29, 149, 0.6)" stroke="#a78bfa" stroke-width="1.5"/>
<text x="725" y="140" fill="white" font-size="14" font-weight="700" text-anchor="middle">MongoDB</text> <text x="730" y="132" fill="white" font-size="11" font-weight="700" text-anchor="middle">MongoDB</text>
<text x="725" y="158" fill="#94a3b8" font-size="10" text-anchor="middle">Asset registry</text> <text x="730" y="150" fill="#c4b5fd" font-size="8" text-anchor="middle">Assets • Tags</text>
<text x="725" y="174" fill="#a78bfa" font-size="9" text-anchor="middle">Tags • Photos • Zones • Events</text> <text x="730" y="164" fill="#c4b5fd" font-size="8" text-anchor="middle">Photos • Zones</text>
<!-- Assets (Mobile) - Flutter --> <!-- Core API label -->
<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="500" y="200" fill="#34d399" font-size="9" text-anchor="middle" font-weight="600">Core API Layer (single integration point for all 5 components)</text>
<text x="155" y="318" fill="white" font-size="13" font-weight="700" text-anchor="middle">Assets (Mobile)</text>
<text x="155" y="336" fill="#94a3b8" font-size="9" text-anchor="middle">Flutter</text>
<text x="155" y="352" fill="#22d3ee" font-size="8" text-anchor="middle">Seek &amp; Find • Inventory</text>
<text x="155" y="366" fill="#94a3b8" font-size="8" text-anchor="middle">Enrollment • Zones</text>
<!-- Capture (PhotoTag) - Go microservice --> <!-- ========== ASSETS (Mobile) ========== -->
<rect x="780" y="290" width="150" height="95" rx="8" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="2"/> <rect x="60" y="380" width="170" height="100" rx="10" fill="rgba(8, 51, 68, 0.5)" stroke="#22d3ee" stroke-width="2"/>
<text x="855" y="318" fill="white" font-size="13" font-weight="700" text-anchor="middle">Capture</text> <text x="145" y="408" fill="white" font-size="13" font-weight="700" text-anchor="middle">Assets</text>
<text x="855" y="336" fill="#94a3b8" font-size="9" text-anchor="middle">(PhotoTag)</text> <text x="145" y="426" fill="#94a3b8" font-size="10" text-anchor="middle">(Mobile)</text>
<text x="855" y="352" fill="#fb7185" font-size="8" text-anchor="middle">Go + Mongo</text> <text x="145" y="444" fill="#22d3ee" font-size="9" text-anchor="middle">Flutter</text>
<text x="855" y="366" fill="#94a3b8" font-size="8" text-anchor="middle">Photo + Barcode QC</text> <text x="145" y="460" fill="#94a3b8" font-size="8" text-anchor="middle">Seek &amp; Find • Inventory • Enrollment</text>
<!-- Print (Zebra ZPL) --> <!-- ========== PRINT ========== -->
<rect x="280" y="420" width="160" height="80" rx="8" fill="rgba(120, 53, 15, 0.4)" stroke="#fbbf24" stroke-width="2"/> <rect x="280" y="380" width="170" height="100" rx="10" fill="rgba(120, 53, 15, 0.45)" stroke="#fbbf24" stroke-width="2"/>
<text x="360" y="448" fill="white" font-size="13" font-weight="700" text-anchor="middle">Print</text> <text x="365" y="408" fill="white" font-size="13" font-weight="700" text-anchor="middle">Print</text>
<text x="360" y="466" fill="#94a3b8" font-size="9" text-anchor="middle">Flutter + Rust</text> <text x="365" y="426" fill="#94a3b8" font-size="10" text-anchor="middle">Flutter + Rust</text>
<text x="360" y="482" fill="#fbbf24" font-size="8" text-anchor="middle">Zebra ZPL + RFID</text> <text x="365" y="444" fill="#fbbf24" font-size="9" text-anchor="middle">Zebra ZPL RFID</text>
<text x="365" y="460" fill="#94a3b8" font-size="8" text-anchor="middle">Print UI + Print API</text>
<!-- IoT (Rust) --> <!-- ========== CAPTURE (native) ========== -->
<rect x="560" y="420" width="160" height="80" rx="8" fill="rgba(251, 146, 60, 0.35)" stroke="#fb923c" stroke-width="2"/> <rect x="500" y="380" width="170" height="100" rx="10" fill="rgba(136, 19, 55, 0.45)" stroke="#fb7185" stroke-width="2"/>
<text x="640" y="448" fill="white" font-size="13" font-weight="700" text-anchor="middle">IoT</text> <text x="585" y="408" fill="white" font-size="13" font-weight="700" text-anchor="middle">Capture</text>
<text x="640" y="466" fill="#94a3b8" font-size="9" text-anchor="middle">Rust (tallyflow-iot)</text> <text x="585" y="426" fill="#94a3b8" font-size="10" text-anchor="middle">(PhotoTag)</text>
<text x="640" y="482" fill="#fb923c" font-size="8" text-anchor="middle">Fixed readers • Sensors</text> <text x="585" y="444" fill="#fb7185" font-size="9" text-anchor="middle">Native App</text>
<text x="585" y="460" fill="#94a3b8" font-size="8" text-anchor="middle">Photo + Barcode QC</text>
<!-- Arrows --> <!-- ========== IoT ========== -->
<rect x="720" y="380" width="170" height="100" rx="10" fill="rgba(251, 146, 60, 0.4)" stroke="#fb923c" stroke-width="2"/>
<text x="805" y="408" fill="white" font-size="13" font-weight="700" text-anchor="middle">IoT</text>
<text x="805" y="426" fill="#94a3b8" font-size="10" text-anchor="middle">Rust</text>
<text x="805" y="444" fill="#fb923c" font-size="9" text-anchor="middle">Fixed Readers • Sensors</text>
<text x="805" y="460" fill="#94a3b8" font-size="8" text-anchor="middle">MQTT / HTTP / BLE</text>
<!-- Assets (Mobile) → Core --> <!-- ========== CONNECTIONS ========== -->
<line x1="232" y1="335" x2="280" y2="190" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="240" y="270" fill="#22d3ee" font-size="9" font-weight="600">Sync / Enroll</text>
<!-- Core Backend ↔ MongoDB --> <!-- Assets → Core (solid) -->
<line x1="382" y1="150" x2="638" y2="150" stroke="#a78bfa" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="232" y1="420" x2="280" y2="340" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="510" y="143" fill="#a78bfa" font-size="9">Owns &amp; Persists</text> <text x="240" y="375" fill="#22d3ee" font-size="9" font-weight="600">API Sync</text>
<!-- Core → Print --> <!-- Print → Core (solid) -->
<line x1="360" y1="192" x2="360" y2="418" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="365" y1="378" x2="365" y2="340" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="375" y="310" fill="#fbbf24" font-size="9">Print Jobs</text> <text x="380" y="362" fill="#fbbf24" font-size="9">Print Jobs / Config</text>
<!-- IoT → Core --> <!-- Capture → Core (solid) -->
<line x1="640" y1="418" x2="640" y2="192" stroke="#fb923c" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="585" y1="378" x2="585" y2="340" stroke="#fb7185" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="655" y="310" fill="#fb923c" font-size="9">Tag Events</text> <text x="600" y="362" fill="#fb7185" font-size="9">Photos + Metadata</text>
<!-- Capture → Core --> <!-- IoT → Core (dashed, optional) -->
<line x1="778" y1="335" x2="720" y2="190" stroke="#fb7185" stroke-width="2" marker-end="url(#arrowhead)"/> <line x1="805" y1="378" x2="805" y2="340" stroke="#fb923c" stroke-width="2" stroke-dasharray="5,4" marker-end="url(#arrowhead-dashed)"/>
<text x="765" y="270" fill="#fb7185" font-size="9" font-weight="600">Photos + Metadata</text> <text x="820" y="362" fill="#fb923c" font-size="9">Events (optional)</text>
<!-- Core Backend owns MongoDB (short solid line) -->
<line x1="432" y1="140" x2="678" y2="140" stroke="#a78bfa" stroke-width="2" marker-end="url(#arrowhead)"/>
<!-- Legend --> <!-- Legend -->
<text x="80" y="570" fill="white" font-size="10" font-weight="700">Legend</text> <text x="60" y="530" fill="white" font-size="10" font-weight="700">Legend</text>
<rect x="80" y="582" width="18" height="11" rx="2" fill="rgba(6, 78, 59, 0.5)" stroke="#34d399" stroke-width="1"/> <rect x="60" y="542" width="18" height="11" rx="2" fill="rgba(6, 78, 59, 0.55)" stroke="#34d399" stroke-width="1"/>
<text x="105" y="591" fill="#94a3b8" font-size="8">Core (Go + Vue.js)</text> <text x="85" y="551" fill="#94a3b8" font-size="8">Core (Go + Vue.js + MongoDB)</text>
<rect x="230" y="582" width="18" height="11" rx="2" fill="rgba(8, 51, 68, 0.45)" stroke="#22d3ee" stroke-width="1"/> <rect x="260" y="542" width="18" height="11" rx="2" fill="rgba(8, 51, 68, 0.5)" stroke="#22d3ee" stroke-width="1"/>
<text x="255" y="591" fill="#94a3b8" font-size="8">Assets (Flutter)</text> <text x="285" y="551" fill="#94a3b8" font-size="8">Assets (Flutter)</text>
<rect x="355" y="582" width="18" height="11" rx="2" fill="rgba(136, 19, 55, 0.4)" stroke="#fb7185" stroke-width="1"/> <rect x="385" y="542" width="18" height="11" rx="2" fill="rgba(120, 53, 15, 0.45)" stroke="#fbbf24" stroke-width="1"/>
<text x="380" y="591" fill="#94a3b8" font-size="8">Capture (Go)</text> <text x="410" y="551" fill="#94a3b8" font-size="8">Print (Flutter/Rust + UI)</text>
<rect x="485" y="582" width="18" height="11" rx="2" fill="rgba(120, 53, 15, 0.4)" stroke="#fbbf24" stroke-width="1"/> <rect x="530" y="542" width="18" height="11" rx="2" fill="rgba(136, 19, 55, 0.45)" stroke="#fb7185" stroke-width="1"/>
<text x="510" y="591" fill="#94a3b8" font-size="8">Print (Flutter/Rust)</text> <text x="555" y="551" fill="#94a3b8" font-size="8">Capture (Native)</text>
<rect x="615" y="582" width="18" height="11" rx="2" fill="rgba(251, 146, 60, 0.35)" stroke="#fb923c" stroke-width="1"/> <rect x="660" y="542" width="18" height="11" rx="2" fill="rgba(251, 146, 60, 0.4)" stroke="#fb923c" stroke-width="1"/>
<text x="640" y="591" fill="#94a3b8" font-size="8">IoT (Rust)</text> <text x="685" y="551" fill="#94a3b8" font-size="8">IoT (Rust)</text>
<rect x="720" y="582" width="18" height="11" rx="2" fill="rgba(76, 29, 149, 0.45)" stroke="#a78bfa" stroke-width="1"/> <line x1="800" y1="547" x2="818" y2="547" stroke="#fb923c" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arrowhead-dashed)"/>
<text x="745" y="591" fill="#94a3b8" font-size="8">MongoDB (Core-owned)</text> <text x="825" y="551" fill="#94a3b8" font-size="8">Optional flow</text>
<line x1="870" y1="587" x2="888" y2="587" stroke="#64748b" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<text x="895" y="591" fill="#94a3b8" font-size="8">API Flow</text>
</svg> </svg>
</div> </div>
@@ -179,10 +183,10 @@
<h3>Core (Central)</h3> <h3>Core (Central)</h3>
</div> </div>
<ul> <ul>
<li>• Go backend (API, business logic)</li> <li>• Go backend + Vue.js Web UI</li>
<li>• Vue.js 2 web frontend</li>
<li>• Owns MongoDB (assets, tags, photos, zones)</li> <li>• Owns MongoDB (assets, tags, photos, zones)</li>
<li>Integration hub for all modules</li> <li>Single API integration point</li>
<li>• Configuration &amp; reporting hub</li>
</ul> </ul>
</div> </div>
<div class="card"> <div class="card">
@@ -191,19 +195,31 @@
<h3>Assets (Mobile)</h3> <h3>Assets (Mobile)</h3>
</div> </div>
<ul> <ul>
<li>• Flutter mobile app</li> <li>• Flutter native mobile app</li>
<li>Lightweight inventory &amp; seek/find</li> <li>Seek &amp; find, lightweight inventory</li>
<li>• Enrollment, zone moves</li> <li>• Enrollment &amp; zone management</li>
<li>• Syncs with Core API</li> <li>• Syncs with Core API</li>
</ul> </ul>
</div> </div>
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<div class="card-dot rose"></div> <div class="card-dot amber"></div>
<h3>Capture (PhotoTag)</h3> <h3>Print</h3>
</div> </div>
<ul> <ul>
<li>Go microservice + Mongo</li> <li>Flutter + Rust</li>
<li>• Zebra ZPL + RFID encoding</li>
<li>• Print UI for configuration</li>
<li>• Or direct Print API usage</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot rose"></div>
<h3>Capture</h3>
</div>
<ul>
<li>• Native mobile app (PhotoTag)</li>
<li>• Photo + barcode association</li> <li>• Photo + barcode association</li>
<li>• QC &amp; visual documentation</li> <li>• QC &amp; visual documentation</li>
<li>• Feeds Core via API</li> <li>• Feeds Core via API</li>
@@ -211,20 +227,20 @@
</div> </div>
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<div class="card-dot amber"></div> <div class="card-dot orange"></div>
<h3>Print + IoT</h3> <h3>IoT</h3>
</div> </div>
<ul> <ul>
<li>Print: Flutter + Rust (Zebra ZPL)</li> <li>Rust service</li>
<li>IoT: Rust (fixed readers, sensors)</li> <li>Fixed readers (Impinj/Zebra)</li>
<li>MQTT / HTTP / BLE ingestion</li> <li>Sensors (location, temp, shock)</li>
<li>All route through Core</li> <li>Optional dashed flow to Core</li>
</ul> </ul>
</div> </div>
</div> </div>
<p class="footer"> <p class="footer">
TallyFlow • Architecture verified against source in ~/src (tallyflow-web, photomicroservice, tallyflow-iot, tallyflow_mobile, tallyflow-print) • 2026 TallyFlow • 5 components only • MongoDB nested inside Core • Verified from source • 2026
</p> </p>
</div> </div>
</body> </body>
+31 -21
View File
@@ -2,33 +2,43 @@
**Diagram:** [[TallyFlow-Architecture-Diagram.html]] **Diagram:** [[TallyFlow-Architecture-Diagram.html]]
## Overview (Source-Verified) ## Overview (5 Components)
TallyFlow's architecture is centered on **TallyFlow Core**, which serves as the single integration hub and owns the primary data store. TallyFlow is built around exactly **five primary components**, with **Core** as the central hub that owns the database.
**Core** consists of: ### 1. Core (Central)
- Go backend (API layer, business logic, using Echo/gRPC patterns) - Go backend + Vue.js Web UI
- Vue.js 2 web frontend (tallyflow-web) for asset management, reporting, and configuration - Owns MongoDB (assets, tags, photos, zones, events)
- MongoDB database (owned by Core) storing the asset registry, tags, photo metadata, zones, events, and audit history - Single API integration point for all other components
- Handles configuration, reporting, and orchestration
All other components communicate exclusively through Core's REST + WebSocket APIs. ### 2. Assets (Mobile)
- Flutter native mobile application
- Lightweight inventory, geiger-style seek & find, enrollment, zone moves
- Communicates with Core via REST/WebSocket API
**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. ### 3. Print
- Flutter + Rust implementation
- Zebra ZPL-compatible label printing and RFID encoding
- Includes **Print UI** for configuration
- Can also be driven directly via the **Print API**
**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. ### 4. Capture (PhotoTag)
- Native mobile application (not Go/Vue)
- Associates photos with barcodes, work orders, and metadata
- Focused on quality control and visual documentation
- Sends data to Core via API
**TallyFlow Print** is a Flutter + Rust application that generates Zebra ZPL-compatible labels and RFID encodings, with optional auto-registration into Core. ### 5. IoT
- Rust-based service
- Integrates fixed RFID readers (Impinj, Zebra) and environmental sensors
- Pushes tag events and sensor data to Core (optional / dashed connection)
**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. **MongoDB** is not a standalone component — it is fully owned and managed inside Core.
This structure was verified by inspecting the repositories in `~/src/` (tallyflow-web, photomicroservice, tallyflow-iot, tallyflow_mobile, tallyflow-print, etc.). ## Data Flows
- **Assets, Print, Capture** → Core: Solid lines (primary, always-available flows)
- **IoT → Core**: Dashed line (optional, event-driven)
- Core Web UI and Print UI are shown as sub-elements within their respective components
## Key Data Flows This structure was verified by reviewing the actual source repositories in `~/src/`. All external modules interact exclusively through Cores API layer.
- **Assets (Mobile) ↔ Core**: Enrollment, inventory updates, seek/find sync via API
- **Capture → Core**: Photo metadata and QC records
- **Print → Core**: Print jobs and optional asset registration
- **IoT → Core**: Real-time tag/sensor events
- **Core ↔ MongoDB**: Core owns and directly manages all persistent data (assets, photos, zones, history)
- **Core Web UI (Vue.js)**: Operator interface for the full system
Core is the only component that directly owns the database. All other modules are clients or data producers that route through Core.