تطبيق تجريبي واحد زائد واحد لتطوير البرمجيات الشعار
العرض 02

منطق الأعمال
BusinessLogicLayer

الكيانات والإعدادات والأدوات المشتركة — كل ذلك دون أن تعلم بوجود متصفّح أصلًا.

ما دور هذه الطبقة؟

  • هنا تعيش الكيانات (‏Car و‏Student…) وقواعد العمل الخاصة بها.
  • ترث من Record في طبقة البيانات — ولذلك يعرف كل كيان أصلًا كيف يُحفَظ ويُحمَّل.
  • يُمنع عليها أن تعرف شيئًا عن الواجهة أو HTTP: لا HttpContext، ولا HTML، ولا Session.
  • بفضل ذلك تخدم الطبقة نفسها موقعًا وتطبيق MAUI و‏Job ليليًا واختبارات.
Web / MAUI / Job UI (عام) BLL DAL + DB

كيان الأعمال = فئة صغيرة

public class Student : Record
{
    [Column(Size = 100)] public string FirstName { get; set; }
    [Column(Size = 100)] public string LastName  { get; set; }
    [Column(Size = 20)]  public string IdNumber  { get; set; }
    [Column] public DateTime BirthDate  { get; set; }
    [Column] public int    Grade        { get; set; }
    [Column] public double Average      { get; set; }
    [Column] public bool   IsActive     { get; set; }
    [Column(Size = 255)] public string Picture { get; set; }
}
  • الحقول = أعمدة. لا شيء أكثر من ذلك.
  • منطق الأعمال (حساب المعدّل، التحقّق من رقم الهوية) يُضاف كدوال على الفئة نفسها.
  • علاقة بكيان آخر؟ خاصية من نوع ذلك الكيان — تُحفظ تلقائيًا كقيمة BIGINT تحمل معرّفه.

‏Settings — إعدادات تعيش في قاعدة البيانات

// القراءة (مخزَّنة في الذاكرة، تُحمَّل مرة واحدة عند إقلاع التطبيق):
string url = Settings.Val["MeshulamUrl"];

// الكتابة:
await Settings.SetAsync("MeshulamUrl", "https://...");

// زرع القيم الافتراضية — كل مشروع يتجاوز CreateAsync:
public override async Task CreateAsync()
{
    await base.CreateAsync();
    await AddAsync("MeshulamUrl", "عنوان بوابة الدفع", "https://sandbox...");
}
  • لماذا قاعدة البيانات لا appsettings.json؟ لأنها تتيح التغيير في زمن التشغيل، ولكل عميل على حدة، دون نشر جديد.
  • الزرع الأولي مُتماثل النتيجة — يعمل مع كل إقلاع ولا يضيف سوى المفاتيح الناقصة.

‏Util — صندوق الأدوات المشترك

  • التواريخ:DateString و‏ToDate والأشهر العبرية والميلادية و‏Decode("תשע\"ו")=776.
  • التحقّق:IsValidTz (رقم تدقيق) و‏IsPhone و‏IsValidEmail.
  • النصوص:StripHTML و‏CleanHiddenChars و‏UrlEscape.
  • الملفات:Mime() والتحقّق من الامتدادات و‏Compress/Decompress (GZip).
  • الأمان:ProtectAppState — تشفير حالة التطبيق المرسَلة إلى العميل.
  • الشبكة:SendHttpRequestAsync باستخدام HttpClient مشترك.

reuse-first: قبل كتابة دالة مساعدة جديدة — تأكّد من أنها ليست موجودة هنا أصلًا.

تصدير المستندات — بدعم كامل للعبرية

  • فئة أساس واحدة: Exporter — تُعيد byte[]؛ ووحدة التحكّم تغلّفها بـFile(...).
  • Excel ‏(NPOI) — ورقة عمل RTL حقيقية؛ ‏Word ‏(OpenXML) — جداول BiDi.
  • PDF ‏(MigraDoc) — خط مضمَّن داخل ملف الـ‏DLL وFlipHebrew التي ترتّب العبرية بصريًا.
Exporter exporter = new ExcelExporter("Students.xlsx", rtl: true);
exporter.AddTable(dataTable);
return File(exporter.GetBytes(), exporter.ContentType, exporter.FileName);

القواعد الذهبية للطبقة

  • غير متزامن حتى النهاية — كل دالة تمسّ قاعدة البيانات تُعيد Task.
  • بلا ارتباط بالواجهة — وما يحتاج إلى بيانات البيئة يتلقّاها كمعامل.
  • كيانات نحيفة؛ والمنطق المشترك يُرفع إلى Util أو إلى فئة أساس.
  • Settings إلى قاعدة البيانات، لا إلى ملفات الإعدادات.
  • وما يصحّ لجميع المشاريع — يُصلَح هنا أولًا، في القالب.