هندسة السداسي (Hexagonal Architecture) في TypeScript: بنية نظيفة لتطبيقات مرنة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا هندسة السداسي ليست مجرد مصطلح رائج، بل هي إنقاذ لرمزك؟

تخيل أنك تبني منزلاً. ألن تقوم بتثبيت المقابس في الجدران الخرسانية؟ أنت أولاً تمد الأسلاك، وتضع العلامات، ثم تقوم بتوصيل المقابس بعناية. في تطوير البرمجيات، الأمر نفسه تمامًا - منطق أعمالك (جوهر التطبيق) لا يجب أن يعتمد على الوسيلة التي يتواصل بها معه: REST API، قاعدة بيانات، أو قوائم الرسائل.

هندسة السداسي (Hexagonal Architecture) (المعروفة أيضًا باسم "المنافذ والمحولات" أو "البنية النظيفة" في تفسير أليستر كوبرن) تحل هذه المشكلة تحديدًا. في هذه المقالة، سنشرح كيفية تنفيذ هذه البنية باستخدام TypeScript مع أمثلة أكواد حقيقية. ستتعلم كيفية فصل منطق الأعمال عن البنية التحتية، وجعل الكود قابلًا للاختبار ومرنًا.



ما هي هندسة السداسي بكلمات بسيطة؟

تخيل شكلًا سداسيًا (hexagon). في المنتصف يوجد تطبيقك مع منطق الأعمال. على كل وجه يوجد "منفذ" (واجهة) يمكن للعالم الخارجي من خلاله التواصل مع الجوهر. المحولات هي تطبيقات محددة: وحدات التحكم، المستودعات، عملاء الخدمات الخارجية. يتم توصيلها بالمنافذ مثل توصيل القابس بالمقبس.

المبدأ الرئيسي: التبعيات تتجه نحو الداخل. الكود الخارجي (قواعد البيانات، الأطر) يعتمد على الجوهر، وليس العكس. هذا يسمح بتغيير قاعدة البيانات من PostgreSQL إلى MongoDB دون لمس سطر واحد في منطق الأعمال.



المفاهيم الأساسية

  • الجوهر (Domain) — منطق الأعمال، الكيانات، كائنات القيمة، حالات الاستخدام. ليس له تبعيات خارجية.
  • المنفذ (Port) — واجهة (في TypeScript هي فئة مجردة أو interface) تحدد عقدًا للعالم الخارجي.
  • المحول (Adapter) — تطبيق محدد للمنفذ. على سبيل المثال، مستودع يعمل مع PostgreSQL عبر TypeORM.
  • التطبيق (Application) — الطبقة التي تنسق تنفيذ حالات الاستخدام.


مثال عملي: نظام إدارة الطلبات

لنقم بتصميم نظام طلبات بسيط. سيكون لدينا كيانات Order و Product، حالة استخدام CreateOrder، منفذ للمستودع، ومحول لـ PostgreSQL.



الخطوة 1: تحديد الجوهر (طبقة المجال)

لننشئ مجلد domain/. هنا تعيش الكيانات والواجهات للمنافذ. لا تبعيات للأطر!



// domain/order.entity.tsexport class Order {    constructor(        public readonly id: string,        public items: OrderItem[],        public status: 'pending' | 'confirmed' | 'shipped',        public total: number    ) {}

public addItem(productId: string, quantity: number, price: number): void { this.items.push({ productId, quantity, price }); this.total = this.calculateTotal(); }

private calculateTotal(): number { return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0); }}

export interface OrderItem { productId: string; quantity: number; price: number;}


الآن لنحدد المنفذ للمستودع:



// domain/ports/order.repository.interface.tsimport { Order } from '../order.entity';

export interface IOrderRepository { save(order: Order): Promise<void>; findById(id: string): Promise<Order | null>; findAll(): Promise<Order[]>;}


الخطوة 2: حالة الاستخدام — قلب التطبيق

حالة الاستخدام هي سيناريو استخدام. تحتوي على منطق الأعمال وتستخدم المنافذ، لكنها لا تعرف من ينفذها.



// application/use-cases/create-order.use-case.tsimport { Order } from '../../domain/order.entity';import { IOrderRepository } from '../../domain/ports/order.repository.interface';

export class CreateOrderUseCase { constructor(private readonly orderRepository: IOrderRepository) {}

async execute(use

المدونات

توصيات الكتب