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

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

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

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

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

در پروژه‌های کوچک PHP معمولاً هر بار که تغییری در یک شیء ایجاد می‌کنیم، همان لحظه آن را در دیتابیس ذخیره می‌کنیم.

برای مثال:


$product->save();

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

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

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

اگر مرحله چهارم با خطا مواجه شود، نمی‌خواهیم سه مرحله اول در دیتابیس باقی بمانند.

اینجاست که مفهوم Unit of Work اهمیت پیدا می‌کند.

Unit of Work مجموعه تغییراتی را که در جریان یک عملیات تجاری روی داده‌ها انجام شده پیگیری می‌کند و در پایان مشخص می‌کند چه تغییراتی باید روی دیتابیس اعمال شوند. Martin Fowler نیز Unit of Work را الگویی برای نگهداری فهرست اشیای تحت تأثیر یک تراکنش و هماهنگ‌کردن نوشتن تغییرات و مسائل همزمانی معرفی می‌کند.

در این مقاله، Unit of Work را از صفر با PHP یاد می‌گیریم و سپس آن را در کنار Repository و Laravel بررسی می‌کنیم.


Unit of Work Pattern چیست؟

Unit of Work Pattern الگویی برای مدیریت مجموعه‌ای از تغییرات مرتبط است که باید به عنوان بخشی از یک عملیات تجاری مدیریت شوند.

به جای اینکه هر تغییر مستقیماً به دیتابیس ارسال شود، Unit of Work تغییرات را ثبت می‌کند و در زمان مناسب آن‌ها را به Persistence Layer منتقل می‌کند.

به شکل ساده:


Application

     ↓

Unit of Work

     ↓

جمع‌آوری تغییرات

     ↓

Commit

     ↓

Database

برای مثال تصور کنید سه محصول را تغییر داده‌ایم:


Product #1 → تغییر قیمت

Product #2 → تغییر موجودی

Product #3 → تغییر وضعیت

به جای اینکه هر تغییر به صورت جداگانه مدیریت شود، Unit of Work می‌تواند وضعیت این تغییرات را نگهداری کند و در پایان آن‌ها را اعمال کند.


مشکل ذخیره‌سازی مستقیم تغییرات چیست؟

فرض کنید چنین کدی داریم:


$product->save();

$order->save();

$payment->save();

اگر ذخیره payment با خطا مواجه شود، ممکن است دو عملیات قبلی قبلاً در دیتابیس انجام شده باشند.

در نتیجه وضعیت دیتابیس ممکن است ناقص شود.

مثلاً:


Product     → ذخیره شد

Order       → ذخیره شد

Payment     → خطا

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

Unit of Work کمک می‌کند تغییرات مرتبط را به صورت هماهنگ مدیریت کنیم.

البته Unit of Work با Database Transaction دقیقاً یک مفهوم نیست. Transaction قابلیت دیتابیس برای تضمین ویژگی‌هایی مانند atomicity است؛ Unit of Work یک الگوی نرم‌افزاری برای پیگیری و هماهنگی تغییرات است. این دو معمولاً می‌توانند در کنار یکدیگر استفاده شوند.


# Unit of Work چگونه کار می‌کند؟

یک پیاده‌سازی ساده Unit of Work می‌تواند چند لیست برای تغییرات داشته باشد:


New Objects

Updated Objects

Deleted Objects

برای مثال:


UnitOfWork

│

├── New

│   ├── Order

│   └── OrderItem

│

├── Updated

│   └── Product

│

└── Deleted

    └── CartItem

در زمان Commit، Unit of Work این تغییرات را پردازش می‌کند.

به صورت مفهومی:


registerNew()

registerDirty()

registerDeleted()



        ↓



      commit()



        ↓



Database Transaction

        ↓

INSERT / UPDATE / DELETE

این ایده با تعریف کلاسیک Unit of Work که تغییرات یک Business Transaction را پیگیری و نوشتن آن‌ها را هماهنگ می‌کند، مطابقت دارد.


# اجزای اصلی Unit of Work

یک پیاده‌سازی معمول می‌تواند شامل این اجزا باشد.

۱. Entity

Entity شیئی است که اطلاعات دامنه را نگهداری می‌کند.

مثلاً:


Product

Order

User

Invoice

