INOVIT Dealer Portal
Solo-built a full replacement for a legacy C#/.NET 4.8 dealer portal — both systems running on the same production database, processing live orders, with zero downtime and zero schema changes.
- Role
- Solo build · architecture → deploy
- Year
- 2026
- Type
- B2B e-commerce · ERP
- Status
- Live

A B2B e-commerce platform replacing a legacy .NET ERP — without ever taking it offline.
The new Django API and the old .NET system read and write the same SQL Server tables, in parallel, processing live dealer orders. 49 API endpoints, four regional databases behind one codebase, cross-repo OpenAPI type generation, and a migration that changed not a single column of the production schema.
My Role
Everything. Architecture design, frontend, backend, database mapping, CI/CD pipelines, Docker infrastructure, monitoring, and production deployment. Solo developer on a greenfield rewrite.
The legacy .NET ERP processes live orders daily — it could not go offline, and its database schema could not change. I had to build a new system that reads and writes the same tables simultaneously, without the old system ever noticing.
At a Glance
Tech Stack
- Frontend
- React · TypeScript · Vite · Ant Design
- Backend
- Django · DRF · Python · Gunicorn
- Database
- SQL Server 2025 — 4 regional databases, 42 unmanaged models via mssql-django
- Infra
- AWS Lightsail · Docker · Caddy · GitHub Actions · S3 · Lambda · SES (SMTP)
- Testing
- Vitest · Playwright · pytest · OpenAPI drift checks in CI
Two Systems, One Database
The new Django API and the legacy .NET ERP read and write to the same SQL Server tables at the same time. Django maps 42 legacy tables with managed = False — the ORM works normally, but Django never tries to create, alter, or migrate those tables. Two new managed tables were added for asset categorization features the ERP doesn't touch. Both systems process orders in parallel without awareness of each other.
New System
- React SPA
- Caddy
- Django API
Shared Infrastructure
- SQL Server 2025
- Legacy .NET ERP
Supporting Services
- S3 + CDN
- Monitoring
One Codebase, Four Databases
Dealers in Australia, UK, and US each have their own SQL Server database with regional pricing, inventory, and orders. Rather than deploying a separate backend per region, a single Django instance routes every ORM query to the correct database based on the JWT region claim — through a custom middleware + database router pair.
Dealer logs in
JWT carries region claim
Middleware
Extracts region per request
DB Router
Routes ORM to correct DB
AU / UK / US / Demo
Isolated data per region
Trade-offs
OpenAPI as the contract boundary
Three repos, one developer — the frontend had to break loudly when the backend API changed. Backend CI generates the OpenAPI spec, dispatches it to the frontend repo, and auto-generates TypeScript types. Contract drift is a build failure, not a silent runtime bug discovered by a dealer.
Auth caching across AWS instances
The SQL Server lives on a separate AWS instance. Every auth lookup crosses the network. A 5-minute LocMemCache on the API server eliminates that round-trip for repeat requests — short enough that ERP-side user changes (disabling an account, changing permissions) still propagate within minutes.
2 MB bundle budget
A hard CI gate at 2 MB catches size regressions before they merge — keeps the app fast without relying on manual review to spot bloated imports.
Build-time architecture enforcement
Nine feature modules can turn into a dependency web fast. ESLint rules encode the dependency direction (types → services → hooks → features) and forbid cross-feature imports. Architectural violations fail the build — they don't survive as "we'll fix it later" TODOs.
Want the full walkthrough?
Happy to talk through the architecture, the decisions, and the trade-offs behind this build.


