CQRS حداقلی: مسیرهای کد را جدا کنید، یک برنامه و یک دیتابیس نگه دارید
· 4 دقیقه مطالعهمهندسی معماری نرمافزار #architecture #cqrs #cqs #dotnet #csharp
در نوشتهی اول دیدیم که یک DTO تغییرکرده چطور میتواند یک عمل کسبوکار را پنهان کند.1 در نوشتهی دوم، آن عمل را با commandها صریح کردیم.2
گام بعدی به سرویس جدید، queue یا دیتابیس دوم نیاز ندارد. یک برنامهی قابل استقرار و یک دیتابیس را نگه دارید و مسیرهای کد را بر اساس کاری که انجام میدهند جدا کنید.
یک برنامه، دو مسئولیت
command وضعیت سیستم را تغییر میدهد. دادهی لازم برای اجرای قانون را میخواند، معتبر بودن درخواست را بررسی میکند و نتیجه را ذخیره میکند.
query به یک سؤال پاسخ میدهد. داده را در شکلی برمیگرداند که فراخواننده لازم دارد و نباید فقط برای ساختن یک صفحه، قواعد مسیر command را اجرا کند.
هر دو مسیر از یک دیتابیس استفاده میکنند و با هم منتشر میشوند. تفاوتشان در کد است، چون کار متفاوتی انجام میدهند. راهنمای CQRS مایکروسافت این الگو را جداسازی مدلهای خواندن و نوشتن میداند و در عین حال میگوید استفاده از یک مخزن دادهی مشترک میتواند همچنان انتخاب مناسبی باشد.[2]
مسیر command از تغییر محافظت میکند
فرض کنید باید سفارشی ثبت شود. درخواست، یک command است، نه یک موجودیت سفارش که ویرایش شده باشد:
public sealed record PlaceOrder(Guid CustomerId, IReadOnlyList<OrderLine> Lines);
handler، مشتری و اقلام لازم را میخواند، موجودی و قوانین ثبت سفارش را بررسی میکند، سفارش را میسازد و تراکنش را ثبت میکند. ممکن است command رد شود، چون درخواست مجاز نیست. این بخشی از قرارداد API است.
مدل نوشتن لازم نیست همهی فیلدهایی را که یک داشبورد نمایش میدهد داشته باشد. فقط باید آنقدر رفتار و وضعیت داشته باشد که بتواند دربارهی معتبر بودن PlaceOrder تصمیم بگیرد.
مسیر query پاسخ را شکل میدهد
فهرست سفارش سؤال دیگری میپرسد:
public sealed record OrderSummaryDto(
Guid Id,
string Number,
string CustomerName,
decimal Total,
string Status);
GetOrderSummary میتواند داده را مستقیماً از همان دیتابیس به این DTO نگاشت کند. لازم نیست یک aggregate از نوع Order را دوباره بسازد، قوانین PlaceOrder را اجرا کند یا فیلدهایی را برگرداند که صفحهی فهرست کنار میگذارد.
دیتابیس مشترک است، اما لازم نیست مدلها همشکل باشند. گرگ یانگ CQRS را جداسازی مسئولیتهای خواندن و نوشتن میداند؛ مدلها هم فقط هرجا مسئولیت به آن نیاز دارد متفاوت میشوند.[1]
CQS و CQRS دو مرز متفاوتاند
| CQS | CQRS حداقلی | |
|---|---|---|
| مرز اصلی | یک عملیات یا وضعیت را تغییر میدهد یا اطلاعات برمیگرداند. | کد command و query مسیرهای جدا دارند. |
| محدوده | یک متد یا عملیات API. | ساختار برنامه پیرامون این عملیات. |
| دیتابیس | تغییری نمیکند. | همچنان یک دیتابیس است. |
| نمونه | PlaceOrder و GetOrderSummary قراردادهای متفاوت دارند. |
handlerها و مدلهای جدا این درخواستها را پردازش میکنند. |
CQS فهم هر عملیات را سادهتر میکند. CQRS این جداسازی را در ساختار برنامه نشان میدهد. یک سیستم میتواند CQS داشته باشد، بدون اینکه کدش را بازآرایی کند؛ همچنین میتواند این مرز حداقلی CQRS را بدون زیرساخت توزیعشده اضافه کند.
این کار چه چیزی نمیخرد
یک برنامه و یک دیتابیس، چند ویژگی مفید را حفظ میکنند:
- command و دادهای که تغییر میدهد میتوانند در یک تراکنش باشند.
- query که بلافاصله پس از command موفق اجرا میشود، وضعیت جدید را میخواند.
- فقط یک استقرار و یک نقطه برای بررسی مشکلهای ذخیرهسازی داریم.
اما این طراحی، مقیاسپذیری مستقل مسیر خواندن، projectionهای نرمالنشده برای گزارش یا جداسازی از بار زیاد نوشتن را ایجاد نمیکند. هر کدام از اینها هزینه و تصمیم جداگانهای هستند. نوشتهی بعدی بررسی میکند وقتی یک تیم مدلها یا مخزنهای داده را بیشتر جدا میکند، چه چیزهایی تغییر میکند.
از مرزی شروع کنید که واقعاً لازم دارید
وقتی مسیر نوشتن قواعد معنادار دارد یا مسیر خواندن به شکلی از داده نیاز دارد که جای آن در مدل نوشتن نیست، مسیر command و query را جدا کنید. تا وقتی مسئلهی واقعی به جداسازی بیشتر نیاز ندارد، برنامه و دیتابیس را کنار هم نگه دارید.