۲. Repository

Repository وظیفه دسترسی به Entityها را بر عهده دارد.

برای مثال:


$productRepository->save($product);

Repository می‌تواند از PDO، Eloquent یا Data Mapper استفاده کند.

۳. Unit of Work

Unit of Work تغییرات را جمع‌آوری و هماهنگ می‌کند.

۴. Transaction

در صورت نیاز، عملیات Commit درون یک Transaction دیتابیس اجرا می‌شود.

ساختار کلی:


Service

   ↓

Unit of Work

   ↓

Repositories

   ↓

Database Transaction


# یک مثال ساده در PHP

برای درک بهتر، یک سیستم ساده مدیریت محصول ایجاد کنیم.

ابتدا Entity محصول را می‌سازیم.


<?php



declare(strict_types=1);



final class Product

{

    public function __construct(

        private int $id,

        private string $name,

        private int $price

    ) {

    }



    public function getId(): int

    {

        return $this->id;

    }



    public function getName(): string

    {

        return $this->name;

    }



    public function getPrice(): int

    {

        return $this->price;

    }



    public function changePrice(int $price): void

    {

        if ($price < 0) {

            throw new InvalidArgumentException(

                'Price cannot be negative.'

            );

        }



        $this->price = $price;

    }

}

اکنون Product یک Entity مستقل از دیتابیس است.


# ساخت Repository

حالا Interface مربوط به Repository را ایجاد می‌کنیم:


<?php



declare(strict_types=1);



interface ProductRepositoryInterface

{

    public function findById(int $id): ?Product;



    public function save(Product $product): void;

}

یک پیاده‌سازی ساده In-Memory نیز می‌توانیم داشته باشیم:


<?php



declare(strict_types=1);



final class InMemoryProductRepository

    implements ProductRepositoryInterface

{

    private array $products = [];



    public function findById(int $id): ?Product

    {

        return $this->products[$id] ?? null;

    }



    public function save(Product $product): void

    {

        $this->products[$product->getId()] = $product;

    }

}


# ساخت Unit of Work

حالا مهم‌ترین بخش را ایجاد می‌کنیم.


<?php



declare(strict_types=1);



final class UnitOfWork

{

    private array $newObjects = [];



    private array $dirtyObjects = [];



    private array $deletedObjects = [];



    public function registerNew(object $object): void

    {

        $this->newObjects[] = $object;

    }



    public function registerDirty(object $object): void

    {

        $this->dirtyObjects[] = $object;

    }



    public function registerDeleted(object $object): void

    {

        $this->deletedObjects[] = $object;

    }



    public function commit(): void

    {

        // Persist changes

    }

}

اکنون Unit of Work سه نوع تغییر را می‌تواند ثبت کند:


New

Dirty

Deleted


# چرا به Dirty Object نیاز داریم؟

فرض کنید محصولی از دیتابیس خوانده شده است:


$product = $repository->findById(10);

بعد قیمت آن تغییر می‌کند:


$product->changePrice(2500000);

Unit of Work باید بداند که این شیء تغییر کرده است.

پس می‌توانیم آن را ثبت کنیم:


$unitOfWork->registerDirty($product);

در نهایت:


$unitOfWork->commit();

باعث می‌شود تغییرات ثبت‌شده پردازش شوند.


# ساخت Unit of Work کامل‌تر

برای اینکه مثال عملی‌تر باشد، Repository را نیز داخل Unit of Work قرار می‌دهیم.


<?php



declare(strict_types=1);



final class ProductUnitOfWork

{

    private array $newProducts = [];



    private array $dirtyProducts = [];



    private array $deletedProducts = [];



    public function __construct(

        private ProductRepositoryInterface $repository

    ) {

    }



    public function registerNew(Product $product): void

    {

        $this->newProducts[] = $product;

    }



    public function registerDirty(Product $product): void

    {

        $this->dirtyProducts[] = $product;

    }



    public function registerDeleted(Product $product): void

    {

        $this->deletedProducts[] = $product;

    }



    public function commit(): void

    {

        foreach ($this->newProducts as $product) {

            $this->repository->save($product);

        }



        foreach ($this->dirtyProducts as $product) {

            $this->repository->save($product);

        }



        $this->clear();

    }



    private function clear(): void

