مكتبات الخلفية والمعمارية — نظام إدارة المغسلة
معلومات الوثيقة
| الحقل |
القيمة |
| المشروع |
نظام إدارة المغسلة |
| الإصدار |
1.0 |
| اللغة |
العربية |
| الإطار |
ASP.NET Core 10 + .NET 10 |
| نوع الوثيقة |
دليل مكتبات الخلفية للمطورين |
1. هيكل الحل
backend/
├── Laundry.sln
├── Laundry.Api/ # المضيف — Controllers, Middleware, wwwroot
├── Laundry.Application/ # حالات الاستخدام — CQRS, DTOs, Validators
├── Laundry.Domain/ # الكيانات، القيم، الواجهات
├── Laundry.Infrastructure/ # EF Core، المستودعات، المحاسبة، المزامنة، الترخيص
└── Laundry.Tests/ # xUnit — Unit + Integration
2. قائمة حزم NuGet الكاملة
Laundry.Api
| الحزمة |
الغرض |
Microsoft.AspNetCore.Authentication.JwtBearer |
مصادقة JWT |
Asp.Versioning.Mvc |
تعيين إصدارات API |
Swashbuckle.AspNetCore |
Swagger/OpenAPI |
Serilog.AspNetCore |
تسجيل منظم |
Serilog.Sinks.Seq |
تسجيل إلى Seq |
Serilog.Sinks.PostgreSQL |
تسجيل إلى قاعدة البيانات |
OpenTelemetry.Extensions.Hosting |
تتبع موزع |
OpenTelemetry.Instrumentation.AspNetCore |
تتبع HTTP |
AspNetCore.HealthChecks.UI |
لوحة فحوصات الصحة |
AspNetCore.HealthChecks.UI.Client |
عميل لوحة الصحة |
AspNetCore.HealthChecks.Npgsql |
فحص PostgreSQL |
AspNetCore.HealthChecks.Uris |
فحص Traefik/Seq |
Laundry.Application
| الحزمة |
الغرض |
MediatR |
نمط CQRS |
FluentValidation.AspNetCore |
التحقق من المدخلات |
AutoMapper |
تحويل DTO |
Laundry.Domain
| الحزمة |
الغرض |
MediatR.Contracts |
عقود أحداث النطاق |
Laundry.Infrastructure
| الحزمة |
الغرض |
Npgsql.EntityFrameworkCore.PostgreSQL |
موفر PostgreSQL |
Microsoft.EntityFrameworkCore.Sqlite |
SQLite للفروع غير المتصلة |
Microsoft.EntityFrameworkCore.Design |
أدوات CLI |
Dapper |
SQL خام للتقارير |
Quartz.Extensions.Hosting |
مهام خلفية |
BCrypt.Net-Next |
تشفير كلمات المرور |
OpenTelemetry.Instrumentation.EntityFrameworkCore |
تتبع استعلامات EF |
Laundry.Tests
| الحزمة |
الغرض |
xunit |
إطار الاختبار |
Moq |
المحاكاة |
FluentAssertions |
تأكيدات قابلة للقراءة |
Testcontainers.PostgreSql |
قاعدة بيانات للاختبارات التكاملية |
Microsoft.AspNetCore.Mvc.Testing |
اختبارات تكاملية لـ Web API |
coverlet.collector |
تغطية الكود |
3. طبقات Clean Architecture
Laundry.Api → مراجع Application + Infrastructure
↓
Laundry.Application → مراجع Domain فقط
↓
Laundry.Domain ← لا يرجع شيئاً
↓
Laundry.Infrastructure → يرجع Domain + Application
القاعدة: الاعتماديات تتجه للداخل. Domain نقي تماماً.
4. حقن الاعتماديات
builder.Services.AddScoped<IInvoiceRepository, InvoiceRepository>();
builder.Services.AddScoped<IAccountingRulesEngine, AccountingRulesEngine>();
builder.Services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(ApplicationAssembly).Assembly));
builder.Services.AddValidatorsFromAssembly(typeof(ApplicationAssembly).Assembly);
builder.Services.AddAutoMapper(typeof(ApplicationAssembly).Assembly);
// سلوكيات MediatR (تنفذ بالترتيب):
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(TransactionBehavior<,>));
5. EF Core — Code-First
DbContext
public class LaundryDbContext : DbContext
{
public DbSet<Invoice> Invoices => Set<Invoice>();
public DbSet<JournalEntryLine> JournalEntryLines => Set<JournalEntryLine>();
// ...
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfigurationsFromAssembly(typeof(LaundryDbContext).Assembly);
// منع التوليد التلقائي لـ UUID (يولدها التطبيق)
foreach (var entity in builder.Model.GetEntityTypes())
{
if (entity.ClrType.GetProperty("Id")?.PropertyType == typeof(Guid))
entity.FindProperty("Id")!.ValueGenerated = ValueGenerated.Never;
}
}
}
تكوين كيان (مثال)
builder.Property(x => x.GrandTotal).HasColumnType("numeric(18,4)"); // مال
builder.Property(x => x.OperationalStatus).HasConversion<string>().HasMaxLength(20);
builder.HasIndex(x => x.InvoiceNumber).IsUnique();
الترحيلات
dotnet ef migrations add InitialCreate -p Laundry.Infrastructure -s Laundry.Api
dotnet ef database update
أمر
public record CreateInvoiceCommand(...) : IRequest<Result<InvoiceDto>>;
معالج
public class CreateInvoiceHandler : IRequestHandler<CreateInvoiceCommand, Result<InvoiceDto>>
{
public async Task<Result<InvoiceDto>> Handle(CreateInvoiceCommand req, CancellationToken ct)
{
var invoice = Invoice.Create(req.CustomerId, req.BranchId, req.Items);
await _repo.AddAsync(invoice, ct);
var entries = _engine.Evaluate("InvoiceCreated", invoice);
await _uow.AddJournalEntriesAsync(entries, ct);
await _uow.SaveChangesAsync(ct);
return Result<InvoiceDto>.Success(_mapper.Map<InvoiceDto>(invoice));
}
}
استعلام (Dapper للتقارير)
var balance = await _db.QuerySingleOrDefaultAsync<decimal>(@"
SELECT COALESCE(SUM(debit),0) - COALESCE(SUM(credit),0)
FROM journal_entry_lines WHERE customer_id = @Id", new { Id });
7. FluentValidation
public class CreateInvoiceValidator : AbstractValidator<CreateInvoiceCommand>
{
public CreateInvoiceValidator()
{
RuleFor(x => x.CustomerId).NotEmpty();
RuleFor(x => x.Items).NotEmpty().Must(x => x.Count > 0);
RuleForEach(x => x.Items).SetValidator(new InvoiceItemValidator());
}
}
يتم التحقق تلقائياً قبل المعالج عبر سلوك MediatR ValidationBehavior.
8. Serilog + OpenTelemetry + Seq
builder.Host.UseSerilog((ctx, cfg) => cfg
.ReadFrom.Configuration(ctx.Configuration)
.WriteTo.Console()
.WriteTo.Seq(ctx.Configuration["SeqServerUrl"]!));
builder.Services.AddOpenTelemetry()
.WithTracing(t => t
.AddAspNetCoreInstrumentation()
.AddEntityFrameworkCoreInstrumentation()
.AddOtlpExporter())
.WithMetrics(m => m
.AddAspNetCoreInstrumentation()
.AddPrometheusExporter());
9. فحوصات الصحة
builder.Services.AddHealthChecks()
.AddNpgsql(connectionString)
.AddUrlGroup(new Uri("http://traefik:8080/ping"), "traefik")
.AddUrlGroup(new Uri("http://seq:5341/health"), "seq");
app.MapHealthChecks("/health");
app.MapHealthChecksUI(o => o.UIPath = "/health-ui");
app.MapPrometheusScrapingEndpoint("/metrics");
10. اتفاقيات الكود
| الاتفاقية |
القاعدة |
| التسمية |
PascalCase للعام. camelCase للمتغيرات. _camelCase للحقول الخاصة. |
| الملفات |
صنف واحد في الملف. اسم الملف = اسم النوع. |
| التزامن |
كل عمليات I/O غير متزامنة. استخدم CancellationToken. |
| المال |
decimal دائماً. numeric(18,4) في PostgreSQL. ممنوع float/double. |
| UUID |
كل المفاتيح Guid.NewGuid() من طبقة التطبيق. القاعدة لا تولدها. |
| المعاملات |
كل معالج أوامر يغلف في معاملة DB عبر TransactionBehavior. |
| الأخطاء |
نمط Result<T> للأخطاء المتوقعة. الاستثناءات للأخطاء غير المتوقعة فقط. |
سجل المراجعة
| التاريخ |
الإصدار |
المعد |
التغييرات |
| 2026-05-10 |
1.0 |
قائد الخلفية |
الإصدار الأولي لدليل مكتبات الخلفية |