0Pricing
PHP Academy · Pelajaran

Membangun Alur Kerja Berbasis Peristiwa

Koordinasikan layanan melalui peristiwa dan idempotensi.

Membangun Alur Kerja Berbasis Peristiwa adalah pelajaran PHP Academy gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar PHP Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus PHP Academy mencakup 4 pelajaran total.

Alur Kerja Berbasis Peristiwa

Satu pesan mudah ditangani. Sebuah alur kerja — "pesanan dibuat → cadangkan stok → tagih kartu → kirim → beri tahu" yang melintasi beberapa layanan — adalah saat rancangan berbasis peristiwa menunjukkan manfaatnya, dan juga saat rancangan itu dapat menyulitkan Anda jika dibuat secara naif.

Pelajaran ini membahas koreografi dan orkestrasi, pola kotak keluar untuk penerbitan atomik, saga untuk pembatalan terdistribusi, serta idempotensi yang menyatukan semuanya.

Koreografi dan Orkestrasi

Ada dua cara untuk mengoordinasikan alur dengan banyak langkah:

  • Koreografi — setiap layanan bereaksi terhadap peristiwa dan menerbitkan peristiwanya sendiri; tidak ada pusat kendali. Keterkaitannya longgar, tetapi alur keseluruhan tersirat dan sulit dilacak.
  • Orkestrasi — koordinator pusat memberi tahu setiap layanan apa yang harus dilakukan berikutnya. Jelas dan mudah diamati, tetapi orkestrator menjadi titik keterkaitan.

Aturan praktis: gunakan koreografi untuk penyebaran sederhana, dan orkestrasi saat alur memiliki banyak langkah berurutan serta memerlukan status yang terlihat.

Peristiwa dan Perintah

Beri nama pesan Anda dengan sengaja:

  • Sebuah peristiwa menyatakan fakta yang telah terjadi: OrderPlaced. Penerbit tidak memedulikan siapa yang mendengarkan.
  • Sebuah perintah meminta tindakan yang akan dilakukan oleh penangan tertentu: ChargeCard.

Peristiwa menggerakkan koreografi; perintah menggerakkan orkestrasi. Mencampur kosakata tersebut (sebuah "peristiwa" yang diam-diam mengharapkan satu penangan) adalah sumber umum ketergantungan tersembunyi.

<?php
final class OrderPlaced {
    public function __construct(
        public readonly string $orderId,
        public readonly string $customerId,
        public readonly int $amountCents,
        public readonly string $occurredAt,
    ) {}
}

$e = new OrderPlaced('o-42', 'c-7', 1990, gmdate('c'));
echo json_encode($e), "\n";

Masalah Penulisan Ganda

Kesalahan klasiknya adalah penangan memperbarui basis data dan menerbitkan pesan sebagai dua operasi terpisah. Jika proses berhenti di antara keduanya, terjadi ketidakkonsistenan — baris berubah tetapi tidak ada peristiwa yang dikirim, atau sebaliknya.

<?php
// BROKEN: not atomic. A crash between the two lines corrupts state.
function placeOrder(PDO $db, $broker, array $o): void {
    $db->prepare('INSERT INTO orders ...')->execute($o);
    // <-- crash here = row exists but no event ever published
    $broker->publish('OrderPlaced', json_encode($o));
}

Kotak Keluar Transaksional

Solusinya adalah pola kotak keluar: dalam transaksi DB yang sama dengan transaksi yang mengubah data Anda, masukkan peristiwa ke tabel outbox. Proses relai terpisah membaca baris yang belum diterbitkan dan mengirimkannya ke perantara. Satu komit atomik, tanpa penulisan ganda.

<?php
function placeOrder(PDO $db, array $o): void {
    $db->beginTransaction();
    $db->prepare('INSERT INTO orders (id, total) VALUES (?, ?)')
       ->execute([$o['id'], $o['total']]);
    // Same transaction -> atomic with the business write
    $db->prepare('INSERT INTO outbox (id, type, payload) VALUES (?, ?, ?)')
       ->execute([bin2hex(random_bytes(8)), 'OrderPlaced', json_encode($o)]);
    $db->commit();
}

Relai (Penerbit)

Sebuah proses pekerja memeriksa kotak keluar secara berkala (atau mengikuti log perubahan DB melalui CDC), menerbitkan setiap baris, lalu menandainya sebagai terkirim. Karena the relai dapat berhenti setelah menerbitkan tetapi sebelum menandai, relai itu sendiri beroperasi setidaknya sekali — hal ini tidak masalah karena konsumen bersifat idempoten.

<?php
function relayOutbox(PDO $db, $broker): void {
    $rows = $db->query(
        'SELECT id, type, payload FROM outbox
         WHERE published_at IS NULL ORDER BY created_at LIMIT 100'
    )->fetchAll(PDO::FETCH_ASSOC);

    foreach ($rows as $r) {
        $broker->publish($r['type'], $r['payload'], messageId: $r['id']);
        $db->prepare('UPDATE outbox SET published_at = now() WHERE id = ?')
           ->execute([$r['id']]);
    }
}

Konsumen Idempoten (Lagi)

Karena relai dan perantara sama-sama beroperasi setidaknya sekali, penangan hilir akan melihat duplikat. Setiap konsumen mencatat pengenal pesan yang telah diproses dan langsung melewati pengulangan — gerbang deduplikasi yang sama seperti yang Anda pelajari sebelumnya, kini diterapkan per layanan.