    {

        $this->newProducts = [];

        $this->dirtyProducts = [];

        $this->deletedProducts = [];

    }

}

این مثال عمداً ساده است تا مفهوم اصلی مشخص باشد.

در یک سیستم واقعی، عملیات حذف، Transaction، مدیریت خطا و جلوگیری از ثبت تکراری نیز باید مدیریت شوند.


# استفاده از Unit of Work

اکنون می‌توانیم چنین کدی داشته باشیم:


$product = new Product(

    id: 1,

    name: 'Keyboard',

    price: 1500000

);



$unitOfWork->registerNew($product);



$unitOfWork->commit();

در اینجا محصول بلافاصله از داخل Entity ذخیره نمی‌شود.

ابتدا در Unit of Work ثبت می‌شود:


Product

   ↓

registerNew()

   ↓

Unit of Work

   ↓

commit()

   ↓

Repository


# Unit of Work همراه با Transaction

حالا فرض کنیم از PDO استفاده می‌کنیم.

برای عملیات حساس بهتر است Commit مربوط به Unit of Work داخل یک Database Transaction انجام شود.

ساختار کلی:


$pdo->beginTransaction();



try {



    $unitOfWork->commit();



    $pdo->commit();



} catch (Throwable $e) {



    $pdo->rollBack();



    throw $e;

}

در این حالت اگر عملیات با خطا مواجه شود، Transaction می‌تواند Rollback شود.

مثلاً:


BEGIN TRANSACTION



INSERT Order

INSERT OrderItem

UPDATE Product

INSERT Payment



        ↓



اگر همه موفق بودند

        ↓

COMMIT

و اگر یکی از عملیات شکست خورد:


BEGIN TRANSACTION



INSERT Order

INSERT OrderItem

UPDATE Product

INSERT Payment ❌



        ↓



ROLLBACK

در نتیجه تغییرات این عملیات به صورت یک مجموعه مدیریت می‌شوند.


# Unit of Work در پروژه فروشگاهی

فرض کنید کاربر محصولی را خریداری کرده است.

این عملیات ممکن است شامل موارد زیر باشد:


Order

OrderItem

Product

Wallet

Payment

در Service:


public function purchase(

    User $user,

    Product $product

): void {



    $order = $this->createOrder($user);



    $orderItem = $this->createOrderItem(

        $order,

        $product

    );



    $this->decreaseStock($product);



    $this->unitOfWork->registerNew($order);



    $this->unitOfWork->registerNew($orderItem);



    $this->unitOfWork->registerDirty($product);



    $this->unitOfWork->commit();

}

در پروژه واقعی، این عملیات باید با Transaction دیتابیس نیز هماهنگ شود.


# تفاوت Unit of Work و Repository

این دو Pattern اغلب با هم استفاده می‌شوند، اما وظیفه یکسانی ندارند.

Repository

Repository بیشتر روی دسترسی به Entity تمرکز دارد.

مثلاً:


$productRepository->findById(10);

یا:


$productRepository->save($product);

Repository یک واسط شبیه Collection برای دسترسی به اشیای دامنه ایجاد می‌کند.

Unit of Work

Unit of Work روی مدیریت مجموعه‌ای از تغییرات تمرکز دارد.

مثلاً:


New:

Order



Updated:

Product

Wallet



Deleted:

CartItem

سپس همه این تغییرات در فرآیند Commit مدیریت می‌شوند.

به صورت خلاصه:


Repository

    ↓

دسترسی به Entity



Unit of Work

    ↓

مدیریت مجموعه تغییرات


# تفاوت Unit of Work و Transaction

این دو مفهوم را نباید با یکدیگر اشتباه گرفت.

Transaction

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

مثلاً:


$pdo->beginTransaction();



try {



    // database operations



    $pdo->commit();



} catch (Throwable $e) {



    $pdo->rollBack();



    throw $e;

}

Unit of Work

Unit of Work یک الگوی نرم‌افزاری برای پیگیری تغییرات Entityها و هماهنگ کردن عملیات ذخیره‌سازی است.

بنابراین می‌توان گفت:


Unit of Work

     +

Database Transaction

می‌توانند مکمل یکدیگر باشند.

Unit of Work مشخص می‌کند چه تغییراتی باید اعمال شوند و Transaction مشخص می‌کند این تغییرات چگونه از نظر اتمیک بودن در دیتابیس اجرا شوند.


# Unit of Work و Data Mapper

Unit of Work معمولاً در معماری‌هایی که Entityها از Persistence جدا هستند، اهمیت بیشتری پیدا می‌کند.

Data Mapper مسئول انتقال داده بین Entity و دیتابیس است، در حالی که Unit of Work تغییرات مربوط به چند Entity را پیگیری و هماهنگ می‌کند. Martin Fowler هر دو را در مجموعه الگوهای مربوط به معماری برنامه‌های سازمانی قرار می‌دهد.

معماری می‌تواند به این شکل باشد:


Application Service

        ↓

   Unit of Work

        ↓

   Repository

        ↓

   Data Mapper

        ↓

    Database

در چنین معماری‌ای هر بخش مسئولیت مشخصی دارد.


# Unit of Work در Laravel

Laravel امکانات قدرتمندی برای Database Transaction دارد:


DB::transaction(function () {



    // عملیات دیتابیس



});

بنابراین در Laravel برای بسیاری از پروژه‌ها نیازی نیست Unit of Work را از صفر پیاده‌سازی کنیم.

مثلاً:


use Illuminate\Support\Facades\DB;



DB::transaction(function () use ($order, $product) {



    $order->save();



    $product->decrement('stock');



});

اگر Exception رخ دهد، Laravel Transaction را مدیریت می‌کند.

اما باید توجه داشت که DB::transaction() به تنهایی معادل کامل Unit of Work نیست.

Transaction بیشتر مسئول کنترل تراکنش دیتابیس است، در حالی که Unit of Work در معنای معماری، مسئول پیگیری مجموعه تغییرات و هماهنگی عملیات Persistence است.


# آیا Eloquent خودش Unit of Work است؟

این سؤال مهمی است.

Eloquent امکاناتی برای مدیریت Modelها، تغییرات و ذخیره‌سازی دارد، اما نباید صرفاً به دلیل وجود save() یا Dirty Tracking نتیجه بگیریم که Eloquent دقیقاً همان Unit of Work کلاسیک است.

Unit of Work یک مفهوم معماری مشخص دارد و معمولاً شامل مدیریت مجموعه‌ای از Entityهای تحت تأثیر یک Business Transaction و هماهنگی عملیات Persistence است.

در Laravel معمولاً برای تراکنش‌های ساده می‌توان از:


DB::transaction()

و برای عملیات پیچیده‌تر از ترکیب:


Service

+

Repository

+

Transaction

استفاده کرد.

اگر معماری پروژه نیاز داشته باشد، می‌توان Unit of Work اختصاصی نیز طراحی کرد.


# یک نمونه Unit of Work در Laravel

برای مثال Interface زیر را ایجاد می‌کنیم:


<?php



declare(strict_types=1);



interface UnitOfWorkInterface

{

    public function begin(): void;



    public function commit(): void;



    public function rollback(): void;

}

سپس پیاده‌سازی Laravel:


<?php



declare(strict_types=1);



namespace App\Infrastructure;



use Illuminate\Support\Facades\DB;



final class LaravelUnitOfWork

{

    public function begin(): void

    {

        DB::beginTransaction();

    }



    public function commit(): void

    {

        DB::commit();

    }



    public function rollback(): void

    {

        DB::rollBack();

    }

}

و استفاده:


$unitOfWork->begin();



try {



    $orderRepository->save($order);



    $productRepository->save($product);



    $walletRepository->save($wallet);



    $unitOfWork->commit();



} catch (Throwable $e) {



    $unitOfWork->rollback();



    throw $e;

}

البته در Laravel معمولاً DB::transaction() ساده‌تر و خواناتر است:


DB::transaction(function () use (

    $order,

    $product,

    $wallet

) {



    $orderRepository->save($order);



    $productRepository->save($product);



    $walletRepository->save($wallet);

});

پس نباید صرفاً برای استفاده از Design Pattern، معماری را پیچیده‌تر کنیم.


# مزایای Unit of Work Pattern

۱. مدیریت متمرکز تغییرات

تغییرات مربوط به چند Entity می‌توانند در یک مکان مدیریت شوند.

