مقدمة: لماذا تطبيقك ليس هو قاعدة بياناته
\n\nتخيل أنك تبني منزلاً. الأساس، الجدران، السقف - كل شيء مدروس. ولكن بعد عام، تقرر تغيير الأسلاك الكهربائية. وهنا تكتشف أن الأسلاك مدفونة في الخرسانة، والمقابس ملحومة بقضبان التسليح. لاستبدال مفتاح واحد، تحتاج إلى هدم نصف المنزل.
\n\nهذا هو شكل تطبيق Java التقليدي تقريبًا، حيث يتشابك منطق الأعمال بشكل وثيق مع الإطار البرمجي، قاعدة البيانات، أو وحدة التحكم REST. هل تريد الانتقال من PostgreSQL إلى MongoDB؟ إعادة كتابة وحدات التحكم من Spring MVC إلى JAX-RS؟ مرحبًا بك في جحيم إعادة الهيكلة.
\n\nالحل هو الهندسة السداسية (المعروفة أيضًا باسم "المنافذ والمحولات"، أو "الهندسة النظيفة" وفقًا لتفسير أليستر كوبرن). إنها ليست رصاصة فضية، ولكنها طريقة لجعل تطبيقك مرنًا، قابلًا للاختبار، ومستقلًا عن العالم الخارجي.
\n\nاليوم سنشرح الهندسة السداسية في Java مع أمثلة كود حقيقية، بدون حشو أو نقاشات نظرية مجردة. فلنبدأ.
\n\nما هي الهندسة السداسية (المنافذ والمحولات)؟
\n\nالفكرة بسيطة: تطبيقك هو نواة (core) تحتوي على منطق الأعمال. حول النواة يوجد "سداسي" (في الواقع عدد عشوائي من الأضلاع)، يتواصل من خلاله العالم الخارجي مع النواة.
\n\nلكل ضلع منفذ خاص به (واجهة). والمحول هو تنفيذ المنفذ لتقنية معينة. على سبيل المثال:
\n- \n
- منفذ "UserRepository" - محول "JpaUserRepository" أو "MongoUserRepository" \n
- منفذ "SendNotification" - محول "EmailSender" أو "SmsSender" \n
القاعدة الأساسية: النواة لا تعرف شيئًا عن المكتبات الخارجية، قواعد البيانات، أو أطر الويب. إنها تعرف فقط منافذها (الواجهات). هذا يوفر عدة مزايا:
\n- \n
- الاستقلال عن البنية التحتية - تغيير قاعدة البيانات أو الإطار البرمجي دون المساس بالمنطق. \n
- قابلية الاختبار - يمكن اختبار منطق الأعمال دون تشغيل سياق Spring أو قاعدة البيانات. \n
- سهولة القراءة - كود النواة يروي قصة المجال، وليس التفاصيل التقنية. \n
هيكل المشروع: كيفية تنظيم الكود
\n\nلا يوجد هيكل حزم واحد صحيح، لكنني أقترح نسخة مجربة تعتمد على DDD (التصميم المدفوع بالمجال) ومبادئ الهندسة السداسية:
\n\ncom.example.app\n├── domain\n│ ├── model // الكيانات، كائنات القيمة\n│ ├── port // واجهات المنافذ (داخلية/خارجية)\n│ └── service // خدمات الأعمال (حالات الاستخدام)\n├── adapter\n│ ├── inbound\n│ │ ├── rest // وحدات تحكم REST (محولات واردة)\n│ │ └── messaging // مستمعي Kafka/RabbitMQ\n│ └── outbound\n│ ├── persistence // مستودعات JPA, JDBC, MongoDB\n│ └── client // عملاء HTTP، واجهات برمجية خارجية\n└── config // تكوين Spring (إذا كنت تستخدمه)\n\n\nلاحظ: domain لا يستورد أي شيء من adapter. هذا أمر بالغ الأهمية. إذا رأيت @Entity أو @RestController في domain، فهذا يعني أن الهندسة معطلة.
مثال: نظام إدارة الطلبات
\n\nلنقم بتصميم نظام بسيط: يمكن للمستخدم إنشاء طلب، ويتحقق النظام من توفر المنتج في المخزون ويرسل إشعارًا.
\n\nالخطوة 1. نموذج المجال (domain/model)
\n\n// Order.java\npublic class Order {\n private final OrderId id;\n private final List<OrderItem> items;\n private OrderStatus status;\n\n public Order(List<OrderItem> items) {\n this.id = new OrderId(UUID.randomUUID().toString());\n this.items = Collections.unmodifiableList(items);\n this.status = OrderStatus.PENDING;\n }\n\n public void confirm() {\n if (status != OrderStatus.PENDING) {\n throw new IllegalStateException("Only PENDING orders can be confirmed");\n }\n this.status = Ord