سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

CMS نویس مرجعی برای آموزش برنامه نویسی وب و PHP، توسعه افزونه‌های اختصاصی وردپرس، طراحی ورک‌فلو و اتوماسیون با n8n و توسعه CMS های اختصاصی است. هدف ما ارائه آموزش‌های کاربردی و راهکارهای حرفه‌ای برای توسعه‌دهندگان، کسب‌وکارها و علاقه‌مندان به دنیای برنامه‌نویسی است.

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
Table Module Pattern در PHP چیست؟ آموزش الگوی Table Module با مثال عملی

Table Module Pattern در PHP چیست؟ آموزش الگوی Table Module با مثال عملی

ممدیر کل سیستم ۲۶ شهریور ۱۴۰۵ ۱۹ دقیقه مطالعه ۲ بازدید
Table Module Pattern در PHP چیست؟ آموزش الگوی Table Module با مثال عملی

در پروژه‌های PHP معمولاً برای کار با اطلاعات دیتابیس از مدل‌ها، Repositoryها، Serviceها یا Query Builder استفاده می‌کنیم.

برای مثال، در یک فروشگاه اینترنتی ممکن است جدولی به نام orders داشته باشیم که سفارش‌های کاربران در آن ذخیره می‌شوند.

در یک طراحی ساده، برای هر سفارش یک Object جداگانه می‌سازیم:


$order = new Order();

اما همیشه لازم نیست منطق برنامه را بر اساس Objectهای مستقل هر رکورد طراحی کنیم.

گاهی بهتر است منطق مربوط به تمام ردیف‌های یک جدول در یک کلاس واحد قرار بگیرد.

این دقیقاً همان ایده اصلی Table Module Pattern است.

در این مقاله یاد می‌گیریم:

  • Table Module Pattern چیست.
  • چه تفاوتی با Domain Model دارد.
  • چگونه آن را با PHP پیاده‌سازی کنیم.
  • چه ارتباطی با Table Data Gateway دارد.
  • در Laravel چگونه از آن استفاده کنیم.
  • چه زمانی این الگو مناسب و چه زمانی نامناسب است.

Table Module Pattern چیست؟

Table Module یکی از الگوهای مربوط به سازمان‌دهی Domain Logic در معماری نرم‌افزارهای سازمانی است.

Martin Fowler این الگو را چنین تعریف می‌کند:

> یک نمونه از کلاس که منطق کسب‌وکار مربوط به تمام ردیف‌های یک جدول یا View را مدیریت می‌کند.

در این الگو معمولاً برای هر جدول دیتابیس یک کلاس در نظر گرفته می‌شود.

برای مثال:


users     → UserTableModule

orders    → OrderTableModule

products  → ProductTableModule

invoices  → InvoiceTableModule

در این ساختار، یک نمونه از OrderTableModule می‌تواند عملیات مربوط به تمام سفارش‌ها را انجام دهد.


$orderModule = new OrderTableModule();



$orderModule->findById(10);

$orderModule->create($data);

$orderModule->cancel(10);

$orderModule->getPendingOrders();

نکته مهم این است که در Table Module برای هر ردیف جدول الزاماً یک Object مستقل ساخته نمی‌شود. در عوض، یک Object مسئول مدیریت منطق مربوط به کل جدول است. <Cite refs={["turn0search1","turn0search4"]} />


Table Module چه مشکلی را حل می‌کند؟

فرض کنید جدول سفارش‌ها چنین داده‌هایی دارد:


orders

------------------------------------------------

id | user_id | status    | total

------------------------------------------------

1  | 10      | pending   | 2500000

2  | 11      | paid      | 1800000

3  | 12      | cancelled | 900000

حالا چند عملیات مختلف روی این جدول داریم:

  • دریافت سفارش بر اساس شناسه
  • ایجاد سفارش
  • محاسبه مبلغ سفارش‌ها
  • دریافت سفارش‌های پرداخت‌نشده
  • لغو سفارش
  • محاسبه فروش روزانه
  • دریافت سفارش‌های یک کاربر
  • بررسی وضعیت سفارش‌ها

اگر این منطق را در چندین Controller یا فایل پراکنده قرار دهیم، نگهداری آن سخت‌تر می‌شود.

در Table Module همه این عملیات در یک کلاس مربوط به جدول سفارش‌ها قرار می‌گیرند:


