Multi-tenant cloud platform / Production
Speed FlowLINK
Cloud platform for managing fuel dispensing stations — a Blazor front end, several APIs with separate responsibilities, and a shared multi-tenant data model.
The problem
A dispensing station combines physical devices, floor operators, self-service, billing and third-party systems. Each speaks a different protocol and has its own life cycle: devices get replaced, operations cannot stop, and one customer's data must never reach another. A single monolithic service would have tied the hardware release cadence to the web interface's.
Constraints
- Strict per-customer isolation on a shared database
- Field devices cannot be updated at the same pace as the cloud
- 24/7 operation — the schema evolves without long maintenance windows
- Surfaces with opposing requirements — the web tolerates latency, hardware does not
Decisions
Outcome
- Blazor Server front end consuming the APIs through injected HTTP services, with logic in ViewModels and thin views
- Data model with per-area prefix conventions — parameters, administration, general, customers and security — that makes the schema readable without opening documentation
- Real-time device status pushed to the interface over a messaging hub
- Third-party integration channel isolated from the business core
How the system is split
The solution is not one large service but a set of services grouped by who they talk to, over a common database:
- Core business API — serves the web interface and holds the domain logic, entities and schema evolution.
- Device API — the channel to hardware devices, with direct control over them.
- SoftAPI — a self-contained service for software-based device communication. It shares the database but deploys on its own.
- Integrations API — the entry point for third-party systems, isolated from the core.
- Real-time hub — pushes device status to the interface so the browser never has to poll.
- Self-service — the surface for unattended operation.
That split is the product’s structural decision: it lets the device channel — the one that cannot fail — evolve at a different pace from the web interface.
Multi-tenancy as an invariant, not a convention
Every customer shares the database. Isolation is solved at the data layer: the repository receives the authenticated user’s context and applies the owner filter uniformly. No query decides on its own whether to filter.
It is the kind of rule that, left to each developer’s judgement, works right up until someone writes a new query in a hurry.
How the work runs
The product moves in sprints. Every requirement is refined beforehand with whoever raised it, estimated as a team and taken into the sprint with an agreed scope; what comes out of it goes through peer code review and continuous integration before it touches production.
That rhythm is what makes it possible to sustain several surfaces at once without any of them having a single owner: work rotates, decisions are recorded in the schema and the shared contracts, and cross-review keeps the judgement even across services. My own work inside that is full stack and cuts across the device channel, the interface, the business core, the data model and the data centralization with the floor application.
Each requirement is followed end to end, not just implemented: gathering it with whoever raised it, proposing the solution, building it, testing, continuous integration and seeing the release through.
Why it belongs here
It is the system I have spent the longest with, and where decisions have consequences measurable in years. It shows sustained work on a real product — not an architecture drawn on a whiteboard, but one that survived five years of changing requirements — and how several people hold up a system no one carries alone.
The source code belongs to Speed Solutions. This case study describes architecture and decisions only — no source code, customer data or business figures.