Menengah6 menit baca

Relasi One-to-Many di DynamoDB

Sebuah control plane SaaS hampir selalu punya hierarki containment: satu workspace memiliki banyak project. Di SQL Anda akan memasang foreign key workspace_id pada tabel project dan melakukan JOIN.

DynamoDB tidak punya join dan tidak punya foreign key, jadi relasinya harus hidup di dalam key schema itu sendiri. Dikerjakan dengan benar, "muat sebuah workspace beserta setiap project di dalamnya" menjadi satu Query alih-alih satu pembacaan ditambah scan susulan.

Bagaimana cara memodelkan relasi one-to-many di DynamoDB?

Berikan induk dan semua anaknya yang sama agar mereka berbagi satu , lalu bedakan mereka dengan sort key. DynamoDB tidak punya join atau foreign key, sehingga relasinya hidup di dalam key schema itu sendiri. Memuat sebuah induk beserta setiap anaknya kemudian menjadi satu Query alih-alih sebuah join.

  • Modelkan pembacaannya, bukan entitasnya. Relasi one-to-many hanya ada untuk melayani "daftar project sebuah workspace" — bentuk key di sekitar query itu.
  • Kodekan induk ke dalam partition key anak. Beri workspace dan semua projectnya nilai partition-key yang sama agar mereka mendarat di satu item collection.
  • Lalu pembacaan daftar menjadi satu Query. Induk plus sejumlah anak sembarang kembali dalam satu panggilan yang ditagih — tanpa join, tanpa round trip kedua.
  • Waspadai hot partition. Satu tenant raksasa memusatkan semua trafiknya pada satu partition; workspace berukuran besar mungkin butuh key yang di-shard dan pembacaan fan-out.

Pola akses, lebih dulu

Pemodelan DynamoDB mengutamakan pola akses, bukan mengutamakan entitas — disiplin yang sama di balik single-table design. Sebelum memilih key apa pun, tuliskan pembacaan yang benar-benar dikeluarkan aplikasi:

  • Ambil setelan satu workspace.
  • Daftar setiap project dalam sebuah workspace, terbaru lebih dulu.
  • Ambil satu project tertentu berdasarkan id.

Relasi "satu workspace, banyak project" hanya penting karena pembacaan #2. Jika Anda tidak pernah perlu mendaftar project sebuah workspace bersama-sama, Anda tak akan memodelkan relasinya sama sekali — Anda akan menyimpan project secara independen.

Jadi pertanyaannya tidak pernah "bagaimana saya merepresentasikan one-to-many?" secara abstrak. Pertanyaannya adalah "query mana yang harus dilayani relasi ini?" Jawab itu, lalu bentuk key di sekitarnya.

Mengapa foreign key tidak menolong di sini

Di DynamoDB setiap GetItem dan Query menargetkan sebuah partition key, dan layanan ini melakukan hash terhadap key itu untuk menemukan partition yang menampung Item tersebut.

AWS menyatakannya langsung dalam dokumen Core Components: nilai partition-key adalah input bagi fungsi hash internal yang memutuskan di mana data berada.

Penempatan berbasis hash itu adalah warisan dari makalah Dynamo: Amazon's Highly Available Key-value Store asli tahun 2007, di mana consistent hashing menyebarkan key ke seluruh node.

Sebuah atribut workspace_id polos pada sebuah Item project tak terlihat oleh mesin itu — DynamoDB tak bisa "mengikutinya".

Untuk mengambil Item terkait dalam satu request, identitas induk harus dikodekan ke dalam partition key project, agar semua Item sebuah workspace di-hash ke partition yang sama dan satu Query bisa menyapunya.

Contoh nyata: workspace dan project

Gunakan key schema generik yang ditumpangi. Beri nama partition key EntityRef dan sort key Detail. Identitas workspace masuk ke EntityRef untuk baik Item workspace maupun setiap project di bawahnya:

EntityRefDetailattributes
WS#acmeMETAdisplayName, region, seatLimit
WS#acmePROJ#2026-0007title, status, createdBy
WS#acmePROJ#2026-0042title, status, createdBy
WS#acmePROJ#2026-0118title, status, createdBy
WS#globexMETAdisplayName, region, seatLimit
WS#globexPROJ#2026-0009title, status, createdBy

Workspace dan semua projectnya berbagi EntityRef = "WS#acme", sehingga mereka membentuk satu item collection yang hidup bersama pada satu partition.

Sort key Detail memisahkan mereka: META adalah catatan workspace, dan setiap project membawa prefiks PROJ# dengan id terurut-waktu yang di-zero-pad sehingga project terurut secara alami.

Secara visual, induk dan anak-anaknya menumpuk di dalam satu partition, terurut berdasarkan sort key:

Partition: EntityRef = WS#acmeMETA setelan workspacePROJ#2026-0007PROJ#2026-0042PROJ#2026-0118

Satu Query pada EntityRef = "WS#acme" menyapu seluruh tumpukan — induk plus setiap anak — dalam satu pembacaan.

Kini ketiga pola akses masing-masing menyusut menjadi satu panggilan:

  • Setelan workspaceGetItem(EntityRef="WS#acme", Detail="META").
  • Daftar project terbaru lebih duluQuery(EntityRef="WS#acme") dengan Detail begins_with "PROJ#", dijalankan dalam urutan menurun (ScanIndexForward = false).
  • Satu projectGetItem(EntityRef="WS#acme", Detail="PROJ#2026-0042").

Yang kedua adalah inti seluruhnya: induk dan sejumlah anak sembarang kembali dalam satu Query yang ditagih, tanpa join dan tanpa round trip kedua. Itulah langkah yang tak bisa Anda lakukan dengan atribut foreign-key dan sebuah Scan.

Menulis kondisi begins_with itu dengan tangan memang merepotkan — sintaks key-condition dan projection-expression menggigit.

DynamoDB Expression Builder menghasilkan KeyConditionExpression, peta placeholder #name/:value, dan snippet SDK siap jalan sehingga Anda tak perlu melawan tata bahasanya:

KeyConditionExpression     "#er = :er AND begins_with(#d, :p)"
ExpressionAttributeNames   { "#er": "EntityRef", "#d": "Detail" }
ExpressionAttributeValues  { ":er": "WS#acme", ":p": "PROJ#" }

Inspeksi item collection di DynoTable

Hasil dari tata letak ini bersifat visual: setiap baris yang berbagi EntityRef adalah workspace plus anak-anaknya, duduk berdampingan.

DynoTable mengelompokkannya agar Anda melihat relasi one-to-many sebagai satu blok yang menyatu alih-alih menebak-nebaknya di antara tabel terpisah.

Item META workspace dan anak-anak PROJ#-nya dikelompokkan sebagai satu item collection di tampilan tabel DynoTable.
Item META workspace dan anak-anak PROJ#-nya dikelompokkan sebagai satu item collection di tampilan tabel DynoTable.

Jebakan dan bentuk alternatif

Beberapa hal yang perlu diwaspadai:

  • Hot partition. Setiap Item untuk satu workspace hidup pada satu partition, jadi satu tenant yang sangat besar atau sangat sibuk memusatkan trafik. Perilaku adaptive capacity yang dijelaskan AWS menyerap skew moderat, tetapi workspace dengan jutaan project mungkin butuh key yang di-shard (mis. WS#acme#01 … #10) dan pembacaan fan-out.
  • Ukuran item collection. Dengan local secondary index, item collection satu partition dibatasi 10 GB; tanpa LSI tidak ada batas semacam itu. Jika Anda menimbang tipe index di sini, lihat GSI vs LSI.
  • Raih Query, jangan pernah Scan. Seluruh desain ini ada agar Anda bisa Query satu partition. Kembali ke Scan berfilter untuk "menemukan project sebuah workspace" membuang model itu dan membaca seluruh tabel — jebakan yang dibahas dalam Query vs Scan.

Jika Anda benar-benar perlu mendaftar project lintas workspace (misalnya, semua project status = ACTIVE secara global), tabel dasar tak bisa menjawabnya — partition keynya tercakup-workspace.

Itu pekerjaan untuk secondary index yang mem-partition ulang project pada atribut berbeda, bukan untuk membentuk ulang relasi ini.

Langkah berikutnya

Modelkan pola akses, kodekan induk ke dalam partition key anak, dan pembacaan one-to-many menjadi satu Query. Bangun dan validasi kondisi key dengan DynamoDB Expression Builder.

Lalu unduh DynoTable untuk memuat schema ini, menelusuri item collection workspace→project secara langsung, dan memastikan setiap query melakukan tepat satu pembacaan.

Diperbarui