cowork-server

توسعه: شروع - به‌روزرسانی

cowork-server یک API مبتنی بر C# برای فضای کار اشتراکی با اتاق‌های جلسه است. بخش جالب پروژه CRUD اتاق‌ها نیست. جریان رزرو باید یک منبع مشترک، مجوزهای کاربر، محدودیت زمانی، تداخل‌ها و قیمت‌گذاری را مدیریت کند؛ بدون اینکه همه‌ی این منطق وارد کنترلرهای API شود.

مسئله

رزرو اتاق فقط بررسی خالی بودن یک بازه نیست. عضو تنها می‌تواند اتاق فعال را، در ساعت کاری و در بازه‌ی مجاز رزرو کند؛ بدون اینکه با رزرو دیگری تداخل داشته باشد یا از سقف روزانه عبور کند.

معماری

کدبیس به پروژه‌های Web API، Application، Domain، Infrastructure و Framework تقسیم شده است. کنترلرها درخواست را از طریق MediatR می‌فرستند. handler‌های Application با repository‌ها کار می‌کنند و قواعد رزرو را اعمال می‌کنند؛ persistence مربوط به EF Core در Infrastructure قرار دارد.

احراز هویت و مجوزدهی

ASP.NET Core Identity کاربران، ورودها، Refresh Token‌ها، کدهای OTP، نقش‌ها و claim‌ها را مدیریت می‌کند. endpoint‌های اتاق جلسه فقط برای کاربر احرازشده و فعال در دسترس هستند. command‌های رزرو، شناسه‌ی کاربر را از context کاربر احرازشده می‌گیرند، نه از body درخواست.

دامنه‌ی رزرو

مدل دامنه‌ی رزرو رابطه‌ی کاربر با نقش‌ها، مجوزها و سهمیه‌ی ماهانه و رابطه‌ی اتاق با رزرو را نشان می‌دهد؛ هر رزرو هم جزئیات خودش را دارد.

یک رزرو، اتاق، مالک، تاریخ و بازه‌ی زمانی، وضعیت، قیمت ساعتی اتاق در لحظه‌ی رزرو، billable minutes و مبلغ کل را ثبت می‌کند. مدل همچنین برای هر کاربر یک رکورد سهمیه‌ی ماهانه با دقایق مصرف‌شده و باقی‌مانده نگه می‌دارد.

قواعد کسب‌وکار

CQRS

command‌ها و query‌ها به‌صورت record‌های جداگانه تعریف شده‌اند و هرکدام handler خودشان را دارند. command‌ها از مسیر نوشتن می‌گذرند و وضعیت را تغییر می‌دهند؛ query‌ها از repository‌های خواندن استفاده می‌کنند و داده‌ی لازم برای فراخواننده را برمی‌گردانند. این جداسازی در سطح Application است و مخزن خواندن جداگانه‌ای وجود ندارد.

در Web API، برای command و query، handler و مسیر جداگانه‌ای در نظر گرفته شده و هر دو از یک پایگاه‌داده استفاده می‌کنند.

چرا CQRS حداقلی؟

رزرو یک عملیات کسب‌وکاری با قواعد مشخص است، نه یک به‌روزرسانی ساده روی برنامه‌ی اتاق‌ها. BookMeetingRoom یک نقطه برای اعتبارسنجی درخواست، شناسایی کاربر فعلی، رد تداخل‌ها، محاسبه‌ی مبلغ و ذخیره‌ی نتیجه دارد. query‌ها می‌توانند دسترس‌پذیری اتاق یا سابقه‌ی رزرو عضو را برگردانند، بی‌آنکه این جریان را دوباره بسازند.

مسیرهای command و query در همان API و همان پایگاه‌داده می‌مانند. می‌خواستم جداسازی در کد برنامه روشن باشد، بدون اینکه فقط به خاطر CQRS سرویس، پایگاه‌داده یا پیام‌رسانی جداگانه‌ای اضافه شود.

سازگاری و تراکنش‌ها

هندلر رزرو پیش از بررسی تداخل و ذخیره‌ی رزرو، از یک semaphore درون‌فرایندی با کلید اتاق و تاریخ استفاده می‌کند. این مکانیزم فقط در یک فرایند برنامه عمل می‌کند. اگر API روی چند نمونه اجرا شود، تداخل رزرو باید در سطح پایگاه‌داده اعمال شود یا با distributed lock مدیریت شود.

کدبیس برای ماندگاری از واحد کار مشترک EF Core استفاده می‌کند. همچنین یک transaction behavior در MediatR دارد که command‌های دارای رابط command تراکنشی می‌توانند از آن استفاده کنند. command فعلی رزرو، تغییرها را مستقیماً از راه همان واحد کار ذخیره می‌کند.

اعلان‌ها

کدبیس یک outbox مبتنی بر پایگاه‌داده دارد. یک worker میزبانی‌شده رویدادهای در انتظار را می‌خواند و رویدادهای اعلان یا ثبت را به پردازشگرها می‌فرستد. مدل‌های اعلان از کانال‌های ایمیل، درون‌برنامه‌ای و SMS پشتیبانی می‌کنند. خود هندلر رزرو رویداد اعلان منتشر نمی‌کند.

زیرساخت

infrastructure شامل مخزن‌های EF Core، store‌های ASP.NET Core Identity، پشتیبانی حافظه‌ی پنهان، لاگ‌نویسی ساخت‌یافته و پیکربندی مبتنی بر options است. Web API مرز برنامه است و کنترلرها فقط درخواست را به هندلر می‌رسانند.

آزمون‌ها

مجموعه‌آزمون بررسی می‌کند که هر command و query یک record و هندلر متناظر داشته باشد. این آزمون‌ها بررسی می‌کنند که handler‌های جدید هم از همین ساختار پیروی کنند.

تصمیم‌های فنی

نوشته‌های مرتبط

مشاهده‌ی کد منبع در گیت‌هاب