مع زيادة تعقيد التطبيقات، تصبح بنية "الكود المعكّر" التقليدية غير قابلة للإدارة. تتشابك المنطق التجاري مع أطر واجهة المستخدم واستعلامات قواعد البيانات، مما يجعل الاختبار كابوساً والتغيير صعباً. هنا يأتي دور الهندسة السداسية (المعروفة أيضاً بمنافذ ومحولات) والهندسة الطباقية. توفر هذه الأنماط مخططاً قوياً لتنظيم الكود، مما يضمن بقاء قواعد عملك الأساسية مستقلة عن الاهتمامات الخارجية مثل قواعد البيانات، أو خوادم الويب، أو واجهات المستخدم.
المبدأ الأساسي: عكس الاعتمادية
القاعدة الذهبية لكل من الهندستين بسيطة: تشير الاعتماديات نحو الداخل. تحتوي الطبقة الداخلية الأكثر على منطق عملك النقي، بينما تحتوي الطبقات الخارجية على التفاصيل التقنية. هذا تطبيق لمبدأ عكس الاعتمادية (DIP). يجب ألا تعتمد الوحدات عالية المستوى على الوحدات منخفضة المستوى؛ بل يجب أن يعتمد كلاهما على التجريدات.
في الهندسة الطباقية القياسية، قد نرى طبقات مثل العرض، والأعمال، والبيانات. ومع ذلك، تأخذ الهندسة السداسية هذا الأمر إلى أبعد من ذلك من خلال الفصل الصريح بين "النطاق" (المنطق الأساسي) و"البنية التحتية" (الأدوات الخارجية). يسمح لك هذا باستبدال قاعدة بيانات MySQL بـ PostgreSQL، أو واجهة برمجة تطبيقات REST بـ gRPC، دون لمس قواعد عملك الأساسية.
المنافذ والمحولات: آلية التفاعل
تستمد الهندسة السداسية اسمها من شكل النظام: القلب هو سداسي الأضلاع، والمنافذ هي الواجهات التي تسمح للعوامل الخارجية بالتفاعل معه.
- المنافذ: واجهات تحددها التطبيق، وليس البنية التحتية. وهي تحدد ماذا يمكن للنظام أن يفعل (على سبيل المثال،
UserRepositoryأوPaymentGateway). - المحولات: تنفيذ لهذه المنافذ. تقوم بترجمة الطلبات الخارجية إلى استدعاءات للمنفذ وتنسيق الردود مرة أخرى إلى العالم الخارجي (على سبيل المثال،
JpaUserRepositoryأوStripePaymentAdapter).
مثال عملي: حقن الاعتمادية
فكر في حالة استخدام بسيطة: تسجيل مستخدم. في نظام متشابك بشدة، ستقوم فئة الخدمة بإنشاء مثيل لـ MysqlUserDAO مباشرة. في التصميم السداسي، تعتمد الخدمة على واجهة.
// المنفذ (الواجهة) - مُعرّف في طبقة النواة/النطاق
public interface UserRepository {
User save(User user);
Optional<User> findByEmail(String email);
}
// خدمة التطبيق - أيضاً في طبقة النواة
public class RegistrationService {
private final UserRepository userRepository;
// حقن الاعتمادية عبر المنشئ
public RegistrationService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void registerUser(String email, String password) {
if (userRepository.findByEmail(email).isPresent()) {
throw new IllegalArgumentException("Email already exists");
}
User user = new User(email, password);
userRepository.save(user);
}
}
لاحظ أن RegistrationService لا يعرف شيئاً عن قواعد البيانات، أو SQL، أو JSON. إنه يعرف فقط واجهة UserRepository. هذا يجعل اختبار الوحدات بسيطاً للغاية. يمكنك حقن تنفيذ وهمي أثناء الاختبار.
// مثال الاختبار
@Test
public void testRegistration() {
UserRepository mockRepo = mock(UserRepository.class);
RegistrationService service = new RegistrationService(mockRepo);
service.registerUser("test@example.com", "password");
verify(mockRepo).save(any(User.class));
}
فوائد هذا النهج
- قابلية الاختبار: يمكن اختبار منطق العمل بشكل منفصل دون بدء الخادم أو الاتصال بقاعدة بيانات حقيقية.
- المرونة: يمكنك تغيير التطبيقات التكنولوجية (مثل تبديل طوابير الرسائل) دون إعادة هيكلة منطق العمل.
- الوضوح: يصبح من الواضح على الفور أين توجد قواعد العمل مقابل أين توجد التفاصيل التقنية.
الخاتمة
يتطلب اعتماد الهندسة الطباقية أو السداسية تحولاً أولياً في العقلية ومزيداً من الكود الأساسي (الواجهات والمحولات). ومع ذلك، فإن الفوائد طويلة المدى في قابلية الصيانة، وقابلية الاختبار، والقابلية للتوسع كبيرة. من خلال فرض عكس الاعتمادية بشكل صارم وفصل المنافذ عن المحولات، فإنك تبني أنظمة مقاومة للتغيير وأسهل لفريقك لفهمها وتعديلها بمرور الوقت.