# BAB 5: DESAIN TAKTIS DENGAN AGREGAT (AGGREGATES)
**Buku:** Domain-Driven Design Distilled  
**Penulis:** Vaughn Vernon  
**Penerjemah:** Everest ERP Architecture Team  

---

Sejauh ini saya telah membahas desain strategis dengan *Bounded Contexts*, *Subdomains*, dan *Context Maps*. Sekarang kita beralih ke instrumen desain taktis yang hidup di dalam sebuah *Bounded Context*: yaitu **Agregat (*Aggregates*)**.

---

### Memahami Entitas, Value Object, dan Agregat

Sebelum memahami Agregat, kita harus membedakan dua blok pembangun utamanya:

#### 1. Apa itu Entitas (*Entity*)?
Sebuah **Entitas** memodelkan sesuatu yang individual dan unik. Setiap Entitas memiliki **identitas unik (*unique identity*)** yang membedakan individualitasnya dari semua Entitas lain dari jenis yang sama atau berbeda.
- Sering kali status (*state*) Entitas dapat berubah seiring waktu (*mutable*).
- Hal utama yang membedakan Entitas dari alat pemodelan lainnya adalah **keunikan identitasnya**.

#### 2. Apa itu Value Object (Objek Nilai)?
Sebuah **Value Object** memodelkan keseluruhan konsep yang bersifat **kekal / tidak dapat diubah (*immutable conceptual whole*)**.
- Di dalam model, Value Object tidak memiliki identitas unik tersendiri.
- Kesetaraan (*equality*) ditentukan dengan membandingkan seluruh atribut nilai yang dienkapsulasi oleh tipe Value Object tersebut. Jika dua Value Object memiliki atribut angka dan satuan yang sama (misal `Rp 50.000` dan `Rp 50.000`), maka keduanya dianggap bernilai sama persis.
- Value Object sering digunakan untuk mendeskripsikan, mengukur, atau menguantifikasi sebuah Entitas (contoh: Nilai Uang, Satuan Ukuran, Rentang Tanggal, Alamat).

#### 3. Apa itu Agregat (*Aggregate*)?
Sebuah **Agregat** tersusun dari satu atau lebih Entitas, di mana salah satu Entitas bertindak sebagai **Entitas Induk / Akar Agregat (*Aggregate Root*)**. Agregat juga dapat memiliki beberapa *Value Objects* di dalamnya.
- **Aggregate Root** memiliki dan mengendalikan semua elemen lain yang dikelompokkan di dalamnya.
- Nama Entitas Induk adalah nama konseptual Agregat tersebut.

---

### Batasan Konsistensi Transaksional (*Transactional Consistency Boundary*)

Setiap Agregat membentuk **batasan konsistensi transaksional (*transactional consistency boundary*)**.  
Ini berarti bahwa di dalam satu Agregat tunggal, semua bagian yang menyusunnya **wajib konsisten sesuai dengan aturan bisnis ketika transaksi basis data di-commit**.

Batas luar yang mengelilingi Agregat mewakili transaksi basis data terpisah yang akan mengendalikan penyimpanan atomik dari setiap kelompok objek.

> **Aturan Dasar:** Modifikasi dan lakukan commit hanya pada **satu instans Agregat dalam satu transaksi database**. Setiap Agregat lainnya harus dimodifikasi dan di-commit dalam transaksi terpisah.

Alasan batasan transaksional ini dimotivasi oleh bisnis: **Bisnislah yang menentukan status valid dari suatu kluster pada waktu tertentu.** Jika Agregat tidak disimpan dalam status yang utuh dan valid, operasi bisnis yang baru saja dilakukan dianggap salah menurut aturan bisnis (*business invariants*).

---

### 🌟 4 Aturan Emas Desain Agregat (Aggregate Rules of Thumb)

Berikut adalah empat aturan dasar desain Agregat yang sangat fundamental:

```
+-------------------------------------------------------------------------+
|                    4 ATURAN EMAS DESAIN AGREGAT                        |
+-------------------------------------------------------------------------+
| 1. Lindungi Business Invariants di dalam batasan Agregat.              |
| 2. Rancang Agregat berukuran kecil (Design Small Aggregates).           |
| 3. Referensikan Agregat lain HANYA melalui Identitas (ID Only).        |
| 4. Perbarui Agregat lain menggunakan Konsistensi Bertahap              |
|    (Eventual Consistency via Domain Events).                           |
+-------------------------------------------------------------------------+
```

---

#### ATURAN 1: Lindungi Business Invariants di Dalam Batas Agregat
Aturan 1 berarti bahwa bisnis harus menentukan komposisi Agregat berdasarkan apa yang wajib konsisten saat transaksi di-commit.
- **Contoh Invariant Bisnis:**  
  Pada Agregat `BacklogItem`, ada aturan bisnis yang menyatakan: *"Ketika semua instans `Task` memiliki sisa jam kerja (`hoursRemaining`) bernilai 0, maka status `BacklogItem` harus otomatis disetel menjadi `DONE`."*
- Oleh karena itu, pada akhir transaksi penyimpanan, aturan bisnis yang sangat spesifik ini harus terpenuhi secara konsisten di dalam batas Agregat tersebut.

---

#### ATURAN 2: Rancang Agregat Berukuran Kecil (*Design Small Aggregates*)
Aturan ini menekankan bahwa konsumsi memori dan cakupan transaksional dari setiap Agregat harus relatif kecil.

