کد را به من نشان بده 👨💻
من ابتدا بر اساس دامنه (Users) و سپس بر اساس ویژگی (ExportData) سازماندهی میکنم. 📂 برخی تیمها مستقیماً Features/ExportUserData را ترجیح میدهند، اما من متوجه شدهام که گروهبندی دامنه زمانی که ویژگیهای زیادی دارید، کمک میکند. ویژگیهای مرتبط به صورت بصری گروهبندی شده باقی میمانند.
در اینجا ظاهر برش ویژگی (feature slice) خروجی داده ما با استفاده از یک request، handler و minimal APIها آمده است: 👇
Features/Users/ExportData/ExportUserData.cs
public static class ExportUserData
{
public record Request(Guid UserId) : IRequest<Response>;
public record Response(string DownloadUrl, DateTime ExpiresAt);
public class Handler(
AppDbContext dbContext,
ICloudStorageClient storageClient,
IEmailSender emailSender)
: IRequestHandler<Request, Response>
{
public async Task<Response> Handle(Request request, CancellationToken ct = default)
{
// Get user data
var user = await dbContext.Users
.Include(u => u.Orders)
.Include(u => u.Preferences)
.FirstOrDefaultAsync(u => u.Id == request.UserId, ct);
if (user == null)
{
throw new NotFoundException($"User {request.UserId} not found");
}
// Generate export data
var exportData = new
{
user.Email,
user.Name,
user.CreatedAt,
Orders = user.Orders.Select(o => new { o.Id, o.Total, o.Date }),
Preferences = user.Preferences
};
// Upload to cloud storage
var fileName = $"user-data-{user.Id}-{DateTime.UtcNow:yyyyMMdd}.json";
var expiresAtUtc = DateTime.UtcNow.AddDays(7);
var downloadUrl = await storageClient.UploadAsJsonAsync(
fileName,
exportData,
expiresAtUtc,
ct);
// Send email notification
await emailSender.SendDataExportEmailAsync(user.Email, downloadUrl, ct);
return new Response(downloadUrl, expiresAtUtc);
}
}
// Simple validation using FluentValidation
public sealed class Validator : AbstractValidator<Request>
{
public Validator()
{
RuleFor(r => r.UserId).NotEmpty();
}
}
}
همه چیز مربوط به خروجی گرفتن از دادههای کاربر در یک مکان قرار دارد: کوئری دیتابیس، اعتبارسنجی، منطق بیزینس، یکپارچهسازی با فضای ذخیرهسازی ابری، و نوتیفیکیشن ایمیل.
endpoint Minimal API سرراست است: 👇
public static class ExportUserDataEndpoint
{
public static void Map(IEndpointRouteBuilder app)
{
app.MapPost("/users/{userId}/export", async (
Guid userId,
IRequestHandler<ExportUserData.Request, ExportUserData.Response> handler) =>
{
var response = await handler.Handle(new ExportUserData.Request(userId));
return Results.Ok(response);
});
}
}
ما حتی میتوانستیم endpoint را داخل فایل ExportUserData.cs تعریف کنیم اگر میخواستیم همه چیز را کنار هم نگه داریم. این بیشتر یک موضوع ترجیح و قراردادهای تیمی است. هر دو رویکرد به خوبی کار میکنند. 👍