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>
<h1>TallyFlow Architecture</h1>
</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 class="diagram-container">
<svg viewBox="0 0 1000 640">
<svg viewBox="0 0 1000 620">
<defs>
<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" />
</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">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/>
</pattern>
@@ -76,99 +79,100 @@
<!-- Background Grid -->
<rect width="100%" height="100%" fill="url(#grid)" />
<!-- Central Core Region (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"/>
<text x="180" y="78" fill="#34d399" font-size="12" font-weight="700">TallyFlow Core (Central Hub)</text>
<!-- ========== CORE (Central, owns DB) ========== -->
<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="220" y="85" fill="#34d399" font-size="13" font-weight="700">TallyFlow Core (Central Hub)</text>
<!-- 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"/>
<text x="290" y="140" fill="white" font-size="14" 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="290" y="174" fill="#34d399" font-size="9" text-anchor="middle">REST + WebSocket API</text>
<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="330" y="132" fill="white" font-size="13" font-weight="700" text-anchor="middle">Core Backend</text>
<text x="330" y="150" fill="#94a3b8" font-size="10" text-anchor="middle">Go (Echo / gRPC)</text>
<text x="330" y="166" fill="#34d399" font-size="9" text-anchor="middle">REST + WebSocket API</text>
<!-- Core Frontend (Vue.js) -->
<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="510" y="140" fill="white" font-size="14" 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="510" y="174" fill="#22d3ee" font-size="9" text-anchor="middle">Asset management, reporting</text>
<!-- Core Web UI (Vue.js) - sub-box -->
<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="560" y="132" fill="white" font-size="13" font-weight="700" text-anchor="middle">Core Web UI</text>
<text x="560" y="150" fill="#94a3b8" font-size="10" text-anchor="middle">Vue.js 2</text>
<text x="560" y="166" fill="#22d3ee" font-size="9" text-anchor="middle">Asset mgmt, reporting, config</text>
<!-- MongoDB (owned by Core) -->
<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="725" y="140" fill="white" font-size="14" 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="725" y="174" fill="#a78bfa" font-size="9" text-anchor="middle">Tags • Photos • Zones • Events</text>
<!-- MongoDB (nested inside Core) -->
<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="730" y="132" fill="white" font-size="11" font-weight="700" text-anchor="middle">MongoDB</text>
<text x="730" y="150" fill="#c4b5fd" font-size="8" text-anchor="middle">Assets • Tags</text>
<text x="730" y="164" fill="#c4b5fd" font-size="8" text-anchor="middle">Photos • Zones</text>
<!-- Assets (Mobile) - Flutter -->
<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="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>
<!-- Core API label -->
<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>
<!-- Capture (PhotoTag) - Go microservice -->
<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="855" y="318" fill="white" font-size="13" font-weight="700" text-anchor="middle">Capture</text>
<text x="855" y="336" fill="#94a3b8" font-size="9" text-anchor="middle">(PhotoTag)</text>
<text x="855" y="352" fill="#fb7185" font-size="8" text-anchor="middle">Go + Mongo</text>
<text x="855" y="366" fill="#94a3b8" font-size="8" text-anchor="middle">Photo + Barcode QC</text>
<!-- ========== ASSETS (Mobile) ========== -->
<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="145" y="408" fill="white" font-size="13" font-weight="700" text-anchor="middle">Assets</text>
<text x="145" y="426" fill="#94a3b8" font-size="10" text-anchor="middle">(Mobile)</text>
<text x="145" y="444" fill="#22d3ee" font-size="9" text-anchor="middle">Flutter</text>
<text x="145" y="460" fill="#94a3b8" font-size="8" text-anchor="middle">Seek &amp; Find • Inventory • Enrollment</text>
<!-- Print (Zebra ZPL) -->
<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="360" y="448" 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="360" y="482" fill="#fbbf24" font-size="8" text-anchor="middle">Zebra ZPL + RFID</text>
<!-- ========== PRINT ========== -->
<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="365" y="408" fill="white" font-size="13" font-weight="700" text-anchor="middle">Print</text>
<text x="365" y="426" fill="#94a3b8" font-size="10" text-anchor="middle">Flutter + Rust</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) -->
<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="640" y="448" fill="white" font-size="13" font-weight="700" text-anchor="middle">IoT</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>
<!-- ========== CAPTURE (native) ========== -->
<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="585" y="408" fill="white" font-size="13" font-weight="700" text-anchor="middle">Capture</text>
<text x="585" y="426" fill="#94a3b8" font-size="10" text-anchor="middle">(PhotoTag)</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 -->
<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>
<!-- ========== CONNECTIONS ========== -->
<!-- Core Backend ↔ MongoDB -->
<line x1="382" y1="150" x2="638" y2="150" stroke="#a78bfa" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="510" y="143" fill="#a78bfa" font-size="9">Owns &amp; Persists</text>
<!-- Assets → Core (solid) -->
<line x1="232" y1="420" x2="280" y2="340" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="240" y="375" fill="#22d3ee" font-size="9" font-weight="600">API Sync</text>
<!-- Core → Print -->
<line x1="360" y1="192" x2="360" y2="418" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="375" y="310" fill="#fbbf24" font-size="9">Print Jobs</text>
<!-- Print → Core (solid) -->
<line x1="365" y1="378" x2="365" y2="340" stroke="#fbbf24" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="380" y="362" fill="#fbbf24" font-size="9">Print Jobs / Config</text>
<!-- IoT → Core -->
<line x1="640" y1="418" x2="640" y2="192" stroke="#fb923c" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="655" y="310" fill="#fb923c" font-size="9">Tag Events</text>
<!-- Capture → Core (solid) -->
<line x1="585" y1="378" x2="585" y2="340" stroke="#fb7185" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="600" y="362" fill="#fb7185" font-size="9">Photos + Metadata</text>
<!-- Capture → Core -->
<line x1="778" y1="335" x2="720" y2="190" stroke="#fb7185" stroke-width="2" marker-end="url(#arrowhead)"/>
<text x="765" y="270" fill="#fb7185" font-size="9" font-weight="600">Photos + Metadata</text>
<!-- IoT → Core (dashed, optional) -->
<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="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 -->
<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"/>
<text x="105" y="591" fill="#94a3b8" font-size="8">Core (Go + Vue.js)</text>
<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="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"/>
<text x="255" y="591" fill="#94a3b8" font-size="8">Assets (Flutter)</text>
<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="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"/>
<text x="380" y="591" fill="#94a3b8" font-size="8">Capture (Go)</text>
<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="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"/>
<text x="510" y="591" fill="#94a3b8" font-size="8">Print (Flutter/Rust)</text>
<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="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"/>
<text x="640" y="591" fill="#94a3b8" font-size="8">IoT (Rust)</text>
<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="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"/>
<text x="745" y="591" fill="#94a3b8" font-size="8">MongoDB (Core-owned)</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>
<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="825" y="551" fill="#94a3b8" font-size="8">Optional flow</text>
</svg>
</div>
@@ -179,10 +183,10 @@
<h3>Core (Central)</h3>
</div>
<ul>
<li>• Go backend (API, business logic)</li>
<li>• Vue.js 2 web frontend</li>
<li>• Go backend + Vue.js Web UI</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>
</div>
<div class="card">
@@ -191,19 +195,31 @@
<h3>Assets (Mobile)</h3>
</div>
<ul>
<li>• Flutter mobile app</li>
<li>Lightweight inventory &amp; seek/find</li>
<li>• Enrollment, zone moves</li>
<li>• Flutter native mobile app</li>
<li>Seek &amp; find, lightweight inventory</li>
<li>• Enrollment &amp; zone management</li>
<li>• Syncs with Core API</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot rose"></div>
<h3>Capture (PhotoTag)</h3>
<div class="card-dot amber"></div>
<h3>Print</h3>
</div>
<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>• QC &amp; visual documentation</li>
<li>• Feeds Core via API</li>
@@ -211,20 +227,20 @@
</div>
<div class="card">
<div class="card-header">
<div class="card-dot amber"></div>
<h3>Print + IoT</h3>
<div class="card-dot orange"></div>
<h3>IoT</h3>
</div>
<ul>
<li>Print: Flutter + Rust (Zebra ZPL)</li>
<li>IoT: Rust (fixed readers, sensors)</li>
<li>MQTT / HTTP / BLE ingestion</li>
<li>All route through Core</li>
<li>Rust service</li>
<li>Fixed readers (Impinj/Zebra)</li>
<li>Sensors (location, temp, shock)</li>
<li>Optional dashed flow to Core</li>
</ul>
</div>
</div>
<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>
</div>
</body>
+31 -21
View File
@@ -2,33 +2,43 @@
**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:
- 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
### 1. Core (Central)
- Go backend + Vue.js Web UI
- Owns MongoDB (assets, tags, photos, zones, events)
- 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
- **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.
This structure was verified by reviewing the actual source repositories in `~/src/`. All external modules interact exclusively through Cores API layer.