From desktop-era licensing to self-serve subscription SaaS on Azure: reverse-engineered domain rules, ASP.NET Core services, Stripe-backed billing, high-volume audit storage, and a React experience accountants can run in the browser.
Client
Independent software vendor (accounting and payroll suite, United States)
Location
United States
Platform
Azure SaaS (.NET 8 APIs and workers, React SPA, Azure SQL and Blob Storage)
Engagement Model
Dedicated Team
Team Size
7 specialists
Duration
18 months
The Customer is a United States software company that sells accounting and payroll packages to tens of thousands of small and mid-sized organizations. A large share of revenue still arrives through accountant and IT partner recommendations, which raised the bar for accuracy, explainability, and predictable upgrades. For years the flagship suite lived as a desktop product with a thin companion portal; customers expected the same depth of ledgers, tax worksheets, and payroll outcomes - now available anywhere, on a subscription they can manage themselves.
Leadership committed to a full move from installable clients to a browser-first SaaS experience on Azure. The product surface was enormous: hundreds of workflows, registers, and payroll wizards that had accreted over more than a decade. Formal specifications were incomplete, and many accounting rules lived only in code paths or field trainer anecdotes. Re-implementing that behavior for the web without silently changing outcomes was the core risk - especially for tax and wage calculations where a rounding difference becomes a support storm.
Commercially, the legacy license model could not support rapid onboarding or usage-based add-ons. Operations still depended on manual billing touches and one-off key issuance. The Customer needed centralized updates, elastic capacity during payroll windows, and a subscription backbone that finance could reconcile without custom spreadsheets - while keeping partner trust high in a regulated-adjacent category.
tenant_id and organization scope; EF Core global filters and row-level conventions prevented cross-tenant reads during refactors.Softellar delivered a modular SaaS architecture. A set of stateless .NET ASP.NET Core APIs owns ledgers, payroll batches, tax configuration, and integration edges. A React and TypeScript SPA provides the day-to-day operator experience, while background workers drain queues for statements, filings packaging, and webhook processing. Entra ID handles workforce sign-in and partner organizations; Stripe backs plans, trials, and seat-based add-ons with webhooks reconciled in .NET.

Analysts and engineers reconstructed behavior screen by screen, then encoded it as a navigable domain graph: entities, calculations, validation gates, and persistence mappings. Each graph node linked to Azure DevOps work items so testers could trace a discrepancy from a UI symptom to the underlying rule, golden file, and deployment that touched it.
Parity suites compared legacy outputs to the new services for representative tenants across wage bases, benefit deductions, and state configurations. Where intentional improvements were approved - such as clearer rounding policy - they were documented as explicit deltas rather than silent drift.

Azure SQL remains the system of record for tenants, fiscal periods, payroll runs, and configuration with EF Core migrations versioned alongside APIs. Large binary outputs - PDF bundles, generated forms, and append-only audit envelopes - land in Azure Blob Storage with metadata rows in SQL for retrieval and lifecycle policies. That split kept OLTP lean while preserving defensible history for partners and auditors.

Document generation moved to provider-backed rendering pipelines orchestrated from .NET workers. Customers can fork approved templates, adjust layouts in the browser for allowed fields, and publish versions with semantic versioning so support can see exactly which template produced a statement.

Stripe Checkout and Customer Portal flows replaced manual invoicing for standard SKUs, while internal admin APIs apply credits and partner-specific price books under RBAC. Webhook handlers in .NET normalize events into ledger-friendly records so finance systems did not need bespoke CSV drops.

Ingestion adapters normalize provider-specific transactions into a canonical ledger event stream; outbound payments use signed requests, stored idempotency keys, and explicit state machines so retries never double post. Application Insights and OpenTelemetry traces span API, worker, and external calls with correlation IDs propagated to support tickets.
Encryption in transit and at rest, least-privilege managed identities for app services and workers, and periodic access reviews for partner impersonation features were treated as product requirements - not late add-ons. Penetration findings fed back into the same work-item graph so remediation stayed traceable.
Delivery unfolded in four coordinated phases:
The Customer now ships continuous improvements instead of annual media drops. Directional metrics improved across onboarding time, finance reconciliation effort, and support repeatability - especially where audit trails and subscription state became queryable for the first time.
.NET 8, C#, ASP.NET Core, Entity Framework Core, React, TypeScript, Azure SQL, Azure Blob Storage, Azure Service Bus, Azure Key Vault, Entra ID, Stripe, Application Insights, OpenTelemetry, GitHub Actions, Playwright, xUnit.
For similar replatforming work, our legacy application modernization and cloud architecture practices connect product strategy, engineering, and reliable Azure operations end to end.
We help product companies move from licensed desktop software to secure, self-serve subscription SaaS on Azure while preserving the calculations customers trust.
Industries:
Technologies:
Industries:
Technologies:
Industries:
Technologies: