# BAB 6: DESAIN TAKTIS DENGAN DOMAIN EVENTS
**Buku:** Domain-Driven Design Distilled  
**Penulis:** Vaughn Vernon  
**Penerjemah:** Everest ERP Architecture Team  

---

Pada bab-bab sebelumnya, Anda telah melihat sekilas bagaimana *Domain Events* digunakan. Sebuah **Domain Event** adalah **catatan resmi mengenai suatu kejadian yang signifikan bagi bisnis yang telah terjadi di masa lalu di dalam sebuah *Bounded Context***.

*Domain Events* adalah instrumen yang sangat penting baik untuk desain strategis maupun desain taktis. Melalui *Domain Events*, model domain menjadi mampu mengabarkan kejadian penting kepada pihak-pihak lain yang berkepentingan di dalam sistem.

---

### Konsistensi Kausalitas (*Causal Consistency*)

Untuk memahami kekuatan penuh yang dihasilkan dari penggunaan *Domain Events*, perhatikan konsep **konsistensi kausalitas (*causal consistency*)**.  
Suatu domain bisnis menyediakan konsistensi kausalitas jika operasi-operasi yang saling berhubungan secara sebab-akibat—di mana satu operasi memicu operasi lainnya—dilihat oleh setiap node yang bergantung dalam urutan yang sama persis.

#### Analogi Pesan Kausalitas:
Bayangkan dialog obrolan berikut:
1. **Sue:** *"Saya kehilangan dompet saya!"*
2. **Gary:** *"Itu mengerikan!"*
3. **Sue:** *"Jangan khawatir, saya sudah menemukan dompet saya!"*
4. **Gary:** *"Bagus sekali!"*

Jika pesan-pesan ini dikirimkan ke berbagai perangkat secara terdistribusi tetapi **tidak dalam urutan kausal yang benar**, bisa saja Gary terlihat merespons *"Bagus sekali!"* terhadap pesan Sue *"Saya kehilangan dompet saya!"*. Hal itu tentu menghasilkan kesimpulan yang salah dan menyesatkan.

Arsitektur sistem yang terurut dan linear secara kausal dapat dicapai dengan andal melalui penciptaan dan penerbitan *Domain Events* yang tersimpan dengan benar.

---

### Merancang dan Mengimplementasikan Domain Events

Berikut adalah antarmuka dasar (*interface*) yang harus didukung oleh setiap *Domain Event*:

```csharp
public interface DomainEvent 
{
    public DateTime OccurredOn { get; }
}
```

Properti `OccurredOn` mencatat tanggal dan waktu persis kapan peristiwa tersebut terjadi di dunia nyata.

---

### Konvensi Penamaan: Kata Kerja Bentuk Lampau (*Past-Tense Verbs*)

Nama tipe *Domain Event* Anda **wajib berupa pernyataan kejadian di masa lalu**, yaitu menggunakan **kata kerja bentuk lampau (*past-tense*)**.

Contoh dari *Agile Project Management Context*:
- `ProductCreated` (Produk telah dibuat)
- `ReleaseScheduled` (Rilis telah dijadwalkan)
- `SprintScheduled` (Sprint telah dijadwalkan)
- `BacklogItemPlanned` (Item backlog telah direncanakan)
- `BacklogItemCommitted` (Item backlog telah di-commit ke sprint)

---

### Relasi Perintah (*Command*) dan Peristiwa (*Domain Event*)

Sebuah *Domain Event* umumnya dipicu oleh sebuah **Command (Perintah)** yang berasal dari aksi pengguna di antarmuka sistem.
- Perintah dinyatakan dalam bentuk imperatif: **`CreateProduct`**.
- Hasil eksekusi dari perintah tersebut menghasilkan Domain Event: **`ProductCreated`**.

```
[Command: CreateProduct] 
   --> Diterima oleh Aggregate 
   --> State Aggregate Berubah 
   --> [Domain Event: ProductCreated Diterbitkan]
```