OrderTableModule

├── create()

├── findById()

├── cancel()

├── getPendingOrders()

├── calculateDailySales()

└── getUserOrders()

این ساختار برای سیستم‌هایی مناسب است که منطق آنها بیشتر حول جدول‌ها و عملیات روی مجموعه‌ای از رکوردها شکل گرفته است.


ساختار کلی Table Module

ساختار ساده این الگو را می‌توان به صورت زیر نمایش داد:


Controller

    |

    v

OrderTableModule

    |

    +── create()

    +── findById()

    +── update()

    +── cancel()

    +── getPendingOrders()

    |

    v

Table Data Gateway

    |

    v

Database

در این ساختار:

  • Controller درخواست کاربر را دریافت می‌کند.
  • Table Module منطق کسب‌وکار را اجرا می‌کند.
  • Table Data Gateway ارتباط با جدول دیتابیس را مدیریت می‌کند.
  • Database داده‌ها را ذخیره می‌کند.

Table Module و Table Data Gateway دو مفهوم متفاوت هستند.

Table Module مسئول Business Logic است، اما Table Data Gateway بیشتر مسئول دسترسی به جدول دیتابیس است.


# تفاوت Table Module و Domain Model

در Domain Model معمولاً برای هر Entity یک Object مستقل داریم.

برای مثال:


$order = new Order(

    id: 10,

    total: 2500000

);

اگر ۱۰۰ سفارش داشته باشیم، می‌توانیم ۱۰۰ نمونه از Order داشته باشیم.

اما در Table Module ممکن است فقط یک نمونه از کلاس مدیریت سفارش‌ها داشته باشیم:


$orderModule = new OrderTableModule();

و همین Object روی همه سفارش‌ها کار کند.

Martin Fowler تفاوت اصلی این دو الگو را در همین موضوع می‌داند: در Domain Model برای هر سفارش یک Object وجود دارد، اما در Table Module یک Object منطق تمام سفارش‌های جدول را مدیریت می‌کند. <Cite refs={["turn0search1","turn0search4"]} />

Domain Model


Order #1 → Order Object

Order #2 → Order Object

Order #3 → Order Object

Table Module


All Orders → One OrderTableModule

هیچ‌کدام به صورت مطلق برای همه پروژه‌ها مناسب نیستند.

انتخاب بین آنها به پیچیدگی Business Logic و نوع داده‌های برنامه بستگی دارد.


# تفاوت Table Module و Transaction Script

در Transaction Script معمولاً هر عملیات به صورت یک Procedure یا متد مستقل سازمان‌دهی می‌شود.

برای مثال:


createOrder()

payOrder()

cancelOrder()

اما در Table Module، این عملیات در یک کلاس مرتبط با جدول قرار می‌گیرند:


OrderTableModule

├── createOrder()

├── payOrder()

└── cancelOrder()

بنابراین Table Module را می‌توان در بسیاری از پروژه‌ها یک نقطه میانی میان Transaction Script و Domain Model دانست. این الگو منطق را نسبت به Transaction Script منسجم‌تر می‌کند، اما مانند Domain Model برای هر رکورد یک Object مستقل ایجاد نمی‌کند. <Cite refs={["turn0search1","turn0search3"]} />


# پیاده‌سازی Table Module در PHP

برای مثال، یک جدول سفارش‌ها را در نظر می‌گیریم:


CREATE TABLE orders (

    id INT AUTO_INCREMENT PRIMARY KEY,

    user_id INT NOT NULL,

    status VARCHAR(30) NOT NULL,

    total INT NOT NULL,

    created_at DATETIME NOT NULL

);

حالا می‌خواهیم یک کلاس به نام OrderTableModule ایجاد کنیم.

این کلاس عملیات مربوط به جدول orders را مدیریت می‌کند.


ساخت کلاس OrderTableModule


<?php



final class OrderTableModule

{

    public function __construct(

        private PDO $pdo

    ) {

    }



    public function findById(int $id): ?array

    {

        $statement = $this->pdo->prepare(

            'SELECT * FROM orders WHERE id = :id'

        );



        $statement->execute([

            'id' => $id,

        ]);



        $order = $statement->fetch(PDO::FETCH_ASSOC);



        return $order ?: null;

    }



    public function getPendingOrders(): array

