«همسایه»: سامانهٔ ابری مدیریت ساختمان و مجتمع مسکونی

«همسایه» محصول خودم است: یک سامانهٔ SaaS برای ادارهٔ ساختمان‌های آپارتمانی و مجتمع‌های مسکونی. مدیر ساختمان،‏ اعضای هیئت‌مدیره،‏ مالکان و ساکنان شارژ،‏ پرداخت،‏ تیکت و اطلاعیه را در آن مدیریت می‌کنند و مدیران سیستم پرتال جداگانه‌ای دارند. معماری آن را طراحی کردم و بیشتر کدش را،‏ هم در بک‌اند و هم در فرانت‌اند،‏ خودم نوشتم؛‏ چند همکار دیگر بخش‌های کوچک‌تری مثل ذخیره‌سازی اطلاعات در فرایند ثبت ساختمان و بومی‌سازی انگلیسی را اضافه کردند.

زمینه

در بسیاری از ساختمان‌ها شارژ ماهانه در یک صفحه‌گسترده یا دفترچه ثبت می‌شود،‏ پرداخت‌ها با پیام تأیید می‌شوند و اطلاعیه‌ها به یک گروه پیام‌رسان فرستاده می‌شوند. «همسایه» این‌ها را با یک سامانهٔ واحد جایگزین می‌کند که در آن هر مبلغ قابل پیگیری است،‏ هر ساکن مانده‌حساب و درخواست‌های خودش را می‌بیند و مدیر کمتر دستی حساب‌وکتاب می‌کند. کار در فازهای محصولی برنامه‌ریزی شده است: از MVP پایه (ورود،‏ ساختمان،‏ شارژ و پرداخت) تا مالی،‏ خدمات،‏ جلسه‌ها و رأی‌گیری،‏ و رشد.

مسئلهٔ مهندسی

اپلیکیشن ساختمان از بیرون ساده به نظر می‌رسد. چهار چیز کار را سخت‌تر می‌کند:

  • نقش به ساختمان بستگی دارد. یک نفر می‌تواند در سطح سیستم کاربر عادی باشد،‏ در یک ساختمان مدیر و در ساختمان دیگری مستأجر. نقش‌های سیستمی و نقش‌های ساختمانی (مدیر،‏ کمک‌مدیر،‏ مالک،‏ مستأجر،‏ ساکن) باید از هم جدا بمانند و هر درخواست باید در برابر همان ساختمانی که به آن مربوط است بررسی شود.
  • حساب‌ها باید جور دربیایند. شارژ،‏ پرداخت،‏ جریمه،‏ تخفیف،‏ اقساط،‏ صندوق‌ها،‏ هزینه‌ها و بودجه همه روی بدهی یک واحد اثر می‌گذارند. اگر عددی قابل توضیح نباشد،‏ ساکنان به کل سامانه بی‌اعتماد می‌شوند.
  • فهرست قابلیت‌ها طولانی است. حسابداری،‏ تیکت،‏ رزرو،‏ رأی‌گیری،‏ اشتراک و بازارچه هم‌زمان رشد می‌کنند. بدون مرزهای محکم،‏ همه در یک مدل دادهٔ واحد درهم می‌روند که هیچ‌کس نمی‌تواند با خیال راحت تغییرش دهد.
  • بیشتر کاربران با گوشی کار می‌کنند. رابط کاربری از اول فارسی و راست‌به‌چپ است و باید روی گوشی به همان خوبیِ دسکتاپ کار کند.

کاری که انجام دادم

بک‌اند دو میزبان API با ASP.NET Core دارد،‏ یکی برای کاربران ساختمان و یکی برای مدیریت سیستم. هر دو روی یک لایهٔ اپلیکیشن مشترک از commandها و queryهای MediatR بنا شده‌اند که با FluentValidation اعتبارسنجی می‌شوند. بخش‌های اصلی:

  • ورود و نشست. کد یک‌بارمصرف که به شمارهٔ موبایل فرستاده می‌شود،‏ نشست‌های قابل تمدید،‏ ورود با رمز عبور همراه با عامل دوم،‏ و جابه‌جایی بین پرتال‌ها برای کسی که بیش از یک نقش دارد. تلاش‌های ناموفق شمرده می‌شوند و به مسدودشدن موقت می‌رسند.
  • راه‌اندازی ساختمان. ساختن ساختمان،‏ افزودن واحدها،‏ دعوت از مدیر و اعضای هیئت‌مدیره،‏ و پیوستن به ساختمان با دعوت‌نامه.
  • حسابداری. دوره‌های شارژ،‏ پرداخت آفلاین و آنلاین،‏ لینک پرداخت،‏ صندوق‌ها،‏ هزینه‌ها،‏ بودجه،‏ مطالبات و طرح‌های اقساط،‏ قاعده‌های جریمه و تخفیف،‏ گزارش سن بدهی،‏ سود و زیان،‏ صورت‌حساب واحد،‏ و خروجی گزارش‌ها به CSV و PDF.
  • عملیات ساختمان. تیکت با پیگیری SLA،‏ اطلاعیه (از جمله اطلاعیهٔ زمان‌بندی‌شده)،‏ رزرو مشاعات،‏ اسناد،‏ دارایی‌ها،‏ و جلسه با رأی‌گیری. سرویس‌های پس‌زمینه کار SLA تیکت‌ها،‏ اطلاعیه‌های زمان‌بندی‌شده،‏ قاعده‌های جریمه و مدت نگه‌داری خروجی گزارش‌ها را انجام می‌دهند.
  • مدیریت سیستم. کاربران،‏ سازمان‌ها،‏ پلن‌های اشتراک،‏ معرفی کاربر (referral)،‏ بازارچه،‏ نماهای عملیاتی و لاگ ممیزی.

