Enterprise C#: لماذا كود البنك وكود المشروع الشخصي عالمين مختلفين تمامًا
\n\nإذا كنت قد انتقلت يومًا من استوديو ويب صغير إلى شركة كبيرة أو شركة تكنولوجيا مالية، فأنت تعرف شعور الصدمة الثقافية الخفيفة. بالأمس فقط، كان بإمكانك إجراء "إصلاح عاجل" بدون اختبارات ومع تعليق واحد //TODO: refactor، واليوم تتلقى مراجعة تحتوي على 20 ملاحظة، حيث تتطلب كل طريقة عامة توثيقًا بتنسيق XML، وأي تبعية تتطلب تسجيلًا في حاوية DI.
التطوير بلغة C# في Enterprise لا يتعلق بـ "القيام بذلك بسرعة"، بل يتعلق بـ "القيام بذلك بحيث لا يتعطل بعد ثلاث سنوات عندما يتم فصل مؤلف الكود". إنه يتعلق بالقابلية للتنبؤ، والتوسع، ومراجعات الكود التي لا نهاية لها. دعنا نفهم مما يتكون تطوير Enterprise بلغة C# حقًا، وكيف لا تُفصل من الأسبوع الأول.
\n\n1. البنية: SOLID ليست كلمة عصرية، بل قانون
\n\nفي الشركة الناشئة، يمكنك تحمل بناء متجانس مع نصوص "سحرية" وفئات ضخمة تحتوي على 2000 سطر. في Enterprise - لا. هنا، كل مبدأ من مبادئ SOLID ليس مجرد تجريد، بل دليل للعمل.
\n\nمبدأ المسؤولية الواحدة (SRP)
\nيجب ألا تعرف فصلك كيفية إرسال رسائل البريد الإلكتروني، والحفظ في قاعدة البيانات، وتحليل XML في نفس الوقت. إذا رأيت ثلاث "مسؤوليات" أو أكثر في فئة واحدة - فهذه رائحة كريهة. في Enterprise، سيتم رفض هذا الكود في مراجعة الكود مع صياغة "انتهاك SRP، أعد الكتابة". مثال على كود سيء:
\n\npublic class OrderService\n{\n public void ProcessOrder(Order order)\n {\n // 1. التحقق\n // 2. الحفظ في قاعدة البيانات\n // 3. إرسال البريد الإلكتروني\n // 4. التسجيل\n }\n}\n\nالنهج الصحيح هو تقسيمها إلى خدمات منفصلة واستخدام نمط Mediator أو Pipeline:
\n\npublic class ProcessOrderCommandHandler : IRequestHandler<ProcessOrderCommand, bool>\n{\n private readonly IOrderRepository _repo;\n private readonly IEmailService _email;\n private readonly ILogger<ProcessOrderCommandHandler> _logger;\n\n public ProcessOrderCommandHandler(IOrderRepository repo, IEmailService email, ILogger<ProcessOrderCommandHandler> logger)\n {\n _repo = repo;\n _email = email;\n _logger = logger;\n }\n\n public async Task<bool> Handle(ProcessOrderCommand request, CancellationToken cancellationToken)\n {\n var order = await _repo.GetByIdAsync(request.OrderId);\n // منطق\n await _email.SendAsync(order.CustomerEmail, "Order processed");\n _logger.LogInformation("Order {Id} processed", order.Id);\n return true;\n }\n}\n\nلاحظ: كل خدمة تقوم بمهمة واحدة بالضبط. اختبار مثل هذا الكود أسهل بكثير.
\n\n2. Dependency Injection - صديقك الأهم
\n\nفي مشاريع Enterprise بلغة C#، نادرًا ما ترى new MyService() داخل فئة. بدلاً من ذلك - حاوية DI (عادةً Autofac أو Microsoft.Extensions.DependencyInjection المدمجة).
القاعدة بسيطة: إذا كانت الفئة تنشئ تبعياتها بنفسها - فهي تنتهك مبدأ انعكاس التبعية. في Enterprise، يعتبر هذا خطأ في البنية. مثال:
\n\n// ❌ سيء: ارتباط صلب\npublic class ReportGenerator\n{\n private readonly PdfExporter _exporter = new PdfExporter();\n public byte[] Generate() => _exporter.Export();\n}\n\n// ✅ جيد: انعكاس التبعيات\npublic class ReportGenerator\n{\n private readonly IExporter _exporter;\n public ReportGenerator(IExporter exporter) => _exporter = exporter;\n public byte[] Generate() => _exporter.Export();\n}\n\nالتسجيل في بدء التشغيل:
\nservices.AddScoped<IExporter, PdfExporter>();\nservices.AddScoped<ReportGenerator>();\n\nلماذا هذا مطلوب؟ أولاً، الاختبارات: يمكنك محاكاة IExporter وعدم إنشاء