در پروژههای کوچک PHP معمولاً هر بار که تغییری در یک شیء ایجاد میکنیم، همان لحظه آن را در دیتابیس ذخیره میکنیم.
برای مثال:
$product->save();
اما در پروژههای بزرگتر، یک عملیات تجاری ممکن است همزمان چندین شیء را تغییر دهد.
فرض کنید در یک فروشگاه اینترنتی کاربر یک سفارش ایجاد میکند. در همین عملیات ممکن است لازم باشد:
- سفارش ایجاد شود.
- آیتمهای سفارش ذخیره شوند.
- موجودی محصولات کاهش پیدا کند.
- کیف پول کاربر بهروزرسانی شود.
- تراکنش مالی ثبت شود.
اگر مرحله چهارم با خطا مواجه شود، نمیخواهیم سه مرحله اول در دیتابیس باقی بمانند.
اینجاست که مفهوم 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 است، اما عملکرد نهایی به طراحی کل سیستم و دیتابیس بستگی دارد.