    {

        $statement = $this->pdo->query(

            "SELECT * FROM orders WHERE status = 'pending'"

        );



        return $statement->fetchAll(PDO::FETCH_ASSOC);

    }

}

در این مثال، OrderTableModule مسئول منطق و عملیات مرتبط با جدول سفارش‌هاست.

استفاده از آن:


$module = new OrderTableModule($pdo);



$order = $module->findById(10);



$pendingOrders = $module->getPendingOrders();


# افزودن عملیات ایجاد سفارش

اکنون متدی برای ایجاد سفارش اضافه می‌کنیم:


public function create(

    int $userId,

    int $total

): int {

    if ($total <= 0) {

        throw new InvalidArgumentException(

            'Order total must be greater than zero.'

        );

    }



    $statement = $this->pdo->prepare(

        'INSERT INTO orders

        (user_id, status, total, created_at)

        VALUES

        (:user_id, :status, :total, :created_at)'

    );



    $statement->execute([

        'user_id' => $userId,

        'status' => 'pending',

        'total' => $total,

        'created_at' => date('Y-m-d H:i:s'),

    ]);



    return (int) $this->pdo->lastInsertId();

}

حالا:


$orderId = $module->create(

    userId: 15,

    total: 3500000

);

در اینجا قانون حداقل مبلغ سفارش نیز داخل Table Module قرار گرفته است.


# افزودن منطق لغو سفارش

فرض کنیم سفارش فقط زمانی قابل لغو است که وضعیت آن pending باشد.


public function cancel(int $id): void

{

    $order = $this->findById($id);



    if (!$order) {

        throw new RuntimeException(

            'Order not found.'

        );

    }



    if ($order['status'] !== 'pending') {

        throw new RuntimeException(

            'Only pending orders can be cancelled.'

        );

    }



    $statement = $this->pdo->prepare(

        "UPDATE orders

         SET status = 'cancelled'

         WHERE id = :id"

    );



    $statement->execute([

        'id' => $id,

    ]);

}

استفاده:


$module->cancel(10);

در این طراحی Controller نیازی ندارد جزئیات وضعیت سفارش را بررسی کند.


# محاسبه فروش روزانه در Table Module

یکی از کاربردهای Table Module مدیریت منطق مربوط به مجموعه‌ای از رکوردهاست.

برای مثال، محاسبه فروش روزانه:


public function calculateDailySales(

    string $date

): int {

    $statement = $this->pdo->prepare(

        "SELECT COALESCE(SUM(total), 0)

         FROM orders

         WHERE status = 'paid'

         AND DATE(created_at) = :date"

    );



    $statement->execute([

        'date' => $date,

    ]);



    return (int) $statement->fetchColumn();

}

استفاده:


$total = $module->calculateDailySales(

    '2026-09-17'

);



echo $total;

این نوع منطق به مجموعه‌ای از سفارش‌ها مربوط است، نه فقط یک سفارش مشخص.

به همین دلیل قرار دادن آن در Table Module می‌تواند طبیعی باشد.


# استفاده از Table Data Gateway

در بسیاری از طراحی‌ها بهتر است ارتباط مستقیم با دیتابیس از منطق کسب‌وکار جدا شود.

در این حالت می‌توان یک OrderTableGateway ایجاد کرد.


<?php



final class OrderTableGateway

{

    public function __construct(

        private PDO $pdo

    ) {

    }



    public function findById(int $id): ?array

    {

        $statement = $this->pdo->prepare(

            'SELECT * FROM orders WHERE id = :id'

        );



        $statement->execute([

            'id' => $id,

        ]);



        $order = $statement->fetch(PDO::FETCH_ASSOC);



        return $order ?: null;

    }



    public function updateStatus(

        int $id,

        string $status

    ): void {

        $statement = $this->pdo->prepare(

            'UPDATE orders

             SET status = :status

             WHERE id = :id'

        );



        $statement->execute([

            'id' => $id,

            'status' => $status,

        ]);

    }

}

حالا Table Module می‌تواند از Gateway استفاده کند:


<?php



final class OrderTableModule

{

    public function __construct(

        private OrderTableGateway $orders

    ) {

    }



    public function cancel(int $id): void

    {

        $order = $this->orders->findById($id);



        if (!$order) {

            throw new RuntimeException(

                'Order not found.'

            );

        }



        if ($order['status'] !== 'pending') {

            throw new RuntimeException(

                'Only pending orders can be cancelled.'

            );

        }



        $this->orders->updateStatus(

            id: $id,

            status: 'cancelled'

        );

    }

}

در این ساختار:

  • Gateway عملیات دیتابیس را انجام می‌دهد.
  • Table Module قوانین کسب‌وکار را اجرا می‌کند.

Table Data Gateway به صورت طبیعی با Table Module هماهنگ می‌شود، زیرا هر دو حول ساختار جدولی داده‌ها طراحی شده‌اند. <Cite refs={["turn0search21","turn0search1"]} />


# Table Module در Laravel

در Laravel می‌توان این الگو را با Query Builder، Eloquent یا یک Gateway سفارشی پیاده‌سازی کرد.

برای مثال، ساختار زیر را در نظر بگیرید:


app/

├── Modules/

│   └── Orders/

│       ├── OrderTableModule.php

│       └── OrderTableGateway.php

│

└── Http/

    └── Controllers/

        └── OrderController.php


ساخت OrderTableModule در Laravel


<?php



namespace App\Modules\Orders;



use Illuminate\Support\Facades\DB;

use RuntimeException;



final class OrderTableModule

{

    public function findById(int $id): ?object

    {

        return DB::table('orders')

            ->where('id', $id)

            ->first();

    }



    public function getPendingOrders(): array

    {

        return DB::table('orders')

            ->where('status', 'pending')

            ->get()

            ->all();

    }



    public function create(

        int $userId,

        int $total

    ): int {

        if ($total <= 0) {

            throw new RuntimeException(

                'Order total must be greater than zero.'

            );

        }



        return (int) DB::table('orders')->insertGetId([

            'user_id' => $userId,

            'status' => 'pending',

            'total' => $total,

            'created_at' => now(),

            'updated_at' => now(),

        ]);

    }



    public function cancel(int $id): void

    {

        $order = $this->findById($id);



        if (!$order) {

            throw new RuntimeException(

                'Order not found.'

            );

        }



        if ($order->status !== 'pending') {

            throw new RuntimeException(

                'Only pending orders can be cancelled.'

            );

        }



        DB::table('orders')

            ->where('id', $id)

            ->update([

                'status' => 'cancelled',

                'updated_at' => now(),

            ]);

    }

}

در این مثال، Table Module به جای ساختن یک Object مستقل برای هر سفارش، عملیات مربوط به کل جدول orders را مدیریت می‌کند.


# استفاده از Table Module در Controller

Controller می‌تواند از Table Module استفاده کند:


<?php



namespace App\Http\Controllers;



use App\Modules\Orders\OrderTableModule;

use Illuminate\Http\JsonResponse;

use Illuminate\Http\Request;



final class OrderController

{

    public function __construct(

        private OrderTableModule $orders

    ) {

    }



    public function store(Request $request): JsonResponse

    {

        $validated = $request->validate([

            'user_id' => ['required', 'integer'],

            'total' => ['required', 'integer', 'min:1'],

        ]);



        $orderId = $this->orders->create(

            userId: $validated['user_id'],

            total: $validated['total']

        );



        return response()->json([

            'id' => $orderId,

        ], 201);

    }



    public function cancel(int $id): JsonResponse

    {

        $this->orders->cancel($id);



        return response()->json([

            'message' => 'Order cancelled successfully.',

        ]);

    }

}

Controller در اینجا فقط درخواست را دریافت و پاسخ را تولید می‌کند.

منطق مربوط به سفارش‌ها در OrderTableModule قرار گرفته است.


# استفاده از تراکنش دیتابیس

اگر یک عملیات روی چند جدول اجرا می‌شود، بهتر است از تراکنش دیتابیس استفاده کنیم.

برای مثال، هنگام ثبت سفارش ممکن است نیاز داشته باشیم:

  1. سفارش ایجاد شود.
  2. آیتم‌های سفارش ذخیره شوند.
  3. موجودی محصولات کاهش پیدا کند.
  4. تراکنش مالی ثبت شود.

در Laravel:


use Illuminate\Support\Facades\DB;



