FinTech
Architecting a compliant multi-tenant SaaS platform for a financial services provider
Background
A financial services provider needed to launch a multi-tenant SaaS platform capable of managing thousands of customers, active loans, and a growing user base, while operating under strict GDPR and DORA requirements. The platform had to serve multiple tenants with rigorously isolated data, while the team needed to move quickly and avoid the operational overhead of a fully distributed system before the product had proven itself.
Ascentic was brought in to design and build the backend architecture from the ground up, covering the multi-tenancy model, module boundaries, encryption, event reliability, and inter-service communication.
The challenge
The client faced a set of tightly interconnected architectural constraints:
- Strict data isolation with regulatory obligations The platform had to comply with GDPR and DORA from day one - tenant data could never be co-mingled, personal data had to be fully erasable, and audit trails had to be immutable and demonstrable to regulators. A shared schema or an application-level filter was not an acceptable risk.
- Domain complexity across multiple business areas The platform needed to support user management, customer profiles, loan processing, notifications, audit logging, and organisational structure, each with its own lifecycle and rate of change. A single unstructured codebase would become a maintenance problem almost immediately.
- Operational maturity constraints The team did not have the infrastructure or experience to justify a full microservices architecture - distributed tracing, a service mesh, multiple deployment pipelines - before the product had been validated in market.
- Reliability without distributed transactions If a customer was created, the downstream notification, audit record, and integrations had to follow reliably, even if the process crashed mid-operation.
The Solution
Ascentic's engineering team designed a modular monolith on .NET 9, delivering domain-level discipline without the operational cost of microservices.
1. Modular monolith with enforced boundaries
Structured the system around distinct business modules - Loans, Customers, Users, Notifications, Audits, and Organisations - with enforced internal boundaries and no direct cross-module references.
Each module followed a consistent Clean Architecture layering: Domain, Application (CQRS via MediatR), Infrastructure (EF Core), and Presentation (REST / gRPC).
2. Database-per-tenant isolation
Provisioned a fully separate SQL Server database per tenant, making the connection string the physical access control boundary and eliminating any risk of cross-tenant leakage through application-layer filtering.
Built tenant context propagation, flowing the correct tenant identity through HTTP requests, background jobs, gRPC calls, and event consumers. Database lookups were cached in Redis to avoid resolution overhead at runtime.
3. gRPC and integration events for inter-module communication
Used gRPC for synchronous queries between modules - permission checks, organisation lookups, tenant identity resolution - where a real-time, typed response was required.
Used integration events via MassTransit (Azure Service Bus) for asynchronous state-change communication. The publishing module moves on; consumers react independently with no coupling to the publisher.
4. Outbox Pattern for reliable event delivery
Implemented the Outbox Pattern so that domain events are written to an OutboxMessages table in the same database transaction as the entity save. A Quartz.NET background job sweeps the outbox and republishes unprocessed messages, ensuring no event is silently dropped, even if the process crashes between the write and the publish.
5. Field-level encryption with a two-layer key hierarchy
Applied AES-256 encryption to sensitive fields via an EF Core interceptor, keeping application code clean while ensuring the database stores only ciphertext. A KEK/DEK structure meant key rotation required re-encrypting only the Data Encryption Key, not all tenant data.
Used Argon2 for password hashing, chosen for its memory-hard properties that make brute-force attacks computationally expensive even with dedicated hardware.
The results
Full regulatory compliance from day one Per-tenant databases gave auditors a clear, demonstrable isolation story. GDPR erasure requests were scoped to a single database. DORA obligations were covered through the Outbox Pattern, Quartz.NET clustering, and an immutable audit log.
Structural data isolation with no conditional risk Physical separation eliminated the category of bugs where misconfigured filters expose tenant data. Isolation was enforced at the infrastructure level, not the application level.
Guaranteed event delivery under failure conditions The Outbox Pattern ensured downstream reactions - notifications, audit records, integrations - were guaranteed to follow every state change, regardless of process interruptions.
Fast onboarding and straightforward debugging Consistent module structure meant new developers could apply knowledge from one module across the entire codebase. A single process and log stream made tracing issues significantly faster than a distributed alternative.