فرانت‌اند با React 19،‏ Vite،‏ TypeScript،‏ MUI و TanStack Query ساخته شده و به دو پرتال مدیریت و ساختمان تقسیم شده است. Capacitor همین اپلیکیشن را برای اندروید بسته‌بندی می‌کند.

نکته‌های طراحی

مونولیت ماژولار،‏ یک schema برای هر حوزه

بک‌اند یک کدبیس با مرزهای داخلی است که به شکل همان دو میزبان API مستقر می‌شود. پایگاه‌دادهٔ PostgreSQL به چند schema تقسیم شده است (identity،‏ building،‏ accounting،‏ governance،‏ growth،‏ service و marketplace) و هرکدام DbContext خودش را در EF Core دارد. حسابداری یک قدم جلوتر می‌رود: پروژه‌های domain،‏ application و infrastructure جداگانهٔ خودش را دارد و به ساختمان‌ها و واحدها فقط با شناسه و یک نسخهٔ ذخیره‌شده (snapshot) از نام یا شمارهٔ واحد ارجاع می‌دهد،‏ نه با navigation property در EF. این کار جدا کردن این بخش را در آینده ممکن نگه می‌دارد،‏ بی‌آنکه امروز بهای تراکنش‌های توزیع‌شده پرداخت شود. تست‌های معماری این قاعده‌ها را اجباری می‌کنند: domain حسابداری به هیچ لایهٔ بیرونی ارجاع نمی‌دهد،‏ موجودیت‌های حسابداری به حوزه‌های دیگر navigation ندارند و کد لایهٔ اپلیکیشن زمان را از یک انتزاع ساعت (clock) می‌خواند.

دفتر کلی که ویرایش نمی‌شود

رویدادهای مالی سند حسابداری دوطرفه ثبت می‌کنند. صدور شارژ،‏ حساب‌های دریافتنی را بدهکار و درآمد شارژ را بستانکار می‌کند؛‏ تأیید پرداخت،‏ صندوق یا بانک را بدهکار و حساب‌های دریافتنی را بستانکار می‌کند؛‏ جریمه،‏ حساب‌های دریافتنی را بدهکار و درآمد جریمه را بستانکار می‌کند. سندی که کمتر از دو سطر داشته باشد یا جمع بدهکار و بستانکارش برابر نباشد پذیرفته نمی‌شود و سند ثبت‌شده دیگر تغییر نمی‌کند: اصلاح،‏ یک سند معکوس تازه است. فهرست‌ها و گزارش‌های مالی فیلتر،‏ مرتب‌سازی،‏ صفحه‌بندی و تجمیع را در خود پایگاه‌داده انجام می‌دهند،‏ نه در حافظه.

ممیزی در پایگاه‌دادهٔ جداگانه

یک middleware هر فراخوانی API را (کاربر،‏ نقش،‏ متد،‏ مسیر،‏ کد وضعیت،‏ مدت اجرا و بدنهٔ درخواست) در یک پایگاه‌دادهٔ PostgreSQL جداگانه ثبت می‌کند. بدنهٔ درخواست‌های احراز هویت با یک نشانهٔ حذف جایگزین می‌شود و اگر نوشتن رکورد ممیزی شکست بخورد،‏ خطا فقط لاگ می‌شود و خود درخواست شکست نمی‌خورد. وقتی دادهٔ ممیزی جدا باشد،‏ رشدش پایگاه‌دادهٔ اصلی را سنگین نمی‌کند و مدیران سیستم می‌توانند آن را فقط به تغییرات مالی محدود کنند.

Redis کمک‌کار است،‏ نه وابستگی

Redis داده‌های کش‌شدهٔ داشبورد و شمارنده‌های محدودسازی ورود را نگه می‌دارد. همهٔ عملیات کش «در حد امکان» (best-effort) انجام می‌شوند: اگر Redis در دسترس نباشد،‏ خواندن مثل نبودِ داده در کش حساب می‌شود و نوشتن نادیده گرفته می‌شود،‏ پس API فقط با پایگاه‌داده هم به کارش ادامه می‌دهد.

