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