<?php
function onOrderPlaced(PDO $db, string $messageId, array $data): void {
    $db->beginTransaction();
    try {
        $db->prepare('INSERT INTO inbox (message_id) VALUES (?)')
           ->execute([$messageId]); // unique index = dedup
    } catch (PDOException $e) {
        $db->rollBack();
        return; // already handled this message
    }
    reserveStock($data['orderId']);
    $db->commit();
}
function reserveStock(string $id): void {}

Saga: Pembatalan Terdistribusi

Anda tidak dapat membuka satu transaksi ACID di seluruh layanan. Sebuah saga memodelkan alur yang berjalan lama sebagai rangkaian transaksi lokal, masing-masing dengan tindakan kompensasi yang membatalkannya. Jika langkah 3 gagal, jalankan tindakan kompensasi untuk langkah 2 dan 1 secara terbalik.

Contoh: pembayaran gagal setelah stok dicadangkan → terbitkan ReleaseStock sebagai kompensasi. Tidak ada pembatalan otomatis — Anda harus merancang tindakan pembatalan untuk setiap langkah.

Saga Terorkestrasi

Orkestrator menggerakkan saga: orkestrator melanjutkan saat berhasil dan mengirimkan tindakan kompensasi saat gagal. Simpan status saga agar the saga dapat dilanjutkan setelah proses berhenti.

<?php
function handleStepResult(array $saga, string $step, bool $ok, $bus): array {
    if ($ok) {
        $next = ['reserveStock' => 'chargeCard', 'chargeCard' => 'ship'][$step] ?? null;
        if ($next) { $bus->send($next, $saga['orderId']); $saga['state'] = $next; }
        else { $saga['state'] = 'completed'; }
    } else {
        // Run compensations in reverse for whatever already succeeded
        foreach (array_reverse($saga['done']) as $s) {
            $bus->send('compensate.' . $s, $saga['orderId']);
        }
        $saga['state'] = 'compensating';
    }
    return $saga;
}

Batas Waktu pada Alur Panjang

Satu langkah saga dapat saja tidak pernah mengirimkan tanggapan — layanan pembayaran tidak aktif, atau persetujuan manusia tidak pernah datang. Tanpa batas waktu, saga akan menggantung selamanya sambil menahan reservasi. Simpan tenggat untuk setiap langkah; penjadwal memindai saga yang melewati tenggat dan memicu jalur kegagalan atau kompensasi.

<?php
function reapTimedOutSagas(PDO $db, $bus): void {
    $rows = $db->query(
        "SELECT order_id, state FROM sagas
         WHERE state NOT IN ('completed','compensating')
           AND deadline_at < now()"
    )->fetchAll(PDO::FETCH_ASSOC);

    foreach ($rows as $r) {
        echo "Saga {$r['order_id']} timed out at step {$r['state']}\n";
        $bus->send('saga.compensate', $r['order_id']); // trigger rollback
    }
}

Penerapan Versi & Observabilitas

Alur kerja berlangsung bertahun-tahun; peristiwa harus berkembang dengan aman:

  • Tambahkan version (atau skema) ke setiap peristiwa; konsumen harus dapat menangani kolom baru yang tidak dikenal dan tidak pernah mengasumsikan bahwa suatu kolom selalu ada.
  • Utamakan perubahan yang bersifat menambahkan; jangan pernah mengubah arti kolom yang sudah ada.
  • Teruskan ID korelasi melalui setiap pesan agar Anda dapat melacak satu transaksi bisnis di semua layanan melalui pencatatan dan pelacakan.

Tanpa ID korelasi, menelusuri masalah dalam alur yang dikoreografikan di lima layanan hampir mustahil.

<?php
$envelope = [
    'type'          => 'OrderPlaced',
    'version'       => 2,
    'correlationId' => $incoming['correlationId'] ?? bin2hex(random_bytes(8)),
    'occurredAt'    => gmdate('c'),
    'data'          => ['orderId' => 'o-42'],
];
echo json_encode($envelope, JSON_PRETTY_PRINT), "\n";

Pemeriksaan Singkat

Menghindari masalah penulisan ganda.

Ringkasan

Sekarang Anda dapat merancang alur kerja berbasis peristiwa yang andal:

  • Pilih koreografi (peristiwa) atau orkestrasi (perintah) berdasarkan kompleksitas alurnya.
  • Atasi penulisan ganda dengan kotak keluar transaksional + penyampai.
  • Jadikan setiap konsumen idempoten melalui kunci kotak masuk/deduplikasi.
  • Gunakan saga dengan tindakan kompensasi untuk pembatalan terdistribusi.
  • Buat versi peristiwa secara aditif dan teruskan ID korelasi agar dapat dilacak.

Pola-pola ini mengubah pesan yang tidak terikat menjadi proses bisnis yang andal dan dapat diamati.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Membangun Alur Kerja Berbasis Peristiwa” gratis?

Ya — teks lengkap “Membangun Alur Kerja Berbasis Peristiwa” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus PHP Academy, upgrade ke CoddyKit PRO. Kursus PHP Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Membangun Alur Kerja Berbasis Peristiwa”?

Koordinasikan layanan melalui peristiwa dan idempotensi. Kamu berlatih PHP Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai PHP Academy?

Tidak diperlukan pengalaman sebelumnya. PHP Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.

Berapa lama pelajaran “Membangun Alur Kerja Berbasis Peristiwa” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran PHP Academy ini?

Ya. Setiap pelajaran PHP Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Mengapa Pesan Asinkron
  2. Bekerja dengan RabbitMQ di PHP
  3. Apache Kafka dengan PHP
  4. Membangun Alur Kerja Berbasis Peristiwa
← Kembali ke PHP Academy