StylebankApparel wholesale, manufacturing and marketplace retail

A multi-tenant apparel platform: what we built for Stylebank

Stylebank runs four apparel businesses on one Django codebase, each with its own database, S3 bucket and Redis namespace. It pulls marketplace orders, factory production and accounting into one place, then plans replenishment against it. Apparel platform development at this shape is really wholesale apparel software, with four businesses sharing one schema.

Read the study

Build fingerprint

Where it runs, how we engaged, and what it was built on.

See it livestylebank.io
Market
India, selling through national marketplaces
Engagement
Dedicated team
Platforms
Web
Stack
10 technologies

Technologies

  • Python
  • Django
  • MySQL
  • Redis
  • Celery
  • django-crontab
  • AWS S3
  • Google BigQuery
  • OpenRouter
  • collectfasta

Trusted by
Global Industry Leaders

Al Amri Express
Cheer Sagar
MRO Supply
BankSathi
DoraDori
Kroolo
StyleBank
Hire Right
Cintas
Up in the Air
Famepilot
Swedishness
Corrib Coil
iCare Heal
TWAM
NDC: National Distribution Company Oman
Moneteo
Algora
Numerology
G4Girl

What makes a multi-tenant apparel platform hard

What makes this problem genuinely hard - before any client name enters the page.

One codebase, several companies, and where you draw the wall

Each business in a group carries its own buyers, factories and books. The cheap option is a tenant column on every table, which holds until one query misses the filter and a company reads a rival's cost sheet. Isolating by database makes it an infrastructure problem instead, and buys per company restore, retention and job windows.

Marketplace data arrives late, then arrives again

Channel data restates itself. An order cancels after picking, a return lands weeks after the sale, settlement follows on its own cycle with commission attached. Channels do not share vocabulary either: a return to origin is not a customer return, although both reduce the same figure. Ingest that as an append and you double count. Pulls upsert on the channel's identifier and carry a snapshot date, so you can answer what you believed on the day you decided.

Replenishment is a fabric question before it is a stock question

You do not replenish finished goods, you replenish the ability to make them. A plan needs rate of sale at style, colour and size, stock at the warehouse and each channel, work in progress on the floor, and fabric on hand including greige not yet dyed. Lead time is a chain: dyeing, cutting, sewing and finishing each hold a queue. B2B and ecom diverge: one plans against an order book and pack ratios, the other against days of cover and a size curve.

History tables grow faster than anyone plans for

A row per style per day per channel per metric compounds quietly, and nothing in the application tells you. You find out when a backup stops fitting its window, or an ALTER becomes an outage rather than a migration. Archiving buys time; the structural fix is to stop mixing an append only fact stream with operational state.

The situation, and what we built

Stylebank is the operating platform for four apparel businesses, each manufacturing, selling wholesale and selling through marketplaces. The commercial picture sat in systems with no reason to talk to each other: channel orders, inventory, returns and GRN in an aggregator fronting five marketplaces; production, defects and costing in a factory ERP; item summary, BOM and fabric stock valuation in a materials ERP; invoices, sale orders and credit notes in Zoho Books; buying and sourcing in a spreadsheet. The people who needed the answers were in none of them, reading the day's numbers in a messaging group. Nobody asked for a new system of record, only for one place where those sources reconcile daily.

One Django project, four isolated tenants

One codebase serves all four. Each tenant runs on its own MySQL database, S3 bucket and Redis database number, so records, media and queues never share a namespace.

The integration layer

Connectors for the channel aggregator, factory ERP, materials ERP and Zoho Books, each with its own auth and refresh path, plus Apps Script sheet imports, Drive images and marketplace style stats.

Planning: replenishment, buying, PO and factory WIP

Replenishment splits into B2B and ecom across fabric and finished goods, beside a greige sourcing tracker, planning PO with factory WIP, and an SCM tracker carrying PPM reports.

Costing and margin

Daily factory cost against target, fed by an Apps Script webhook and pushed to the messaging group, with a production PnL dashboard carrying marketplace style stats, ads and CM2.

The pipeline, the reports and the per style AI read

Celery workers on Redis run the ETL into BigQuery, including replenishment pushes and per style LLM analysis through OpenRouter. Around 35 django-crontab jobs schedule the rest; reports leave as Excel and PDF to Slack, messaging groups and email.

Tradeoffs

Decisions, and what each one cost

Every choice closed off something else. Those costs are stated, not hidden.

A database per tenant, not a tenant column

Instead of: One database with row level filtering on a tenant id. Separate factories and books, and per tenant ETL heavy enough that one should not slow another.

What we acceptedEvery migration runs four times, connection pools multiply, and no cross tenant question survives a JOIN.

Scheduled pulls with idempotent upserts, not event driven ingestion

Instead of: Webhooks and streaming from each upstream system. Several sources publish on their own batch schedule with no usable event stream, and a re-runnable pull recovers more cheaply than a missed event.

What we acceptedData is only as fresh as its cron window, and roughly 35 jobs need watching, hence the run tracker.