۲. کاهش ذخیره‌سازی‌های پراکنده

به جای اینکه هر قسمت برنامه بلافاصله داده را ذخیره کند، عملیات می‌تواند تا مرحله Commit مدیریت شود.

۳. مناسب برای عملیات چند Entity

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

۴. هماهنگی بهتر با Transaction

Unit of Work و Transaction می‌توانند برای مدیریت عملیات چندمرحله‌ای در کنار یکدیگر استفاده شوند.

۵. مناسب برای معماری‌های پیچیده

در سیستم‌هایی که Repository، Data Mapper و Domain Model وجود دارند، Unit of Work می‌تواند نقش هماهنگ‌کننده Persistence را ایفا کند.


# معایب Unit of Work

Unit of Work نیز مانند سایر Design Patternها همیشه بهترین انتخاب نیست.

۱. افزایش پیچیدگی

برای یک CRUD ساده، ایجاد Unit of Work ممکن است بیش از نیاز پروژه باشد.

۲. نیاز به مدیریت State

Unit of Work باید بداند کدام Entity جدید، تغییرکرده یا حذف‌شده است.

۳. مدیریت همزمانی پیچیده‌تر

در سیستم‌های بزرگ ممکن است مسائل مربوط به Concurrency و وضعیت داده‌ها اهمیت بیشتری پیدا کنند.

۴. احتمال ایجاد Abstraction اضافی

اگر Unit of Work صرفاً یک Wrapper دور DB::transaction() باشد، ممکن است ارزش معماری آن محدود باشد.


# چه زمانی از Unit of Work استفاده کنیم؟

Unit of Work بیشتر زمانی ارزش خود را نشان می‌دهد که یک عملیات تجاری چندین Entity را تغییر دهد.

برای مثال:


ثبت سفارش

    ↓

Order

OrderItem

Product

Wallet

Payment

یا:


پرداخت قبض

    ↓

Invoice

Wallet

Transaction

Notification

یا در یک سیستم مدیریتی:


ثبت فاکتور

    ↓

Invoice

InvoiceItems

ProductStock

AccountingTransaction

در چنین فرآیندهایی، مدیریت متمرکز تغییرات می‌تواند مفید باشد.


# چه زمانی از Unit of Work استفاده نکنیم؟

اگر پروژه فقط چنین عملیاتی دارد:


$product = Product::find($id);



$product->name = 'New Name';



$product->save();

احتمالاً ساخت یک Unit of Work کامل ضرورتی ندارد.

همچنین در Laravel اگر فقط چند Query ساده در یک Transaction دارید، ممکن است همین کافی باشد:


DB::transaction(function () {



    // queries



});

هدف Design Pattern این نیست که تعداد کلاس‌ها را افزایش دهیم؛ هدف آن حل یک مسئله معماری مشخص است.


# Unit of Work و سیستم‌های مالی

در سیستم‌های مالی، Unit of Work می‌تواند برای فرآیندهایی که چند رکورد باید هماهنگ تغییر کنند، کاربرد داشته باشد.

مثلاً فرض کنید کاربر 500 هزار تومان پرداخت می‌کند:


Wallet

    ↓

-500,000



Order

    ↓

Paid



Payment

    ↓

Created



Transaction

    ↓

Debit

اگر یکی از عملیات شکست بخورد، نباید سیستم در وضعیتی قرار بگیرد که مثلاً موجودی کیف پول کم شده ولی Payment ثبت نشده باشد.

در اینجا ترکیب:


Application Service

       ↓

Unit of Work

       ↓

Repositories

       ↓

Database Transaction

می‌تواند ساختار مناسبی برای مدیریت این فرآیند باشد.


# تست Unit of Work

یکی از نکات مهم، تست رفتار Unit of Work است.

برای مثال می‌توان بررسی کرد که Entityهای جدید ثبت می‌شوند:


$unitOfWork->registerNew($product);



$unitOfWork->commit();



$product = $repository->findById(1);



$this->assertNotNull($product);

همچنین می‌توان رفتار Rollback را بررسی کرد.

در تست‌های معماری پیچیده‌تر، می‌توان Repositoryهای Fake یا In-Memory ایجاد کرد تا تست‌ها به دیتابیس واقعی وابسته نباشند.

طراحی‌های نمونه Repository در PHP نیز معمولاً امکان استفاده از In-Memory Persistence را برای تست نشان می‌دهند.


# اشتباهات رایج در استفاده از Unit of Work

اشتباه اول: یکی دانستن Unit of Work با Transaction

این دو مفهوم مرتبط هستند اما یکی نیستند.


Transaction ≠ Unit of Work

اشتباه دوم: ساخت Unit of Work برای هر Query

Unit of Work برای مدیریت یک مجموعه تغییرات مرتبط معنا پیدا می‌کند، نه برای هر Query ساده.

اشتباه سوم: قرار دادن Business Logic داخل Unit of Work

Unit of Work نباید تبدیل به Service عظیمی شود که تمام منطق کسب‌وکار را در خود نگه می‌دارد.

اشتباه چهارم: مدیریت نکردن Rollback

اگر Unit of Work با Transaction دیتابیس کار می‌کند، مسیر خطا و Rollback باید به‌درستی طراحی شود.

اشتباه پنجم: پیچیده کردن بی‌دلیل Laravel

اگر DB::transaction() تمام نیاز پروژه را برطرف می‌کند، ایجاد یک سیستم Unit of Work چندصد خطی احتمالاً ارزش معماری کافی ندارد.


# ساختار پیشنهادی در یک پروژه بزرگ PHP

در یک پروژه بزرگ می‌توان معماری را به این شکل طراحی کرد:


app/

│

├── Domain/

│   ├── Orders/

│   │   ├── Order.php

│   │   └── OrderItem.php

│   │

│   └── Products/

│       └── Product.php

│

├── Application/

│   └── Orders/

│       └── CreateOrderService.php

│

├── Repositories/

│   ├── OrderRepositoryInterface.php

│   └── ProductRepositoryInterface.php

│

├── Infrastructure/

│   ├── Persistence/

│   │   ├── EloquentOrderRepository.php

│   │   └── EloquentProductRepository.php

│   │

│   └── UnitOfWork/

│       └── LaravelUnitOfWork.php

│

└── Providers/

    └── AppServiceProvider.php

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


Controller

    ↓

Application Service

    ↓

Unit of Work

    ↓

Repositories

    ↓

Eloquent / Data Mapper

    ↓

Database

این معماری برای پروژه‌های بزرگ‌تر قابل استفاده است؛ اما ساختار دقیق باید بر اساس نیاز واقعی پروژه تعیین شود.


# Repository + Data Mapper + Unit of Work

اکنون می‌توانیم سه Pattern مهمی را که در این کلاستر بررسی کرده‌ایم کنار هم قرار دهیم.

Repository

مسئول فراهم کردن دسترسی شی‌ءگرایانه به Entityها:


ProductRepository

Data Mapper

مسئول تبدیل بین Entity و داده Persistence:


Product Entity

      ↕

Data Mapper

      ↕

Database Row

Unit of Work

مسئول هماهنگ کردن مجموعه تغییرات:


Product

Order

Payment

Wallet



      ↓



Unit of Work



      ↓



Commit

در یک معماری پیچیده می‌توان این سه را به صورت زیر ترکیب کرد:


                    Application Service

                           │

                           ▼

                     Unit of Work

                      /        \

                     /          \

                    ▼            ▼

              Repository     Repository

                  │                │

                  ▼                ▼

             Data Mapper      Data Mapper

                  │                │

                   \              /

                    \            /

                       Database

این ترکیب از مفاهیم شناخته‌شده در الگوهای معماری Enterprise Application است.


# آیا Unit of Work باعث افزایش سرعت می‌شود؟

نباید Unit of Work را صرفاً یک تکنیک Performance در نظر گرفت.

یکی از مشکلاتی که Fowler درباره ذخیره‌سازی تک‌به‌تک تغییرات مطرح می‌کند، افزایش تعداد فراخوانی‌های کوچک به دیتابیس است؛ Unit of Work می‌تواند با جمع‌آوری تغییرات و هماهنگ‌کردن نوشتن آن‌ها، این نوع عملیات را مدیریت کند.

اما عملکرد نهایی به عوامل زیادی بستگی دارد:

  • نوع دیتابیس
  • تعداد Queryها
  • Indexها
  • Transactionها
  • حجم داده
  • Connection Pool
  • Batch Operations
  • Cache
  • طراحی Query

