مقدمه
در دنیای توسعه نرمافزار با فریمورک داتنت (Microsoft .NET)، الگوی مدیاتور (Mediator Pattern) بهعنوان روشی برای پیادهسازی جداسازی مسئولیتهای فرمان و پرسوجو (Command Query Responsibility Segregation یا CQRS) شناخته شده است. این الگو به توسعهدهندگان کمک میکند تا کدهایی تمیز، قابل نگهداری و مقیاسپذیر تولید کنند. کتابخانه MediatR سالها بهعنوان ابزاری محبوب برای پیادهسازی این الگو مورد استفاده قرار گرفته است. اما اخیراً اعلام شده که MediatR به سمت مدل تجاری حرکت میکند و برخی از کاربردهای آن نیازمند لایسنس تجاری خواهد بود. این تغییر، توجه توسعهدهندگان را به جایگزینهای قدرتمندی مانند ولورین (
Wolverine) جلب کرده است.
ولورین یک کتابخانه مدرن و با کارایی بالا در اکوسیستم داتنت است که نهتنها قابلیتهای MediatR را پوشش میدهد، بلکه ویژگیهای پیشرفتهای مانند پردازش پیام توزیعشده، زمانبندی، بازآزمایی (Retries) و پشتیبانی از الگوی صندوق خروجی (Outbox) را ارائه میدهد. در این مقاله، با بررسی ویژگیهای ولورین و پیادهسازی یک API ساده برای مدیریت کاربران، به شما نشان خواهیم داد که چگونه میتوانید از این ابزار برای ساخت برنامههایی تمیز و مقیاسپذیر استفاده کنید.
چرا ولورین؟
ولورین بهعنوان جایگزینی برای MediatR طراحی شده است، اما قابلیتهای آن فراتر از یک جایگزین ساده است. این کتابخانه با بهرهگیری از مولدهای منبع (Source Generators)، عملکرد بهتری نسبت به MediatR ارائه میدهد. برخی از ویژگیهای کلیدی ولورین عبارتاند از:
- پیامرسانی داخلی و توزیعشده: پشتیبانی از پیامرسانی در حافظه (مشابه MediatR) و همچنین پیامرسانی توزیعشده با ابزارهایی مانند RabbitMQ، Kafka و Azure Service Bus.
- زمانبندی و بازآزمایی: امکان زمانبندی پیامها برای اجرا در آینده و مدیریت خودکار تلاش مجدد در صورت شکست.
- الگوی صندوق خروجی: تضمین تحویل پیامها با ذخیرهسازی موقت آنها، بدون نیاز به پیادهسازی دستی این الگو.
- مدیریت ساگا (Saga) و جریانهای کاری: پشتیبانی از هماهنگی جریانهای کاری پیچیده و بلندمدت.
- ادغام با APIهای حداقلی (Minimal APIs): یکپارچگی ساده با معماریهای مدرن داتنت.
این ویژگیها ولورین را به گزینهای ایدهآل برای پروژههای جدید یا مهاجرت از MediatR تبدیل کردهاند.
پیادهسازی یک API ساده با ولورین
برای درک بهتر نحوه کار با ولورین، یک API ساده برای مدیریت کاربران (CRUD: Create, Read, Update, Delete) پیادهسازی میکنیم. این مثال نشان میدهد که چگونه ولورین میتواند کدهایی تمیز، تستپذیر و مقیاسپذیر تولید کند.
گام ۱: تعریف مدل کاربر
ابتدا مدل کاربر را تعریف میکنیم:
public record User(Guid Id, string Name, string Email);
برای شبیهسازی ذخیرهسازی، یک لیست در حافظه ایجاد میکنیم:
public static class InMemoryUsers
{
public static readonly List<User> Users = new();
}
گام ۲: تعریف فرمانها و پرسوجوها
هر عملیات با یک شیء فرمان یا پرسوجو تعریف میشود:
public record CreateUser(string Name, string Email);
public record GetUser(Guid Id);
public record UpdateUser(Guid Id, string Name, string Email);
public record DeleteUser(Guid Id);
این اشیاء بهعنوان پیامهایی عمل میکنند که ولورین آنها را به کنترلکنندههای مناسب هدایت میکند.
گام ۳: پیادهسازی کنترلکنندهها
ولورین بهصورت خودکار کلاسهای کنترلکننده را شناسایی کرده و پیامها را بر اساس نوع و نام متد هدایت میکند. نیازی به پیادهسازی رابطهای خاص (مانند IRequest در MediatR) نیست.
کنترلکننده ایجاد کاربر:
public class CreateUserHandler
{
public User Handle(CreateUser command)
{
var user = new User(Guid.NewGuid(), command.Name, command.Email);
InMemoryUsers.Users.Add(user);
return user;
}
}کنترلکننده دریافت کاربر:
public class GetUserHandler
{
public User? Handle(GetUser query)
{
return InMemoryUsers.Users.FirstOrDefault(u => u.Id == query.Id);
}
}کنترلکننده بهروزرسانی کاربر:
public class UpdateUserHandler
{
public User? Handle(UpdateUser command)
{
var user = InMemoryUsers.Users.FirstOrDefault(u => u.Id == command.Id);
if (user is null) return null;
var updated = user with { Name = command.Name, Email = command.Email };
InMemoryUsers.Users.Remove(user);
InMemoryUsers.Users.Add(updated);
return updated;
}
}کنترلکننده حذف کاربر:
public class DeleteUserHandler
{
public bool Handle(DeleteUser command)
{
var user = InMemoryUsers.Users.FirstOrDefault(u => u.Id == command.Id);
if (user is null) return false;
InMemoryUsers.Users.Remove(user);
return true;
}
}
گام ۴: پیکربندی API حداقلی با ولورین
ولورین بهسادگی با APIهای حداقلی داتنت ادغام میشود. در این مرحله، ولورین را به برنامه اضافه کرده و نقاط پایانی (Endpoints) را تعریف میکنیم:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Host.UseWolverine(); // فعالسازی ولورین
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
// ایجاد کاربر
app.MapPost("/users", async (CreateUser request, IMessageBus bus) =>
{
var user = await bus.InvokeAsync<User>(request);
return Results.Created($"/users/{user.Id}", user);
});
// دریافت کاربر
app.MapGet("/users/{id:guid}", async (Guid id, IMessageBus bus) =>
{
var user = await bus.InvokeAsync<User?>(new GetUser(id));
return user is null ? Results.NotFound() : Results.Ok(user);
});
// بهروزرسانی کاربر
app.MapPut("/users/{id:guid}", async (Guid id, UpdateUser command, IMessageBus bus) =>
{
if (id != command.Id) return Results.BadRequest();
var updated = await bus.InvokeAsync<User?>(command);
return updated is null ? Results.NotFound() : Results.Ok(updated);
});
// حذف کاربر
app.MapDelete("/users/{id:guid}", async (Guid id, IMessageBus bus) =>
{
var deleted = await bus.InvokeAsync<bool>(new DeleteUser(id));
return deleted ? Results.NoContent() : Results.NotFound();
});
app.Run();در اینجا، IMessageBus بهعنوان واسطی برای ارسال پیامها به ولورین عمل میکند. متد InvokeAsync پیام را به کنترلکننده مناسب ارسال کرده و نتیجه را برمیگرداند. این فرآیند بهصورت زیر انجام میشود:
- ولورین پیام (مانند
CreateUser) را دریافت میکند. - کنترلکننده مناسب (مانند
Handle در CreateUserHandler) را پیدا میکند. - متد را اجرا کرده و نتیجه را بازمیگرداند.
مزایای ولورین نسبت به MediatR
بر اساس اطلاعات موجود و تحلیلهای انجامشده، ولورین ویژگیهایی ارائه میدهد که آن را از MediatR متمایز میکند:
- عملکرد بهتر: استفاده از مولدهای منبع بهجای بازتاب (Reflection) باعث افزایش سرعت اجرا میشود.
- پیامرسانی پیشرفته: پشتیبانی از پیامرسانی توزیعشده، بر خلاف MediatR که تنها پیامرسانی در حافظه را پشتیبانی میکند.
- پشتیبانی از قابلیتهای مدرن: امکاناتی مانند زمانبندی، بازآزمایی و الگوی صندوق خروجی، نیاز به پیادهسازی دستی این قابلیتها را از بین میبرند.
- سادگی در استفاده: عدم نیاز به رابطهای پیچیده و شناسایی خودکار کنترلکنندهها.
احترام به MediatR
هرچند ولورین یک جایگزین قدرتمند است، اما باید به ارزشهای MediatR نیز احترام گذاشت. این کتابخانه سالها بهصورت رایگان به جامعه داتنت خدمت کرده و نسخههای موجود آن همچنان متنباز باقی خواهند ماند. حرکت به سمت مدل تجاری، به پایداری و ادامه توسعه این ابزار کمک میکند. توسعهدهندگان میتوانند بر اساس نیازهای پروژه خود بین MediatR و ولورین انتخاب کنند.
نتیجهگیری
ولورین بهعنوان یک جایگزین مدرن و قدرتمند برای MediatR، ابزارهایی پیشرفته برای پیادهسازی الگوی CQRS و مدیریت پیامها در برنامههای داتنت ارائه میدهد. این کتابخانه با ویژگیهایی مانند پیامرسانی توزیعشده، زمانبندی، و پشتیبانی از الگوی صندوق خروجی، گزینهای ایدهآل برای پروژههای جدید و معماریهای مدرن است. پیادهسازی مثال API مدیریت کاربران نشان داد که ولورین چگونه میتواند کدهایی تمیز، تستپذیر و مقیاسپذیر تولید کند. اگر در حال بررسی گزینههای جایگزین برای MediatR هستید یا میخواهید از قابلیتهای پیشرفتهتر در پروژههای خود بهره ببرید، ولورین ارزش بررسی دقیق را دارد. در عین حال، انتخاب بین این دو ابزار به نیازهای خاص پروژه و ترجیحات تیم توسعه بستگی دارد.