BigQuery for analysis, MySQL for operations

Instead of: Read replicas and heavier indexes on the primary. The operational database carries a large append only history stream, so analytical scans compete with the write path.

What we acceptedA second copy with its own lag, and two places a number could be computed, so definitions pin in the pipeline.

Reusable

Which system owns which number

Every multi system apparel stack argues with itself eventually. The ownership map underneath:

SystemOwnsDo not ask it forHow the data arrives
Channel aggregatorChannel orders, inventory sync, returns, GRN, putawayLanded margin, factory costContinuous pull, returns restate for weeks
Factory ERPProduction output, GRN summary, defects, cost per unitWhat actually soldDaily, after shift close
Materials ERPItem summary, BOM, stock, fabric valuationFinished goods at the channelDaily batch
Zoho BooksInvoices, sale orders, credit notes, customer masterPhysical stockOn document creation, token expires
Marketplace directStyle stats, ads, CM2 inputsOrder truth, cancellation restates itReporting lag of days
BigQuery warehouseReconciled history everyone reports offAnything live or operationalWhenever the ETL last ran

The system that owns the event owns the number, and reconciliation belongs in the pipeline, never in each dashboard.

Delivery

What shipped

Capabilities delivered and the scope of the build - not results attributed to it.

Capabilities

  • Returns reconciliation with package sync and warehouse dashboard
  • Marketplace analytics covering style stats, ads and CM2

Scope

  • Scope, not results: 4 tenants, each with own database, S3 bucket and Redis DB
  • 9 Django apps in one project
  • 15 external systems integrated
  • 5 marketplaces through one aggregator
  • Around 35 scheduled jobs on django-crontab

Scope of the build, not results attributed to it.

Building something with the same constraints?

Bring us the part you think is hardest.

Risk, ownership and commercials

Access and data

An IP whitelist middleware geolocates the caller, with per profile activity tracking behind it. Records and media stay in the tenant's own database and bucket.

Ownership

Because isolation is physical rather than a query filter, separating one business is a copy, not an extraction. Marketplace, accounting and messaging credentials stay in the client's accounts.

Operational visibility

Nobody watches 35 jobs by eye, so a report status page records every cron run and allows a re-trigger. A failed job shows as a row, not an email nobody received.

Engagement model
Dedicated team
Rate band
40 to 100 USD per hour by role, blending to 60 to 70
How we quote
No fixed price before discovery. The integration inventory is the estimate, because every upstream system brings its own auth, rate limit and way of failing quietly.

Reflection

What we would do differently

The style history table should have been append only and partitioned from the start, with a short retention window in MySQL and the long tail in BigQuery. It grew inside the operational database instead, so a schema change is now a scheduling exercise rather than a migration. Two schedulers is one too many: django-crontab and Celery overlap. The per style AI read stays advisory, because we have not earned the right to let it move a number.

Why give each tenant its own database instead of a tenant column?

Because a shared table with a tenant filter puts every future query one mistake away from exposing another company's costing. Separate databases make isolation an infrastructure property, with per business backup and retention. The cost is real: every migration runs four times, and cross tenant questions need a warehouse.

Why does marketplace data need reconciliation rather than a straight import?

Because a marketplace sale is not final when placed. Cancellations, returns to origin, customer returns and settlement adjustments all restate it, sometimes weeks later. Pulls upsert on the channel's identifier, so a restatement corrects the row instead of adding one.

What does the AI actually do here?

Inside the ETL pipeline, not the request path. Celery workers assemble the data for a style, send it through OpenRouter, and store the result where planners see it. It suggests; a person decides what to repeat, discount or drop.

Context

Where this sits in our work

Talk to an engineer, not a salesperson

Tell us what your operation runs on today.

Our Success Stories

Real feedback from the people we've proudly partnered with.

Brooklyn Foster profile

Brooklyn Foster

Sales Director |Cintas

United States

GoodFirms
"

Zyneto Global Technologies provided excellent project management and technical expertise throughout the engagement. The team was responsive, collaborative, and adaptive, ensuring the project met our expectations and set a strong foundation for future growth.

"
Verified Review
Rating: 5 out of 5
Krystian Chlebek profile

Krystian Chlebek

Founder & CEO |Moneteo

TechBehemoths
"

We engaged Zyneto to design and develop a custom web platform for Moneteo, aimed at improving project management, data tracking, and collaboration across internal teams and external partners. Their work included full-stack web development, custom modules for workflow automation, API integration, and comprehensive testing.

"
Verified Review
Rating: 5 out of 5
Kevin Scott profile

Kevin Scott

CEO |E-Commerce Platform

Clutch
"

Overall, their responsiveness and timely deliveries contributed positively to the project's success. The client achieved better data management and quality. The service provider delivered the project on time and ensured prompt responsiveness throughout the engagement. Their innovative approach was outstanding.

"
Verified Review
Rating: 5 out of 5
WhatsApp
Email
Book a Meeting