Hamsayeh («همسایه», Persian for "neighbour") is my own product: a SaaS for running apartment buildings and residential complexes. Managers, board members, owners and residents use it for service charges, payments, tickets and announcements, and a separate portal serves the system's administrators. I designed its architecture and wrote most of the code, on both the back end and the front end; a few other contributors added smaller pieces, such as onboarding persistence and English localization.
Context
In many buildings the monthly service charge lives in a spreadsheet or a notebook, payments are confirmed by message, and announcements go to a group chat. The product replaces that with one system in which every amount can be traced, residents see their own balance and requests, and the manager does less bookkeeping by hand. The work is planned in product phases, from a foundation MVP (sign-in, buildings, charges and payments) through finance, services, governance and growth.
The engineering problem
A building app looks simple from the outside. Four things make it harder:
- Roles depend on the building. A person can be an ordinary user of the system, the manager of one building and a tenant in another. System roles and building roles (manager, deputy, owner, tenant, resident) have to be kept apart, and every request has to be checked against the building it touches.
- Money has to add up. Charges, payments, penalties, discounts, installments, funds, expenses and budgets all affect what a unit owes. If a figure cannot be explained, residents stop trusting the system.
- The feature list is long. Accounting, tickets, reservations, voting, subscriptions and a marketplace grow at the same time. Without firm boundaries they collapse into one data model that nobody can change safely.
- Most users are on phones. The interface is Persian and right-to-left first, and has to work as well on a phone as on a desktop.
What I built
The back end has two ASP.NET Core API hosts, one for building users and one for administration, over a shared application layer of MediatR commands and queries validated with FluentValidation. The main areas:
- Sign-in and sessions. One-time codes sent to a mobile number, refresh sessions, a password sign-in with a second-factor challenge, and a portal switch for people who hold more than one role. Failed attempts are counted and lead to a temporary block.
- Onboarding. Creating a building, adding units, inviting managers and board members, and joining a building through an invitation.
- Accounting. Charge cycles, offline and online payments, payment links, funds, expenses, budgets, receivables and installment plans, penalty and discount rules, debtor aging, profit and loss, unit statements, and CSV and PDF report exports.
- Building operations. Tickets with SLA tracking, announcements (including scheduled ones), shared-facility reservations, documents, assets, and meetings with voting. Background services handle ticket SLAs, scheduled announcements, penalty rules and the retention of exported reports.
- Administration. Users, organizations, subscription plans, referrals, a marketplace, operations views and the audit log.
The front end is React 19 with Vite, TypeScript, MUI and TanStack Query, split into an admin portal and a building portal. Capacitor wraps the same app for Android.
Design notes
A modular monolith, one schema per area
The back end is one codebase with internal boundaries, deployed as the two API hosts. The PostgreSQL database is divided into schemas (identity, building, accounting, governance, growth, service and marketplace), each mapped by its own EF Core DbContext. Accounting goes further: it has its own domain, application and infrastructure projects, and it refers to buildings and units by id plus a stored snapshot of the name or unit number, never through an EF navigation property. That keeps a later split possible without paying for distributed transactions today. Architecture tests enforce the rules: the accounting domain references no outer layer, accounting entities do not navigate into other areas, and application code reads time through a clock abstraction.
A ledger that is never edited
Financial events post double-entry journal entries. Issuing a charge debits accounts receivable and credits charge revenue; approving a payment debits cash or bank and credits receivables; a penalty debits receivables and credits penalty revenue. A journal entry is refused unless it has at least two lines and its debits equal its credits, and once posted it does not change: a correction is a new reversing entry. Financial lists and reports filter, sort, page and aggregate in the database rather than in memory.
Audit in its own database
A middleware records every API call (actor, role, method, path, status, duration and request body) in a separate PostgreSQL database. Bodies of authentication calls are replaced with a redaction marker, and a failure to write the audit record is logged without failing the request. Kept apart, audit data grows without weighing on the main database, and administrators can filter it down to financial changes.
Redis as a helper, not a dependency
Redis holds cached dashboard data and the counters behind sign-in throttling. Every cache operation is best-effort: if Redis is unavailable, a read is treated as a miss and a write is skipped, so the API keeps working with the database alone.
Testing and CI
- About 390 xUnit test methods on the back end, including API tests through ASP.NET Core's test host and the architecture tests above.
- About 100 Vitest test files on the front end, plus custom checks that stop imports from crossing portal boundaries and keep routes and navigation in sync.
- Playwright journeys for each role, cross-portal access and the mobile right-to-left layout; a small-building scenario suite runs in CI against the full Docker Compose stack. A Locust suite load-tests the main journeys.
- GitHub Actions for back-end CI (format check, build with warnings as errors, tests), front-end CI, Docker image builds, CodeQL analysis of C# and TypeScript, and an Android debug APK build.
Stack
- .NET 10, ASP.NET Core, EF Core, MediatR, FluentValidation
- PostgreSQL 17 (main and audit databases)
- Redis 7.4
- React 19, Vite, TypeScript, MUI, TanStack Query, Capacitor
- xUnit, Vitest, Testing Library, Playwright, Locust
- Docker Compose, GitHub Actions
Status
- Hamsayeh is in active development. The whole stack (two APIs, the web app, two PostgreSQL instances and Redis) starts with Docker Compose and can seed minimal or demo data.
- Online payments run through a sandbox flow for now. The project's own list of work before production includes hardened production configuration, an outbox for payment and notification side effects, durable file storage, and stricter redaction of sensitive data in logs.
Related
The multi-tenant microservice platform takes the other route: separate services, with tenant isolation in the database. If you are planning a SaaS product on .NET, see Work with me.
Frequently asked questions
What is Hamsayeh?
Hamsayeh is Mahdi Molavi's own SaaS product for running apartment buildings and residential complexes. It covers units and residents, service charges, payments and accounting, tickets, announcements, reservations, documents and meetings, with separate portals for building users and system administrators.
Which technologies does Hamsayeh use?
The back end is .NET 10 with ASP.NET Core, EF Core and MediatR on PostgreSQL 17, with a separate PostgreSQL database for the audit log and Redis for caching. The front end is React 19 with Vite, TypeScript, MUI and TanStack Query, and the whole stack runs with Docker Compose.
How does Hamsayeh keep its financial records consistent?
Accounting is a separate module with a double-entry ledger. A journal entry needs at least two lines with equal debit and credit totals, and a posted entry is never edited; a correction is a new reversing entry.
What was Mahdi Molavi's role?
He designed the architecture and wrote most of the code, on both the back end and the front end. A few other contributors added smaller pieces, such as onboarding persistence and English localization.
Is Hamsayeh finished?
No. It is in active development. The full stack runs with Docker Compose and demo data, online payments use a sandbox flow, and the project's own list of work before production includes an outbox for side effects, durable file storage and hardened production configuration.
More in this section
- Adaptive Testing Platform: IRT-based assessments for about 89,000 students
- AI Question Ingestion: from exam PDFs to structured LaTeX items
- Delivery and Field-Service Platform: orders, couriers, technicians and stock in one system
- Exam-Peak High Availability: Redis Sentinel for 45,000 to 70,000 concurrent users
- FX Remittance Back Office for Refah Bank's Exchange House
- Financial Indicator Dashboard for MBRI
- Multi-Tenant Microservice Platform: tenancy, messaging and workflow foundations (in progress)
Mahdi Molavi