درک الگوی Result 🤔
الگوی Result یک الگوی طراحی است که نتیجه یک عملیات را کپسوله میکند، که میتواند یا موفقیتآمیز باشد یا یک شکست. به جای پرتاب استثناها، متدها یک آبجکت Result را برمیگردانند که نشان میدهد آیا عملیات موفق شده یا شکست خورده است، همراه با هرگونه داده یا پیام خطای مرتبط.
آبجکت Result از بخشهای زیر تشکیل شده است:
🔹 IsSuccess/IsError:
یک مقدار بولین که نشان میدهد آیا عملیات موفقیتآمیز بوده است یا نه.
🔹 Value:
مقدار نتیجه زمانی که عملیات موفقیتآمیز است.
🔹 Error:
یک پیام یا آبجکت خطا زمانی که عملیات شکست میخورد.
بیایید یک مثال ساده از نحوه پیادهسازی یک آبجکت Result را بررسی کنیم: 🎁
public class Result<T>
{
public bool IsSuccess { get; }
public bool IsFailure => !IsSuccess;
public T? Value { get; }
public string? Error { get; }
private Result(bool isSuccess, T? value, string? error)
{
IsSuccess = isSuccess;
Value = value;
Error = error;
}
public static Result<T> Success(T value)
{
return new Result<T>(true, value, null);
}
public static Result<T> Failure(string error)
{
return new Result<T>(false, default(T), error);
}
}
در اینجا ما یک کلاس جنریک Result تعریف کردهایم که میتواند یا یک مقدار موفقیتآمیز یا یک خطا را در خود نگه دارد. برای ایجاد آبجکت Result میتوانید از متد استاتیک Success یا Failure استفاده کنید.
بیایید پیادهسازی endpoint قبلی Create Shipment را با الگوی Result خود بازنویسی کنیم: ✅
public async Task<Result<ShipmentResponse>> CreateAsync(
CreateShipmentCommand request,
CancellationToken cancellationToken)
{
var shipmentAlreadyExists = await context.Shipments
.Where(s => s.OrderId == request.OrderId)
.AnyAsync(cancellationToken);
if (shipmentAlreadyExists)
{
return Result.Failure<ShipmentResponse>(
$"Shipment for order '{request.OrderId}' is already created");
}
var shipment = request.MapToShipment(shipmentNumber);
context.Shipments.Add(shipiment);
await context.SaveChangesAsync(cancellationToken);
var response = shipment.MapToResponse();
return Result.Success(response);
}
در اینجا متد <Result<ShipmentResponse را برمیگرداند که ShipmentResponse را داخل یک کلاس Result میپیچد.
وقتی یک shipment از قبل در دیتابیس وجود داشته باشد، ما Result.Failure را با یک پیام متناظر برمیگردانیم. وقتی یک درخواست موفق میشود، ما Result.Success را برمیگردانیم.
در اینجا نحوه مدیریت یک آبجکت Result در endpoint آمده است: 👇
public void MapEndpoint(WebApplication app)
{
app.MapPost("/api/v1/shipments", Handle);
}
private static async Task<IResult> Handle(
[FromBody] CreateShipmentRequest request,
IShipmentService service,
CancellationToken cancellationToken)
{
var command = request.MapToCommand();
var response = await service.CreateAsync(command, cancellationToken);
return response.IsSuccess ? Results.Ok(response.Value) : Results.Conflict(response.Error);
}
شما باید بررسی کنید که آیا پاسخ موفقیتآمیز است یا شکست خورده و یک نتیجه HTTP مناسب را برگردانید. حالا کد قابل پیشبینیتر به نظر میرسد و راحتتر خوانده میشود، درست است؟ 😉