بنابراین نباید نتیجه گرفت که استفاده از Unit of Work همیشه باعث سریع‌تر شدن برنامه می‌شود.


# جمع‌بندی

Unit of Work Pattern در PHP الگویی برای مدیریت مجموعه‌ای از تغییرات مرتبط در جریان یک عملیات تجاری است.

به جای اینکه هر Entity به صورت مستقل و پراکنده ذخیره شود، Unit of Work می‌تواند تغییرات را جمع‌آوری کرده و در زمان Commit آن‌ها را به لایه Persistence منتقل کند.

به صورت خلاصه:


Entity

   ↓

Repository

   ↓

Unit of Work

   ↓

Transaction

   ↓

Database

البته در معماری‌های مختلف ممکن است ترتیب و مسئولیت‌ها متفاوت باشد.

مهم‌ترین نکته این است که Unit of Work را با Database Transaction اشتباه نگیریم.

Transaction مکانیزم دیتابیس برای اجرای اتمیک عملیات است، در حالی که Unit of Work الگویی برای پیگیری و هماهنگ‌سازی تغییرات موجودیت‌هاست.

در PHP خام می‌توان Unit of Work را با PDO و Repository پیاده‌سازی کرد و در Laravel، بسته به نیاز پروژه، می‌توان از DB::transaction() یا یک abstraction اختصاصی استفاده کرد.

اگر پروژه ساده است، استفاده مستقیم از Transaction احتمالاً کافی است؛ اما در سیستم‌های بزرگ‌تر که چند Entity و چند Repository در یک Business Transaction تغییر می‌کنند، Unit of Work می‌تواند نقش مهمی در معماری داشته باشد.


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

Unit of Work Pattern در PHP چیست؟

Unit of Work یک الگوی معماری برای پیگیری مجموعه‌ای از Entityهای جدید، تغییرکرده یا حذف‌شده و هماهنگ کردن عملیات ذخیره‌سازی آن‌هاست.

تفاوت Unit of Work و Transaction چیست؟

Transaction مکانیزم دیتابیس برای مدیریت اتمیک عملیات است؛ Unit of Work یک الگوی نرم‌افزاری برای مدیریت تغییرات Entityهاست.

تفاوت Repository و Unit of Work چیست؟

Repository بیشتر مسئول دسترسی به Entityهاست، در حالی که Unit of Work مجموعه‌ای از تغییرات مرتبط را مدیریت و هماهنگ می‌کند.

آیا Unit of Work در Laravel وجود دارد؟

Laravel ابزارهایی مانند DB::transaction() برای مدیریت تراکنش دارد. اما این ابزار را نباید بدون توجه به تعریف معماری، معادل کامل Unit of Work کلاسیک در نظر گرفت.

آیا برای هر پروژه PHP باید Unit of Work بسازیم؟

خیر. برای پروژه‌های ساده، ساخت Unit of Work ممکن است پیچیدگی غیرضروری ایجاد کند. زمانی استفاده از آن منطقی‌تر است که یک عملیات تجاری چند Entity یا Repository را هم‌زمان تغییر دهد.

آیا Unit of Work با Repository استفاده می‌شود؟

بله. Repository و Unit of Work می‌توانند مکمل یکدیگر باشند. Repository مسئول دسترسی به Entityهاست و Unit of Work می‌تواند تغییرات چند Repository را در یک فرآیند هماهنگ کند.

آیا Unit of Work فقط برای دیتابیس است؟

این الگو در معماری‌های مربوط به Persistence و تغییر داده‌ها کاربرد دارد و معمولاً در ارتباط با دیتابیس و سیستم ذخیره‌سازی دیده می‌شود؛ اما مفهوم اصلی آن مدیریت مجموعه تغییرات یک Business Transaction است.

آیا Unit of Work باعث افزایش Performance می‌شود؟

لزوماً خیر. یکی از کاربردهای آن کاهش ذخیره‌سازی‌های پراکنده و هماهنگ کردن عملیات Persistence است، اما عملکرد نهایی به طراحی کل سیستم و دیتابیس بستگی دارد.

نظرات (0)

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

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

مقالات مرتبط

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

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

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

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

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

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

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

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

۲۲ دقیقه