public function createOrder(

    int $userId,

    array $items

): int {

    return DB::transaction(function () use (

        $userId,

        $items

    ) {

        $orderId = DB::table('orders')->insertGetId([

            'user_id' => $userId,

            'status' => 'pending',

            'total' => 0,

            'created_at' => now(),

            'updated_at' => now(),

        ]);



        foreach ($items as $item) {

            DB::table('order_items')->insert([

                'order_id' => $orderId,

                'product_id' => $item['product_id'],

                'quantity' => $item['quantity'],

                'price' => $item['price'],

            ]);

        }



        return $orderId;

    });

}

در این مثال، Table Module می‌تواند فرآیند مربوط به جدول سفارش‌ها و جدول آیتم‌های سفارش را هماهنگ کند.

با این حال، اگر عملیات بیش از حد پیچیده شود، بهتر است Application Service یا Domain Model نیز وارد طراحی شوند.


# تفاوت Table Module و Repository

Repository معمولاً برای دسترسی به مجموعه‌ای از Domain Objectها طراحی می‌شود.

مثلاً:


$order = $orderRepository->findById(10);

اما Table Module معمولاً علاوه بر دسترسی به داده، منطق کسب‌وکار مربوط به جدول را نیز مدیریت می‌کند:


$orderModule->cancel(10);

$orderModule->calculateDailySales($date);

Repository

تمرکز اصلی:


Find

Save

Delete

Search

Table Module

تمرکز اصلی:


Business Logic for a Table

Create Order

Cancel Order

Calculate Sales

Validate Table Rules

البته در پروژه‌های واقعی ممکن است Repository و Table Module در کنار یکدیگر استفاده شوند.


# تفاوت Table Module و Active Record

در Active Record معمولاً هر Object نماینده یک رکورد دیتابیس است.

برای مثال در Laravel:


$order = Order::find(10);



$order->status = 'paid';



$order->save();

اما در Table Module یک کلاس واحد می‌تواند روی تمام رکوردهای جدول کار کند:


$orderModule->findById(10);

$orderModule->cancel(10);

$orderModule->getPendingOrders();

Active Record معمولاً داده، رفتار مرتبط و عملیات Persistence را در یک Object ترکیب می‌کند.

Table Module بیشتر بر منطق مجموعه‌ای از رکوردهای یک جدول تمرکز دارد.


# مزایای Table Module Pattern

۱. ساختار ساده‌تر نسبت به Domain Model

در پروژه‌هایی که مدل‌سازی Objectهای مستقل برای هر رکورد ضروری نیست، Table Module می‌تواند ساده‌تر باشد.

۲. تمرکز منطق جدول در یک مکان

تمام Business Logic مربوط به جدول سفارش‌ها در OrderTableModule قرار می‌گیرد.

۳. مناسب برای داده‌های جدولی

اگر برنامه بیشتر با Record Setها، گزارش‌ها و عملیات دسته‌جمعی کار می‌کند، این الگو می‌تواند طبیعی باشد.

۴. هماهنگی خوب با Table Data Gateway

Table Module و Table Data Gateway هر دو حول جدول دیتابیس طراحی می‌شوند و می‌توانند به خوبی با یکدیگر ترکیب شوند. <Cite refs={["turn0search21","turn0search1"]} />

۵. کاهش تکرار Query و Business Rule

عملیات رایج مانند دریافت سفارش‌های Pending یا محاسبه فروش روزانه می‌توانند در یک کلاس متمرکز شوند.


# معایب Table Module Pattern

۱. بزرگ شدن کلاس

اگر جدول عملیات زیادی داشته باشد، Table Module ممکن است به یک کلاس بسیار بزرگ تبدیل شود.

۲. وابستگی به ساختار دیتابیس

در بسیاری از پیاده‌سازی‌ها، Table Module مستقیماً با نام جدول و ستون‌های دیتابیس کار می‌کند.

۳. دشواری در مدل‌سازی رفتارهای پیچیده هر Entity

اگر هر سفارش قوانین و رفتارهای پیچیده و متفاوتی داشته باشد، Domain Model ممکن است مناسب‌تر باشد.

۴. احتمال تبدیل شدن به کلاس همه‌کاره

اگر تمام گزارش‌ها، عملیات دیتابیس، قوانین مالی، Notificationها و منطق خارجی در یک Table Module قرار بگیرند، طراحی به‌مرور پیچیده می‌شود.

۵. تست‌های وابسته به دیتابیس

اگر منطق کسب‌وکار مستقیماً به Query Builder یا PDO وابسته باشد، تست واحد آن دشوارتر از یک Domain Model مستقل خواهد بود.


