In production · B2B SaaS · Pharma Procurement
Haelias
- Role
- Solo CTO / Principal Engineer (with contributor support on UI)
- Period
- 2022 – present
- Clients
- Montefarmaco · Logista SPA

Key numbers
- 5.5M+Live MongoDB documents
- ~14kIn-stock cross-supplier products
- 143Express endpoints (single `main.ts`)
- 493Backend test cases (Jest + supertest)
01 · Haelias
Problem
Italian pharmacies handle procurement through phone calls, emails and faxes to multiple wholesalers, negotiate prices verbally and reconcile invoices by hand. They need a real-time multi-supplier catalog, digital purchase orders, compliance-grade audit trails and safety UX for high-value orders.
02 · Haelias
Architecture
End-to-end production stack across 5 independent git repos (core / ui / sync-worker / landingpage / haelias_docs): an Express + TypeScript backend exposing 143 endpoints mounted from a single `main.ts`, a React 19 + Tailwind 3 + MUI 7 SPA with 136 components organised in Atomic Design and 5 React Contexts (no Redux), a Vite landing page, and a Rust + Tokio sync worker that ingests Farmadati's SOAP catalog into the shared Managed MongoDB. The data layer is 16 Mongoose schemas with a multi-tenant entity model (5 RBAC roles: admin / manager / buyer / supplier / referral_partner), a custom search engine over partial weighted text indexes, and a two-tier index strategy that confines Discover/search indexes to the ~14k in-stock products instead of the full 5.5M-document catalog. The ODA domain is a 6-state order machine with cascading sub-orders (`createSubOda`), a 6-state per-line counter-offer state machine (`requested → admin_absorbed | sent_to_supplier → supplier_responded → resolved`), and a `stripSupplierDataForBuyer` sanitiser called on every buyer-facing order read. Customer support is a bidirectional Discord ↔ SSE bridge: REST-polled Discord messages stream into the UI in real time. 493 backend test cases (Jest + supertest), 68% statement coverage. The main UI is CRA + craco (not Next.js); Vite powers the landing page only.
03 · Haelias
Decisions
- 01
Cluster upgrade vs index optimization
Diagnosed via WiredTiger cache-pressure analysis: out of 5.5M product docs, only ~14k carry active supply at any time. Replaced full indexes with partial indexes filtered on `{hasSupply: true, status: "active"}` — name, producer, category, plus a partial weighted multi-field text index (name:10, sku:8, ean:8, producer:5, category:3, description:1). Result: ~99% reduction in the in-cache search-index footprint, 70% on-disk index reduction live (1.69 GB → 369 MB), $15/mo cluster kept, €1,260/year upgrade avoided.
- 02
Rust + Tokio Farmadati sync worker
Continuous SOAP-to-Mongo sync with concurrent table downloads + per-table page-level concurrency (`FARMADATI_PAGE_CONCURRENCY`) and bulk writes. Lock + run-history collections (`farmadati_sync_states`, `farmadati_sync_runs`) plus a heartbeat-based `resume` command for crash recovery. CLI modes: `delta` / `full` / `auto` / `resume` / `snapshot` / `reset-lock`. Rust `$set`-only is intentional: the backend's `hasSupply` denormalisation flag survives every Farmadati upsert because the worker only sets its own fields.
- 03
Privacy-by-design between supplier and buyer
`stripSupplierDataForBuyer` (`core/routes/orders.ts:182`) is the single sanitiser called on every buyer-facing order read: removes `warehouseAssignments`, drops `warehouse`/`entityId` from line breakdowns, and gates `priceNegotiation` visibility on order status. The counter-offer state machine has 6 states + 3 resolutions (`buyer_price_accepted` / `supplier_price_accepted` / `negotiation_failed`) with `originalNetPrice` and `absorbedNetPrice` strictly separated.
- 04
Modal warnings for high-value orders
Picked a configurable warning over a hard block for €250k–€1M+ orders — safety without frustrating enterprise buyers.
- 05
Sub-ODA cascading for partial fulfilment
When a warehouse partially fulfils a line, admin issues a sub-ODA (`createSubOda`, `routes/orders.ts:6350`) linked by `parent.subOrderIds[]` and `subOrderId: "ODA-SUB-..."`. The buyer keeps a single ODA in their view; the warehouse-side picking flow gets one fresh ODA per fulfilment round. Tested at `tests/orders.test.ts:3570-3645`.
- 06
Discord ↔ SSE customer support bridge
Real-time customer support without paying for Intercom: REST-polls a Discord channel every 10s (`libs/support-discord.ts`), persists messages to `support-conversations` + `support-messages`, then pushes them into the UI via Server-Sent Events (`libs/support-stream.ts`). Handles 429-retry, bot user filtering, and conversation-scoped subscription.
- 07
Single bulkWrite for 50k-row CSVs
CSV uploads (`csv-upload.ts`) and supply-flag maintenance (`hasSupplyMaintenance.ts`) issue a single MongoDB bulkWrite per upload regardless of size — even a 50k-row CSV is one operation, with progress streamed to the UI via the upload-progress utility.
04 · Haelias
Results
- 5.5M+
Live MongoDB documents
- ~14k
In-stock cross-supplier products
- 143
Express endpoints (single `main.ts`)
- 493
Backend test cases (Jest + supertest)
- 68%
Backend statement coverage
- 16
Mongoose schemas
- 136
React components (Atomic Design)
- 5
Independent git repos
- 70% (1.69 GB → 369 MB)
On-disk index footprint reduction (live)
- ~99%
In-cache search-index reduction
- €1,260/yr
Annual savings vs cluster upgrade
- 50k rows = 1 bulkWrite
CSV ingest benchmark
05 · Haelias
Tech & Pillars
Tech stack
- Node.js
- Express 4
- TypeScript
- MongoDB
- Mongoose
- MongoDB driver (native)
- Helmet
- Web Push
- Resend
- @react-email/components
- PDFKit
- ExcelJS
- Turf.js
- React 19
- React Router v7
- Tailwind 3
- MUI 7
- MUI X DataGrid
- framer-motion
- Chart.js
- Recharts
- react-window
- CRA + craco
- Vite (landingpage only)
- Rust 2021
- Tokio
- reqwest
- mongodb (Rust driver)
- rustls
- Jest
- supertest
- DigitalOcean App Platform
- Managed MongoDB
- Vercel
- Discord Bot API
- Server-Sent Events
- Farmadati SOAP
Pillars touched
- Traditional Software Engineering
- Cloud & DevOps
- IT Management & Digitalization
Contact
Building something similar?
Walk me through the context in a 30-minute call.
hello@andrea0x.me · @andrea0x_eth