تست و CI

  • حدود ۳۹۰ متد تست xUnit در بک‌اند،‏ از جمله تست‌های API با میزبان تست ASP.NET Core و تست‌های معماری که بالاتر گفتم.
  • حدود ۱۰۰ فایل تست Vitest در فرانت‌اند،‏ به‌علاوهٔ بررسی‌های اختصاصی که نمی‌گذارند importها از مرز پرتال‌ها عبور کنند و مسیرها و منوی ناوبری را هماهنگ نگه می‌دارند.
  • سناریوهای Playwright برای هر نقش،‏ دسترسی بین پرتال‌ها و چیدمان راست‌به‌چپ موبایل؛‏ یک مجموعه سناریوی «ساختمان کوچک» در CI روی کل پشتهٔ Docker Compose اجرا می‌شود. یک مجموعهٔ Locust هم مسیرهای اصلی را زیر بار می‌برد.
  • GitHub Actions برای CI بک‌اند (بررسی قالب‌بندی،‏ build با تبدیل هشدارها به خطا،‏ تست)،‏ CI فرانت‌اند،‏ ساخت imageهای Docker،‏ تحلیل CodeQL روی C# و TypeScript و ساخت APK آزمایشی (debug) اندروید.

فناوری‌ها

  • ‎.NET 10،‏ ASP.NET Core،‏ EF Core،‏ MediatR،‏ FluentValidation
  • PostgreSQL 17 (پایگاه‌دادهٔ اصلی و پایگاه‌دادهٔ ممیزی)
  • Redis 7.4
  • React 19،‏ Vite،‏ TypeScript،‏ MUI،‏ TanStack Query،‏ Capacitor
  • xUnit،‏ Vitest،‏ Testing Library،‏ Playwright،‏ Locust
  • Docker Compose،‏ GitHub Actions

وضعیت

  • «همسایه» در حال توسعهٔ فعال است. کل پشته (دو API،‏ اپلیکیشن وب،‏ دو نمونهٔ PostgreSQL و Redis) با Docker Compose بالا می‌آید و می‌تواند دادهٔ حداقلی یا دادهٔ نمایشی بسازد.
  • پرداخت آنلاین فعلاً از یک مسیر آزمایشی (sandbox) می‌گذرد. فهرست کارهای پیش از production که خود پروژه نوشته،‏ شامل سخت‌گیرانه‌تر کردن پیکربندی production،‏ یک outbox برای اثرهای جانبی پرداخت و اعلان،‏ ذخیره‌سازی پایدار فایل‌ها و پنهان‌سازی دقیق‌تر داده‌های حساس در لاگ‌هاست.

مطالب مرتبط

پلتفرم میکروسرویسی چندمستأجری راه دیگر را می‌رود: سرویس‌های جدا،‏ با جداسازی مستأجرها در خود پایگاه‌داده. اگر یک محصول SaaS روی ‎.NET در دست برنامه‌ریزی دارید،‏ صفحهٔ همکاری را ببینید.

پرسش‌های رایج

«همسایه» چیست؟

«همسایه» محصول SaaS خود مهدی مولوی برای ادارهٔ ساختمان‌های آپارتمانی و مجتمع‌های مسکونی است. واحدها و ساکنان،‏ شارژ،‏ پرداخت و حسابداری،‏ تیکت،‏ اطلاعیه،‏ رزرو،‏ اسناد و جلسه‌ها را پوشش می‌دهد و برای کاربران ساختمان و مدیران سیستم پرتال‌های جداگانه دارد.

«همسایه» با چه فناوری‌هایی ساخته شده است؟

بک‌اند آن ‎.NET 10 با ASP.NET Core،‏ EF Core و MediatR روی PostgreSQL 17 است،‏ با یک پایگاه‌دادهٔ PostgreSQL جداگانه برای لاگ ممیزی و Redis برای کش. فرانت‌اند React 19 با Vite،‏ TypeScript،‏ MUI و TanStack Query است و کل پشته با Docker Compose اجرا می‌شود.

«همسایه» چطور سوابق مالی را سازگار نگه می‌دارد؟

حسابداری یک ماژول جداگانه با دفتر کل دوطرفه است. هر سند حسابداری دست‌کم دو سطر با جمع بدهکار و بستانکار برابر لازم دارد و سند ثبت‌شده هرگز ویرایش نمی‌شود؛‏ اصلاح،‏ یک سند معکوس تازه است.

نقش مهدی مولوی در این پروژه چه بود؟

معماری را طراحی کرد و بیشتر کد را،‏ هم در بک‌اند و هم در فرانت‌اند،‏ نوشت. چند همکار دیگر بخش‌های کوچک‌تری مثل ذخیره‌سازی اطلاعات در فرایند ثبت ساختمان و بومی‌سازی انگلیسی را اضافه کردند.

آیا «همسایه» کامل شده است؟

نه. در حال توسعهٔ فعال است. کل پشته با Docker Compose و دادهٔ نمایشی اجرا می‌شود،‏ پرداخت آنلاین از مسیر آزمایشی (sandbox) می‌گذرد و فهرست کارهای پیش از production در خود پروژه شامل outbox برای اثرهای جانبی،‏ ذخیره‌سازی پایدار فایل‌ها و سخت‌گیرانه‌تر کردن پیکربندی production است.

در همین بخش

نمونه‌کارها