PlantUML ERD
This commit is contained in:
parent
d63aa0aa4e
commit
f9ba636837
324
instruction/erd/erd-plantuml-create.md
Normal file
324
instruction/erd/erd-plantuml-create.md
Normal file
@ -0,0 +1,324 @@
|
||||
# ERD / PlantUML / Buat
|
||||
|
||||
Berikut panduan cara membuat ERD dengan PlantUML.
|
||||
|
||||
Acuan format standarnya adalah `plan/milestone/moop.puml` (sumber) dan
|
||||
`plan/milestone/moop.png` (hasil render). Ikuti gaya itu persis — header-nya sama,
|
||||
tata letak entity-nya sama, cara menulis constraint dan relasinya juga sama.
|
||||
|
||||
Yang perlu disiapkan:
|
||||
- prefix modul/domain (misal `moop`, `chat`, `memories`) — dipakai sebagai prefix nama tabel/entity
|
||||
- daftar tabel beserta kolomnya
|
||||
- relasi antar tabel (siapa mereferensi siapa, wajib atau boleh kosong)
|
||||
- direktori tujuan untuk pasangan file `.puml` + `.png` (bawaan: `plan/milestone/`)
|
||||
|
||||
Bahasa penulisan: **isi diagram tetap bahasa Inggris** (nama tabel, tipe, dan teks
|
||||
constraint seperti `on update cascade` / `null priority = disabled`) supaya konsisten
|
||||
dengan skema SQL dan `moop.puml`. Penjelasan di dokumen ini saja yang bahasa Indonesia.
|
||||
|
||||
## Penamaan file dan lokasinya
|
||||
|
||||
- satu diagram = satu pasangan file: `<name>.puml` dan `<name>.png`
|
||||
- file `.puml` dan `.png` diletakkan berdampingan di direktori yang sama
|
||||
- nama diagram mengikuti nama fitur/modul, kebab-case atau huruf kecil biasa
|
||||
(contoh: `moop.puml`, `2-basic-features.puml`)
|
||||
- jangan pernah men-commit `.puml` tanpa `.png` hasil render-nya, dan render ulang setiap
|
||||
kali `.puml` diubah
|
||||
|
||||
## Header wajib
|
||||
|
||||
Setiap ERD dibuka persis seperti ini. Tanpa judul, tanpa legenda, tanpa blok penulis:
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
|
||||
hide circle
|
||||
skinparam linetype ortho
|
||||
|
||||
...
|
||||
|
||||
@enduml
|
||||
```
|
||||
|
||||
- `hide circle` menghapus lingkaran actor di depan nama entity.
|
||||
- `skinparam linetype ortho` membuat garis relasi menyiku/lurus supaya diagram tetap enak dibaca.
|
||||
- sisakan satu baris kosong setelah `@startuml` dan sebelum `@enduml`.
|
||||
- jangan menambah `skinparam` lain, warna, atau blok `<style>`. File ERD tetap polos.
|
||||
|
||||
## Kelompokkan tabel dengan package
|
||||
|
||||
Cluster entity berdasarkan sub-domain memakai `package <nama> { ... }` (huruf kecil,
|
||||
sedapatnya satu kata, contoh: `option`, `provider`). Semua entity dalam satu ranah
|
||||
dimasukkan ke package yang sama, dan relasi ranah itu juga ditulis di dalam blok yang
|
||||
sama. Antar package dipisah satu baris kosong.
|
||||
|
||||
```plantuml
|
||||
package provider {
|
||||
|
||||
...entity dan relasi milik domain provider...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Blok entity
|
||||
|
||||
Nama entity adalah nama tabelnya, apa adanya: berprefix nama modul, `snake_case`,
|
||||
tunggal atau jamak mengikuti skema aslinya (contoh: `moop_model_set`, `moop_api`).
|
||||
|
||||
```plantuml
|
||||
entity moop_model {
|
||||
id : binary(40) <<PK>>
|
||||
api : binary(40) <<FK>>
|
||||
set : binary(40) <<FK>>
|
||||
model : longtext
|
||||
priority : int null
|
||||
--
|
||||
* api = moop_api.id
|
||||
on update cascade
|
||||
on delete cascade
|
||||
* set = moop_model_set.id
|
||||
on update cascade
|
||||
on delete cascade
|
||||
* null priority = disabled
|
||||
}
|
||||
```
|
||||
|
||||
Aturannya:
|
||||
- isi entity di-indent 4 spasi (kalau di dalam package: 8 spasi untuk atribut).
|
||||
- satu atribut per baris, formatnya `nama : tipe` diikuti penanda stereotype.
|
||||
- `--` memisahkan bagian kolom dari bagian constraint/aturan. Selalu tulis pemisahnya,
|
||||
walaupun di bawahnya tidak ada apa-apa.
|
||||
- tidak ada baris kosong di dalam blok entity.
|
||||
- jangan pakai ekstensi `<<primary key>>`/`{mask}` dari PlantUML, jangan pakai warna,
|
||||
jangan pakai alias (`entity "foo" as f`). Nama di sebelah kiri ya nama tabel aslinya.
|
||||
|
||||
### Sintaks atribut
|
||||
|
||||
| Kondisi | Sintaks | Contoh |
|
||||
|---|---|---|
|
||||
| primary key | `id : binary(40) <<PK>>` | selalu baris pertama, selalu `binary(40)` |
|
||||
| foreign key | `api : binary(40) <<FK>>` | kolom FK juga `binary(40)` |
|
||||
| kolom unique | `type : binary(40) unique <<FK>>` | `unique` ditulis tepat setelah tipe |
|
||||
| boleh null | `priority : int null` | `null` ditulis setelah tipe |
|
||||
| teks | `name : longtext` | |
|
||||
| angka | `priority : int` | |
|
||||
|
||||
Tipe ditulis sebagai tipe SQL (`binary(40)`, `longtext`, `int`), bukan tipe Python.
|
||||
Kunci surrogate dan semua referensi ke baris tabel lain memakai `binary(40)`.
|
||||
|
||||
### Bagian constraint
|
||||
|
||||
Setiap baris di bawah `--` diawali `* `. Aturan yang panjangnya lebih dari satu baris
|
||||
dilanjutkan di baris berikutnya dengan indent lebih dalam, tanpa mengulang tanda `*`.
|
||||
|
||||
Tulis di sini, berurutan seperti ini:
|
||||
1. Tujuan FK: `* <kolom> = <tabel_induk>.id`, lalu `on update cascade` dan
|
||||
`on delete cascade` masing-masing di baris sendiri.
|
||||
2. Aturan unique/komposit: `* unique(set, type)`.
|
||||
3. Integritas antar kolom: `* set_type.type must equal type`.
|
||||
4. Nilai yang diizinkan: `* type availability =` lalu daftar nilainya di baris
|
||||
berikutnya, diberi kutip dan dipisah pipe — `"llm" | "embedding" | "image generation"`.
|
||||
5. Catatan bisnis/semantik dengan kalimat biasa: `* null priority = disabled`,
|
||||
`* empty key = no bearer`, `* one default set per model type`.
|
||||
|
||||
Bagian constraint inilah yang membuat diagram bisa di-review tanpa membaca SQL-nya.
|
||||
Kalau ada aturan di skema atau di spesifikasi produk, tempatnya ada di sini.
|
||||
|
||||
## Relasi
|
||||
|
||||
Tulis setiap relasi di baris sendiri, tepat setelah blok entity yang memilikinya
|
||||
(entity anak yang punya kolom FK, atau induknya kalau relasi itu memang dikenalkan dari
|
||||
sana). Tanpa label, tanpa klausa `to`, tanpa baris kosong antara entity dan relasinya.
|
||||
|
||||
```plantuml
|
||||
moop_model_set ||--|{ moop_model_set_type
|
||||
moop_model_type ||-o{ moop_model_set_type
|
||||
moop_model_type ||--|| moop_model_type_default
|
||||
moop_model_set_type ||--o| moop_model_type_default
|
||||
moop_api ||--o{ moop_key
|
||||
```
|
||||
|
||||
Kardinalitas selalu `induk <--> anak`, memakai notasi crow-foot:
|
||||
|
||||
| Kiri (induk) | Arti | Kanan (anak) | Arti |
|
||||
|---|---|---|---|
|
||||
| `\|\|` | tepat satu | `\|\|` | tepat satu |
|
||||
| `\|\|` | tepat satu | `\|{` | satu atau lebih |
|
||||
| `\|\|` | tepat satu | `o{` | nol atau lebih |
|
||||
| `\|\|` | tepat satu | `o\|` | nol atau satu |
|
||||
|
||||
Simbolnya: `||` = wajib satu, `o|` = opsional satu, `|{` = satu atau lebih, `o{` = nol atau lebih.
|
||||
|
||||
**Jumlah dash bukan kardinalitas.** Yang membawa makna hanya karakter di kedua ujungnya
|
||||
(`|`, `o`, `{`, `}`). Jumlah `-` adalah **knob tata letak yang mengatur orientasi**:
|
||||
|
||||
| Sintaks | Efek (sudah diverifikasi ke jar-nya) |
|
||||
|---|---|
|
||||
| `\|-` (satu dash) | **horizontal** — kedua entity berdampingan, edge-nya tidak menambah rank |
|
||||
| `\|--` (dua dash) | **vertikal** — entity anak ditumpuk di bawah induknya. Ini bawaannya |
|
||||
| `\|---`, `\|----` | tetap vertikal, cuma lebih panjang. Pakai saat garis saling menutupi |
|
||||
|
||||
Diukur pada rantai 3 entity yang identik: satu dash merender 543×95 (a→b→c berjajar),
|
||||
dua dash merender 171×349 (a→b→c menurun). Menambah dash ke tiga atau empat hanya
|
||||
menambah tinggi (275 → 336 px), tidak pernah mengubah orientasi.
|
||||
|
||||
Ini disengaja di `moop.puml`: `moop_model_type ||-o{ moop_model_set_type` memakai
|
||||
**satu** dash supaya `moop_model_type` jatuh ke sebelah *kiri* tabel junction, sedangkan
|
||||
semua relasi induk→anak memakai **dua** dash supaya diagram tetap mengalir dari atas ke bawah.
|
||||
|
||||
Jadi aturannya:
|
||||
|
||||
- pakai `--` sebagai default untuk semua relasi — diagramnya terbaca sebagai hierarki vertikal
|
||||
- pindah ke satu `-` hanya kalau dua tabel memang perlu berdampingan agar render tidak kusut
|
||||
- jangan pernah memakai jumlah dash untuk arti "kuat vs lemah" atau "identifying vs
|
||||
non-identifying". Makna itu tidak ada, dan pembaca akan salah tafsir
|
||||
- jangan pakai `..` (garis putus-putus). Hasilnya garis dashed dan tidak dipakai sama sekali
|
||||
di format standar ini
|
||||
|
||||
## Menentukan posisi entity (mana yang atas/bawah, mana yang kiri/kanan)
|
||||
|
||||
Sisi penulisan entity pada baris relasi **adalah** posisinya. Entity di sisi kiri akan
|
||||
ditempatkan **di atas** (relasi vertikal) atau **di sebelah kiri** (relasi horizontal)
|
||||
dari entity di sisi kanan. Sudah diverifikasi ke jar-nya:
|
||||
|
||||
| Baris relasi | Hasil |
|
||||
|---|---|
|
||||
| `induk \|--\|{ anak` | `induk` di atas, `anak` di bawah |
|
||||
| `anak }--\|\| induk` | `anak` di atas, `induk` di bawah (terbalik) |
|
||||
| `induk \|-o{ anak` | `induk` di kiri, `anak` di kanan |
|
||||
| `anak }o-\|\| induk` | `anak` di kiri, `induk` di kanan (terbalik) |
|
||||
|
||||
Dua konsekuensi yang penting:
|
||||
|
||||
1. **Urutan deklarasi `entity` di file tidak berpengaruh.** Menulis `anak` sebelum
|
||||
`induk` tetap merender `induk` di atas, selama baris relasinya `induk ||--|{ anak`.
|
||||
Jangan memperbaiki posisi dengan memindah-mindah blok entity — yang menggeser posisi
|
||||
hanya baris relasi.
|
||||
2. **Menulis barisnya terbalik akan membalik diagram dan merusak notasi.** Pertahankan
|
||||
induk di sisi kiri, supaya crow's foot jatuh di sisi anak. Kalau karena alasan tata
|
||||
letak anak harus muncul di atas induk, pindahkan *package/entity*-nya dan tetap tulis
|
||||
`induk <--> anak`, jangan membalik baris relasinya.
|
||||
|
||||
Resep praktis supaya hasilnya enak dilihat:
|
||||
|
||||
- gambar hierarki utama dari atas ke bawah dengan `--`, induk selalu di kiri
|
||||
- untuk tabel junction, pisahkan dua induknya: induk utama di atasnya dengan `||--|{`,
|
||||
induk lookup di sampingnya dengan `||-o{` (satu dash) supaya induk itu jatuh ke kiri,
|
||||
bukan menumpuk — persis kasus `moop_model_type`
|
||||
- kalau dua entity sibling saling desak, beri satu dash tambahan pada salah satu
|
||||
relasinya (`||---|{`) untuk membuka ruang vertikal
|
||||
- kalau rantai tabelnya terlalu panjang sampai diagramnya terlalu tinggi, pecah per
|
||||
sub-domain ke `package` sendiri — bukan dengan membalik baris relasi
|
||||
|
||||
Cek konsistensi yang wajib dilakukan:
|
||||
- kalau kolom FK-nya `NOT NULL`, sisi anak harus memakai `|` (`||--|{`, `||--o|`)
|
||||
- kalau kolom FK-nya nullable/opsional, sisi anak memakai `o` (`||-o{`, `||--o|`)
|
||||
- kolom FK yang `UNIQUE` berarti relasi 1-ke-1 (`||--||` atau `||--o|`), bukan 1-ke-banyak
|
||||
- setiap `<<FK>>` yang dideklarasikan di entity harus punya baris relasi yang cocok di
|
||||
suatu tempat, dan setiap baris relasi harus punya kolom FK yang cocok. Tidak boleh ada
|
||||
relasi yatim, tidak boleh ada FK yang tidak terdokumentasi.
|
||||
|
||||
## Relasi antar package
|
||||
|
||||
Kalau sebuah tabel di satu package mereferensi tabel di package lain, tetap deklarasikan
|
||||
FK-nya di dalam entity anak dan gambar relasinya dari sisi anak, di dalam blok package
|
||||
anaknya. Jangan membuat bagian "relations" terpisah di bawah file.
|
||||
|
||||
## Urutan penulisan
|
||||
|
||||
Aturan di bagian ini hanya untuk **keterbacaan sumbernya** — tidak mengubah tata letak
|
||||
hasil render. Posisi ditentukan oleh baris relasi (lihat "Menentukan posisi entity").
|
||||
|
||||
- package: dari domain yang paling mandiri ke yang paling bergantung
|
||||
- di dalam package: tabel induk dulu, lalu tabel junction/anaknya, lalu tabel yang
|
||||
bergantung pada tabel-tabel itu
|
||||
- atribut: `id` dulu, lalu FK dan kolom data, lalu kolom nullable/opsional paling akhir
|
||||
|
||||
## Render
|
||||
|
||||
Menggunakan jar yang sudah tersedia di `plan/milestone/`:
|
||||
|
||||
```bash
|
||||
cd plan/milestone && java -jar plantuml-mit-1.2026.8.jar -tpng -charset UTF-8 moop.puml -o .
|
||||
```
|
||||
|
||||
- `-tpng` untuk keluaran PNG, `-charset UTF-8` supaya nilai enum yang dikutip tidak rusak
|
||||
- `-o <dir>` menulis gambar di samping file sumbernya
|
||||
- setelah render, buka `.png`-nya dan pastikan: diagramnya mengalir atas ke bawah, tidak
|
||||
ada kotak yang tumpang tindih, tidak ada garis bersilangan yang sebenarnya bisa dihindari
|
||||
dengan memindah satu relasi ke satu dash (berdampingan) atau menambah dash untuk membuka
|
||||
ruang vertikal, setiap entity berada di tepat satu package, dan setiap relasi benar-benar
|
||||
tersambung. Ingat: memindah blok `entity` di sumber **tidak** menggeser apa pun — yang
|
||||
menggeser hanya baris relasi
|
||||
|
||||
## Template minimal
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
|
||||
hide circle
|
||||
skinparam linetype ortho
|
||||
|
||||
package <domain> {
|
||||
|
||||
entity <prefix>_parent {
|
||||
id : binary(40) <<PK>>
|
||||
name : longtext
|
||||
--
|
||||
}
|
||||
|
||||
entity <prefix>_type {
|
||||
id : binary(40) <<PK>>
|
||||
type : longtext
|
||||
--
|
||||
* type availability =
|
||||
"a" | "b" | "c"
|
||||
}
|
||||
|
||||
entity <prefix>_parent_type {
|
||||
id : binary(40) <<PK>>
|
||||
parent : binary(40) <<FK>>
|
||||
type : binary(40) <<FK>>
|
||||
--
|
||||
* parent = <prefix>_parent.id
|
||||
on update cascade
|
||||
on delete cascade
|
||||
* type = <prefix>_type.id
|
||||
on update cascade
|
||||
on delete cascade
|
||||
* unique(parent, type)
|
||||
}
|
||||
<prefix>_parent ||--|{ <prefix>_parent_type
|
||||
<prefix>_type ||-o{ <prefix>_parent_type
|
||||
|
||||
entity <prefix>_child {
|
||||
id : binary(40) <<PK>>
|
||||
parent : binary(40) <<FK>>
|
||||
priority : int null
|
||||
--
|
||||
* parent = <prefix>_parent.id
|
||||
on update cascade
|
||||
on delete cascade
|
||||
* null priority = disabled
|
||||
}
|
||||
<prefix>_parent ||--o{ <prefix>_child
|
||||
|
||||
}
|
||||
|
||||
@enduml
|
||||
```
|
||||
|
||||
## Checklist review
|
||||
|
||||
- [ ] `@startuml` / `hide circle` / `skinparam linetype ortho` / `@enduml`, tidak ada yang lain di header
|
||||
- [ ] semua entity dikelompokkan dalam `package`, tidak ada entity mengambang
|
||||
- [ ] nama entity = nama tabel asli, berprefix modul, snake_case
|
||||
- [ ] `id : binary(40) <<PK>>` jadi atribut pertama di setiap entity
|
||||
- [ ] pemisah `--` ada di setiap entity
|
||||
- [ ] kolom FK ditandai `<<FK>>` dan diulang di bagian constraint beserta aturan cascade-nya
|
||||
- [ ] nilai enum/domain diberi kutip dan dipisah pipe
|
||||
- [ ] setiap relasi di baris sendiri, tepat setelah entity-nya, induk di sisi kiri
|
||||
- [ ] kardinalitas cocok dengan nullability dan uniqueness FK
|
||||
- [ ] jumlah dash hanya dipakai sebagai knob tata letak (`-` = berdampingan, `--` = menumpuk), bukan sebagai makna. Tanpa `..`, tanpa tafsir "kuat vs lemah"
|
||||
- [ ] induk selalu ditulis di sisi kiri setiap baris relasi, supaya dia yang muncul di atas / di kiri
|
||||
- [ ] `.png` hasil render ada di samping `.puml` dan hasilnya bersih secara visual
|
||||
6
instruction/erd/erd.md
Normal file
6
instruction/erd/erd.md
Normal file
@ -0,0 +1,6 @@
|
||||
# ERD
|
||||
|
||||
Berikut daftar instruction yang bisa kamu panggil.
|
||||
|
||||
Pembuatan Diagram
|
||||
- `erd-plantuml-create.md` Membuat ERD (Entity Relationship Diagram) dengan PlantUML sesuai format standar
|
||||
Loading…
Reference in New Issue
Block a user