Alasan saya mengatakan bahwa transaksi tidak termasuk dalam lapisan model pada dasarnya adalah ini:
Model dapat memanggil metode dalam model lain.
Jika model mencoba memulai transaksi, tetapi model tersebut tidak mengetahui apakah pemanggilnya sudah memulai transaksi, maka model harus secara kondisional memulai transaksi, seperti yang ditunjukkan pada contoh kode di jawaban @Bubba . Metode model harus menerima tanda sehingga pemanggil dapat memberitahunya apakah diizinkan untuk memulai transaksinya sendiri atau tidak. Atau model harus memiliki kemampuan untuk menanyakan status "dalam transaksi" pemanggilnya.
public function setPrivacy($privacy, $caller){
if (! $caller->isInTransaction() ) $this->beginTransaction();
$this->privacy = $privacy;
// ...action code..
if (! $caller->isInTransaction() ) $this->commit();
}
Bagaimana jika penelepon bukan objek? Di PHP, ini bisa berupa metode statis atau kode sederhana yang tidak berorientasi objek. Ini menjadi sangat berantakan, dan menyebabkan banyak kode berulang dalam model.
Ini juga merupakan contoh Kontrol Kopling , yang dianggap buruk karena pemanggil harus mengetahui sesuatu tentang cara kerja internal objek yang dipanggil. Misalnya, beberapa dari metode Model Anda mungkin memiliki parameter $transactional, tetapi metode lain mungkin tidak memiliki parameter itu. Bagaimana penelepon bisa tahu ketika parameter itu penting?
// I need to override method's attempt to commit
$video->setPrivacy($privacy, false);
// But I have no idea if this method might attempt to commit
$video->setFormat($format);
Solusi lain yang saya lihat disarankan (atau bahkan diimplementasikan dalam beberapa kerangka kerja seperti Propel) adalah membuat beginTransaction()
dan commit()
no-ops ketika DBAL tahu itu sudah dalam transaksi. Tetapi ini dapat menyebabkan anomali jika model Anda mencoba untuk melakukan dan menemukan bahwa itu tidak benar-benar berkomitmen. Atau mencoba melakukan rollback dan permintaan itu diabaikan. Saya telah menulis tentang anomali ini sebelumnya.
Kompromi yang saya sarankan adalah Model tidak tahu tentang transaksi . Model tidak tahu apakah permintaannya ke setPrivacy()
adalah sesuatu yang harus segera dilakukan atau merupakan bagian dari gambaran yang lebih besar, rangkaian perubahan yang lebih kompleks yang melibatkan banyak Model dan harus hanya berkomitmen jika semua perubahan ini berhasil. Itulah inti dari transaksi.
Jadi jika Model tidak tahu apakah mereka dapat atau harus memulai dan melakukan transaksi mereka sendiri, lalu siapa yang tahu? GRASP menyertakan Pola pengontrol yang merupakan kelas non-UI untuk kasus penggunaan, dan diberi tanggung jawab untuk membuat dan mengontrol semua bagian untuk menyelesaikan kasus penggunaan itu. Pengendali mengetahui tentang transaksi karena di situlah semua informasi dapat diakses tentang apakah kasus penggunaan lengkap itu kompleks, dan memerlukan banyak perubahan untuk dilakukan di Model, dalam satu transaksi (atau mungkin dalam beberapa transaksi).
Contoh yang sudah saya tulis sebelumnya, yaitu memulai transaksi di beforeAction()
metode Kontroler MVC dan komit di afterAction()
metode, adalah penyederhanaan . Pengendali harus bebas untuk memulai dan melakukan transaksi sebanyak yang diperlukan secara logis untuk menyelesaikan tindakan saat ini. Atau terkadang Pengendali dapat menahan diri dari kontrol transaksi eksplisit, dan mengizinkan Model untuk melakukan autocommit setiap perubahan.
Tetapi intinya adalah bahwa informasi tentang transaksi apa yang diperlukan adalah sesuatu yang tidak diketahui oleh Model -- mereka harus diberitahu (dalam bentuk parameter $transactional) atau menanyakannya dari pemanggil mereka, yang harus mendelegasikan pertanyaan sampai ke tindakan Pengendali.
Anda juga dapat membuat Lapisan Layanan kelas yang masing-masing tahu bagaimana mengeksekusi kasus penggunaan yang kompleks seperti itu, dan apakah akan menyertakan semua perubahan dalam satu transaksi. Dengan begitu Anda menghindari banyak kode berulang. Tapi itu tidak umum untuk aplikasi PHP untuk menyertakan Service Layer yang berbeda; tindakan Controller biasanya bertepatan dengan Service Layer.