Atribut yang ada di dalam *Domain Event* harus mencakup properti yang diberikan bersama perintah yang menyebabkannya:
1. `tenantId` (identitas penyewa/organisasi)
2. `productId` (identitas produk yang baru dibuat)
3. `name` (nama produk)
4. `description` (deskripsi produk)
5. `occurredOn` (waktu kejadian)

---

### Menyimpan Agregat dan Domain Event dalam Satu Transaksi yang Sama

> **Prinsip Kritis Transaksional:**  
> Sangat penting bahwa **Agregat yang dimodifikasi dan Domain Event yang dihasilkannya disimpan bersama-sama di dalam transaksi database atomik yang sama**.

Jika Anda menggunakan database relasional (seperti MySQL/PostgreSQL):
1. Mulai transaksi database (`trans_start`).
2. Simpan perubahan status Agregat ke tabel bisnis.
3. Simpan rekaman *Domain Event* ke tabel antrean (**Event Store**).
4. Commit transaksi (`trans_complete`).
5. Publikasikan event ke broker pesan asinkron.

Dengan cara ini, dipastikan tidak akan pernah terjadi kondisi di mana data Agregat berhasil tersimpan tetapi event gagal diterbitkan, atau sebaliknya.

---

### Domain Event Berbasis Waktu (*Time-Based Domain Events*)

Tidak semua *Domain Event* dipicu oleh perintah manusia di UI. Beberapa event dipicu oleh **berlalunya waktu (*expiration of time*)**, seperti:
- `BusinessDayClosed` (Hari Kerja Berakhir)
- `FiscalYearEnded` (Tahun Buku Fiskal Berakhir)
- `MarketsClosed` (Pasar Saham Ditutup pada pukul 16.00)

Peristiwa berbasis waktu ini merupakan fakta sejarah bisnis yang tidak dapat dibatalkan, sehingga dimodelkan secara alami sebagai *Domain Event*.

---

### Event Sourcing

**Event Sourcing** adalah pola arsitektur mutakhir di mana status akhir (*current state*) dari suatu instans Agregat **tidak disimpan sebagai satu baris tabel yang terus-menerus ditimpa (*overwritten*)**, melainkan disimpan sebagai **rekaman aliran peristiwa (*event stream*) dari semua Domain Event yang pernah terjadi padanya sejak awal waktu**.

```
[Event 1: BacklogItemPlanned]
      ↓
[Event 2: BacklogItemStoryDefined]
      ↓
[Event 3: BacklogItemCommitted]
      ↓
(Rekonstruksi Objek di Memori: Menjalankan ulang seluruh event secara berurutan)
```

#### Keunggulan Utama Event Sourcing:
1. **Append-Only Performance:** Penyimpanan ke basis data hanya berupa operasi `INSERT` (*append-only*), tanpa pernah melakukan `UPDATE` atau `DELETE`. Operasi penyimpanan menjadi sangat cepat, minim penguncian baris database (*zero table locks*), dan sangat terukur (*highly scalable*).
2. **100% Immutable Audit Trail:** Memberikan catatan sejarah yang sempurna dan tidak dapat dimanipulasi untuk kebutuhan audit keuangan, pelaporan kepatuhan ISO/PSAK/pajak, dan analitik masa depan.
3. **Time-Travel Debugging:** Pengembang dapat memutar ulang aliran event untuk merekonstruksi status sistem pada tanggal dan jam berapa pun di masa lalu guna melacak bug secara instan.

#### Optimasi Kinerja: Snapshotting & Caching
Jika sebuah Agregat telah memiliki ribuan event, memuat ulang seluruh event dari awal dapat memakan waktu. Solusinya adalah menggunakan **Snapshots**: sistem secara berkala (misal setiap 100 event) menyimpan salinan instan (*snapshot*) dari status Agregat ke cache memori atau tabel database sekunder. Saat memuat, sistem cukup mengambil snapshot terakhir dan hanya menerapkan sisa event baru setelah snapshot tersebut.