# چه زمانی از Table Module استفاده کنیم؟

Table Module می‌تواند برای شرایط زیر مناسب باشد:

  • پروژه‌های CRUD محور
  • سیستم‌های گزارش‌گیری
  • سیستم‌هایی که داده‌ها بیشتر به صورت جدول مدیریت می‌شوند
  • منطق ساده یا متوسط مربوط به یک جدول
  • برنامه‌هایی که از Record Set استفاده می‌کنند
  • سیستم‌های اداری و مالی ساده
  • پروژه‌هایی که Domain Model کامل برای آنها بیش از نیاز است
  • عملیات دسته‌جمعی روی رکوردها

برای مثال، یک سیستم مدیریت سفارش ساده می‌تواند چنین ساختاری داشته باشد:


OrderTableModule

├── create()

├── update()

├── cancel()

├── findByUser()

└── calculateSales()


# چه زمانی از Table Module استفاده نکنیم؟

اگر هر Entity رفتارهای پیچیده و مستقل دارد، بهتر است Domain Model را بررسی کنیم.

برای مثال در یک سیستم بیمه، هر قرارداد ممکن است:

  • شرایط متفاوت داشته باشد.
  • قوانین محاسبه متفاوت داشته باشد.
  • وضعیت‌های پیچیده داشته باشد.
  • به چند Entity دیگر وابسته باشد.
  • رویدادهای Domain تولید کند.

در چنین شرایطی یک Table Module بزرگ ممکن است تمام این منطق را در یک کلاس جمع کند و نگهداری را دشوار سازد.

همچنین اگر هدف اصلی جداسازی کامل Domain از دیتابیس باشد، Data Mapper و Domain Model می‌توانند انتخاب مناسب‌تری باشند.


# تست Table Module با PHPUnit

برای تست Table Module در Laravel می‌توان از تست‌های دیتابیسی استفاده کرد.

مثلاً:


<?php



namespace Tests\Feature;



use App\Modules\Orders\OrderTableModule;

use Illuminate\Foundation\Testing\RefreshDatabase;

use Illuminate\Support\Facades\DB;

use Tests\TestCase;



final class OrderTableModuleTest extends TestCase

{

    use RefreshDatabase;



    public function test_it_can_create_an_order(): void

    {

        $module = app(OrderTableModule::class);



        $orderId = $module->create(

            userId: 1,

            total: 2500000

        );



        $this->assertDatabaseHas('orders', [

            'id' => $orderId,

            'user_id' => 1,

            'status' => 'pending',

            'total' => 2500000,

        ]);

    }



    public function test_it_can_cancel_a_pending_order(): void

    {

        $orderId = DB::table('orders')->insertGetId([

            'user_id' => 1,

            'status' => 'pending',

            'total' => 2500000,

            'created_at' => now(),

            'updated_at' => now(),

        ]);



        $module = app(OrderTableModule::class);



        $module->cancel($orderId);



        $this->assertDatabaseHas('orders', [

            'id' => $orderId,

            'status' => 'cancelled',

        ]);

    }

}

برای اجرای تست:


php artisan test --filter=OrderTableModuleTest

این نوع تست بیشتر رفتار Table Module را در کنار دیتابیس بررسی می‌کند.


# اشتباهات رایج در پیاده‌سازی Table Module

۱. قرار دادن تمام منطق برنامه در یک Table Module

Table Module باید مسئول منطق مرتبط با یک جدول یا View باشد، نه تمام سیستم.

۲. ترکیب بیش از حد Query و منطق خارجی

ارسال ایمیل، اجرای API خارجی و مدیریت پرداخت نباید بدون دلیل در همان کلاس قرار بگیرند.

۳. استفاده از Table Module برای Domain بسیار پیچیده

اگر قوانین هر Entity پیچیده هستند، Domain Model می‌تواند طراحی مناسب‌تری ارائه دهد.

۴. نادیده گرفتن تراکنش‌های دیتابیس

اگر عملیات چند مرحله‌ای است، باید احتمال شکست در میانه عملیات را در نظر گرفت.

۵. نوشتن Queryهای تکراری در Controllerها

اگر یک Query یا قانون در چند Controller تکرار می‌شود، بهتر است به Table Module یا Gateway منتقل شود.