**❌ Desain Agregat Raksasa yang Salah (Large Cluster):**
Mendesain `Product` di mana satu objek `Product` menampung kumpulan ribuan `BacklogItem`, ratusan `Release`, dan puluhan `Sprint`.
- Objek menjadi sangat berat dan memakan banyak memori server.
- Sangat lambat saat dimuat dari database.
- Memicu tabrakan kunci transaksi (*locking collision*) ketika beberapa pengguna mencoba memperbarui task yang berbeda pada produk yang sama secara bersamaan.

**✅ Desain Agregat Kecil yang Benar:**
Pecah kluster raksasa tersebut menjadi **empat Agregat terpisah yang ramping**:
1. Agregat `Product`
2. Agregat `BacklogItem`
3. Agregat `Release`
4. Agregat `Sprint`

Agregat kecil dimuat dengan cepat, hemat memori, cepat dibersihkan oleh *garbage collector*, dan memiliki tingkat keberhasilan transaksi konkurensi yang jauh lebih tinggi. Masing-masing Agregat mematuhi *Single Responsibility Principle* (SRP) dan jauh lebih mudah diuji dengan *unit test*.

---

#### ATURAN 3: Referensikan Agregat Lain HANYA Melalui Identitas (Identity Only)
Setelah memecah kluster besar menjadi empat Agregat kecil, bagaimana masing-masing Agregat saling merujuk?
- **Pegang ID-nya saja, bukan referensi objek memori langsung.**
- `BacklogItem`, `Release`, dan `Sprint` merujuk ke `Product` dengan memegang nilai **`ProductId`** (Value Object).
- Ini mencegah satu Agregat secara sembarangan memodifikasi status Agregat lain di dalam transaksi yang sama.
- Hal ini juga memungkinkan Agregat disimpan dengan fleksibel di berbagai jenis basis data (MySQL, PostgreSQL, MongoDB, atau Cache).

---

#### ATURAN 4: Perbarui Agregat Lain Menggunakan Eventual Consistency
Ketika sebuah `BacklogItem` di-commit ke dalam sebuah `Sprint`, kedua Agregat harus bereaksi:
1. Di dalam transaksi pertama, status `BacklogItem` diperbarui sehingga menyimpan `SprintId` tujuan, lalu transaksi di-commit ke database dan menerbitkan Domain Event **`BacklogItemCommitted`**.
2. Domain Event `BacklogItemCommitted` diterima oleh subscriber lokal, yang kemudian memulai transaksi kedua terpisah untuk memperbarui Agregat `Sprint` agar menyimpan `BacklogItemId` baru.

---

### Memerangi Anemic Domain Model (Model Domain yang Anemia)

Salah satu jebakan terbesar yang sering dibuat programmer adalah **Anemic Domain Model**.
- Ini adalah kondisi di mana kelas model domain hanya berisi sekumpulan properti privat dengan *public getter* dan *public setter* tanpa memiliki logika perilaku bisnis sama sekali.
- Logika bisnis justru tercecer di luar model (di controller atau helper), sementara model hanya menjadi kantong penampung data bodoh (*dumb data holder*).

**Lawan Anemia Domain:**
- **Hapus public setter!** Ubah status internal objek hanya melalui metode perilaku (*behavioral methods*) yang memiliki nama eksplisit sesuai *Ubiquitous Language*.
- Contoh: Jangan gunakan `product.setName(x)`, gunakan metode bisnis seperti `product.rebrandAs(newName)`.

---

### Contoh Kode Struktur Agregat (C# / Bahasa Berorientasi Objek)

```csharp
// Entitas Induk (Aggregate Root)
public class Product : Entity 
{
    // Identitas berbasis Value Object
    private TenantId tenantId;
    private ProductId productId;

    // Atribut intrinsik
    private string name;
    private string description;

    // Behavioral Methods (Perilaku Bisnis Eksplisit)
    public void PlanBacklogItem(string summary, string story) 
    {
        // Validasi business invariant di sini...
    }

    public void ScheduleRelease(string releaseName, DateTime targetDate) 
    {
        // Logika bisnis penjadwalan rilis...
    }

    public void ScheduleSprint(string sprintName, DateTime startsOn, DateTime endsOn) 
    {
        // Logika bisnis sprint...
    }
}
```

---

### 5 Langkah Menentukan Batasan Agregat yang Tepat (*Right-Sizing Aggregates*)

1. **Mulai dari Satu Entitas:** Awali setiap Agregat hanya dengan satu Entitas yang bertindak sebagai *Aggregate Root*.
2. **Identifikasi Invariant Bisnis:** Tentukan atribut apa saja yang wajib valid dan terkunci saat objek pertama kali dibuat dan disimpan.
3. **Tanyakan Batasan Waktu Reaksi kepada Pakar Bisnis:**
   - (a) Apakah perubahan pada Agregat A harus merefleksikan perubahan Agregat B **seketika itu juga (*immediately*)**?
   - (b) Atau boleh terjadi dalam hitungan beberapa detik/menit (**bertahap / *eventual***)?
4. **Jika Harus Seketika (Immediately):** Gabungkan kedua entitas tersebut ke dalam satu batasan Agregat yang sama.
5. **Jika Boleh Bertahap (Eventual):** Pisahkan menjadi dua Agregat berbeda dan sinkronkan menggunakan **Domain Events**.
