Multi-Site Quoting Without Leaving the CRM
“Compress the payload. Move computation to the data. Hydrate only what the eye can see.”
The companion to the catalog-engine research: what the quoting experience feels like when payloads are small and results arrive targeted.
LWCGZIPOBJECT STORAGERUSTTYPESCRIPT
Quoting stays in the native component across tested fixtures - no portal redirect in the flow
What Made the Problem Difficult
Independent work on generated fixtures. Multi-site quoting is a good stress case because it combines three pains at once: many locations, many attributes per location, and users who refuse to leave the CRM to do the work. I built a synthetic version of that problem to test one claim - that a native component can stay native if it never holds the whole dataset.
Core Platform Constraint
Systemic FrictionWhat breaks when the dataset outgrows the component: transferring full payloads freezes the UI thread, holding everything in memory exhausts the tab, and paging through a server per keystroke feels sluggish. The temptation is an external portal - which users experience as abandonment.
- !Quoting stays inside the CRM component - no portal redirect
- !No tab crashes or multi-second freezes on large fixtures
- !Respect sandbox transport limits and security boundaries
- !Search across locations and attributes must feel instant
What System Was Designed
Compress on the way in, index outside the render path, hydrate incrementally. The component loads Pako from static resources and works in array buffers, not JSON strings. An async transport layer pages compressed chunks; the Rust indexer handles heavy scans while the TypeScript engine answers interactive refinements in memory. Only the visible slice - ranked IDs plus display fields - reaches the DOM.
Pivotal Architectural Choices
Key Architectural Decisions
Client-side gzip vs. moving quoting off-platform
The industry default for oversized payloads is an external app. Users hate the context switch.
- Standalone external quoting app
- Dozens of paged server queries per interaction
- Uncompressed batched polling
Keeps reps in the workflow they know while cutting wire bytes dramatically on repetitive catalog data.
Two engines with distinct jobs vs. one store doing everything
Bulk indexing and interactive refinement stress different resources.
- One monolithic query path
- Pure client-side array scans
Each does what it is good at. A small router sends bulk scans one way and keystroke refinements the other, over a shared contract.
What Was Actually Built
Loaded Pako as a static resource and kept payloads as array buffers end to end - no JSON string round-trips.
Chunked ingest with cooperative yielding so decompression never blocks input handling.
Built the transport as a thin chunking and orchestration layer over object storage - no database, no session affinity.
Used the Rust indexer for high-cardinality scans and json-query-lite in memory for refinements and ranking.
Rendered incrementally: the component holds result IDs and hydrates display rows for the viewport only.
Kept a shared record contract between engines so either can answer a query class without the UI caring.
Deliberate Architectural Compromises
Trade-offs & Mitigations
⚖Decompression CPU vs. transfer time
Far fewer bytes cross the network; payload caps stop biting.
The client spends cycles expanding gzip.
Chunked, yielded decompression scheduled around input frames.
⚖Two-language stack vs. one
Bulk throughput plus UI agility.
Two toolchains to govern.
One record contract and one query grammar shared by both engines.
Verified Outcomes
Quoting stays in the native component across tested fixtures - no portal redirect in the flow
Wire bytes fall sharply on repetitive catalog fixtures versus uncompressed JSON
Interactive refinements answer from memory without a server round-trip
Render cost stays flat via viewport hydration regardless of result-set size
System Schematic & Data Flow
Compressed chunks travel once; engines answer locally; the component renders only the visible slice.
Text alternative for screen readers: Architecture flow: Native Component UI to Gzip Transport via Compressed Chunk Ingest; Gzip Transport to Object Storage Layer via Chunked Binary Stream; Object Storage Layer to Rust & TS Engines via Indexed Search & Refine; Rust & TS Engines to Native Component UX via Viewport Hydration
Architectural Conclusion
“A native experience survives scale when the component stops carrying the dataset and starts carrying answers.”
- •Decide where bytes rest, where they move, and where they compute - in that order.
- •Static resources plus disciplined buffers take a CRM component surprisingly far.