۶. نام‌گذاری مبهم کلاس

نام‌هایی مانند Helper یا CommonService مشخص نمی‌کنند کلاس چه مسئولیتی دارد.

نامی مانند:


OrderTableModule

واضح‌تر است.


# نمونه ساختار پیشنهادی در Laravel

برای پروژه متوسط می‌توان از این ساختار استفاده کرد:


app/

└── Modules/

    └── Orders/

        ├── OrderTableModule.php

        ├── OrderTableGateway.php

        ├── OrderValidator.php

        └── Exceptions/

            ├── OrderNotFound.php

            └── InvalidOrderStatus.php

اگر پروژه بزرگ‌تر شد، می‌توان Application Service را نیز اضافه کرد:


app/

├── Application/

│   └── Orders/

│       ├── CreateOrder.php

│       └── CancelOrder.php

│

├── Modules/

│   └── Orders/

│       ├── OrderTableModule.php

│       └── OrderTableGateway.php

│

└── Http/

    └── Controllers/

        └── OrderController.php

در این معماری:

  • Controller مسئول HTTP است.
  • Application Service جریان عملیات را هماهنگ می‌کند.
  • Table Module منطق جدول را مدیریت می‌کند.
  • Gateway به دیتابیس متصل می‌شود.

# جمع‌بندی

Table Module Pattern در PHP الگویی است که منطق کسب‌وکار مربوط به تمام ردیف‌های یک جدول یا View را در یک کلاس واحد قرار می‌دهد.

در Domain Model معمولاً برای هر Entity یک Object مستقل داریم، اما در Table Module یک Object می‌تواند منطق تمام رکوردهای یک جدول را مدیریت کند. <Cite refs={["turn0search1","turn0search4"]} />

این الگو برای پروژه‌هایی مناسب است که:

  • داده‌های آنها بیشتر جدولی هستند.
  • Business Logic ساده یا متوسط است.
  • عملیات دسته‌جمعی روی رکوردها اهمیت دارد.
  • استفاده از Domain Model کامل بیش از نیاز پروژه است.

در مقابل، اگر رفتار هر Entity پیچیده و مستقل باشد، بهتر است Domain Model را بررسی کنیم.

در Laravel نیز می‌توان Table Module را با Query Builder، Eloquent یا Table Data Gateway پیاده‌سازی کرد.

اصل مهم این است که الگو را متناسب با پیچیدگی واقعی پروژه انتخاب کنیم.


# سوالات متداول

Table Module Pattern در PHP چیست؟

Table Module Pattern الگویی است که منطق کسب‌وکار مربوط به تمام ردیف‌های یک جدول یا View را در یک نمونه کلاس مدیریت می‌کند.

تفاوت Table Module و Domain Model چیست؟

در Domain Model معمولاً برای هر رکورد یک Object مستقل داریم، اما در Table Module یک Object منطق مربوط به تمام رکوردهای جدول را مدیریت می‌کند.

آیا Table Module برای Laravel مناسب است؟

بله. می‌توان آن را با Query Builder، Eloquent یا یک Table Data Gateway در Laravel پیاده‌سازی کرد.

آیا Table Module همان Repository است؟

خیر. Repository بیشتر برای دسترسی به مجموعه‌ای از Objectها استفاده می‌شود، اما Table Module علاوه بر دسترسی به داده، منطق کسب‌وکار مربوط به جدول را نیز مدیریت می‌کند.

Table Module با Table Data Gateway چه تفاوتی دارد؟

Table Data Gateway مسئول ارتباط با جدول دیتابیس است، اما Table Module مسئول اجرای Business Logic مربوط به آن جدول است.

آیا Table Module برای پروژه‌های CRUD مناسب است؟

بله. در پروژه‌های CRUD و سیستم‌هایی که منطق آنها حول جدول‌ها شکل گرفته است، Table Module می‌تواند ساختار ساده و قابل فهمی ایجاد کند.

آیا در Table Module برای هر رکورد یک Object ساخته می‌شود؟

خیر. معمولاً یک نمونه از Table Module روی تمام رکوردهای جدول کار می‌کند.

چه زمانی بهتر است از Domain Model استفاده کنیم؟

وقتی هر Entity رفتارها و قوانین پیچیده و مستقل دارد، Domain Model معمولاً گزینه مناسب‌تری برای بررسی است.

نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه