All case studies

Client project · Real estate & facility management

Ninety-six apartments, one record for each.

The client ran leasing, cheques, owners and maintenance from a workbook and phone calls. We built a single operations system around their real portfolio, so the office, the owners, the contractors and the technicians all work from the same facts.

A Dubai real-estate company (name withheld)Dubai, UAE
A facility technician fixing an air-conditioning unit while holding a phone with a work-order app
96
Apartments in the pilot building, across 8 floors
5
Applications: back office, tenant, owner and contractor portals, technician app
Photos
Technicians update jobs and attach before/after photos from an Android phone
1 → many
Single-client build later generalised into a multi-company product

01

The brief

The company manages a residential building and wanted to stop running it from a spreadsheet. Units, tenancies, post-dated cheques, ownership shares, maintenance requests and contractor jobs were spread across a workbook, WhatsApp and memory.

They needed one system the office team uses every day, with a portal for each outside party: tenants raising requests and seeing payments, owners seeing dashboards and reports, contractors receiving and closing jobs, and technicians working on site.

02

What made it hard

The data was real and imperfect. The client’s workbook had units that did not match, ownership shares that did not add up to 100% and amenities recorded as free text. Importing it as-is would have put those errors straight into the new system.

Technicians work in plant rooms and basements on a weak signal, so their app had to fail honestly: a dropped connection shows a clear error, never a false “saved”. And every reference number — tenancies, parties, work orders — had to stay unique and sequential however many people were entering data at once.

03

How we approached it

We built to the client’s actual portfolio from the first week instead of to a prototype. A workbook import checks every row and lists the problems — mismatched units, broken ownership splits — for the office to fix before anything is committed.

Reference numbers are issued by the database itself, so they cannot collide. Notifications go out through an outbox, which means a message is only sent once the change it describes has been saved. Every write carries an idempotency key, so a retry after a dropped connection never creates a duplicate. Every screen shows the same record to each role, limited to that role’s part of it.

04

What we delivered

  • Back office: today view, properties, units, assets, ownership and data-quality checks
  • Maintenance: requests, work orders, contractors and quotations
  • Tenant portal: lease, payments and maintenance requests
  • Owner portal: dashboards, approvals and printable reports
  • Contractor portal: jobs, quotations and completion
  • Technician app for Android: assigned jobs, status updates and photos
  • Workbook import with a data-quality review before go-live
  • Users and roles, and a readable history of what changed and who changed it

What went into it

TypeScript monorepoFastify APIPostgreSQLDrizzle ORMReact + ViteCapacitor (Android)Transactional outbox

What we would tell the next team

  • — Build against the client’s real data from day one. The problems in a spreadsheet only show up when you load it.
  • — Let the database guarantee what must never go wrong, such as unique reference numbers, instead of trusting the application to.
  • — A field app must never show work as saved when it was not. Full offline work orders are a deliberate design step of their own, not a checkbox.
It became EXO Estates

Building something similar?

Tell us what it has to do. We reply within one business day with questions and a way to scope it.