DBML (Database Markup Language) adalah bahasa markup open-source yang dirancang khusus untuk mendefinisikan dan mendokumentasikan struktur database. DBML dikembangkan oleh tim Holistics dengan tujuan membuat desain database lebih mudah dibaca, ditulis, dan dikomunikasikan antar tim.
DBML adalah bahasa deklaratif yang memungkinkan Anda:
✓ Simple & Readable - Syntax yang mudah dipahami
✓ Database Agnostic - Tidak terikat vendor database tertentu
✓ Visual Diagram - Auto-generate ERD dari kode
✓ Version Control Ready - Perfect untuk Git workflow
✓ Team Collaboration - Mudah direview dan didiskusikan
✓ Documentation - Self-documenting schema
SQL:
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
title VARCHAR(200) NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
DBML:
Table users {
id int [pk, increment]
username varchar(50) [not null, unique]
email varchar(100) [not null, unique]
created_at timestamp [default: `now()`]
}
Table posts {
id int [pk, increment]
user_id int [not null, ref: > users.id]
title varchar(200) [not null]
content text
created_at timestamp [default: `now()`]
}
Keuntungan DBML:
DBML sangat cocok untuk:
// Komentar single line
/*
Komentar
multi-line
*/
Project project_name {
database_type: 'PostgreSQL'
Note: 'Deskripsi project'
}
Table table_name {
column_name data_type [settings]
Note: 'Catatan untuk tabel'
}
Ref: table1.column > table2.column
| Element | Deskripsi |
|---|---|
Table |
Mendefinisikan tabel database |
Ref |
Mendefinisikan relasi antar tabel |
Enum |
Mendefinisikan tipe data enum |
TableGroup |
Mengelompokkan tabel terkait |
Project |
Metadata project database |
Note |
Menambahkan dokumentasi |
Table users {
id int
username varchar
email varchar
age int
}
Table public.users {
id int
username varchar
}
Table admin.users {
id int
username varchar
}
Table users as U {
id int [pk]
username varchar
}
Table posts as P {
id int [pk]
user_id int [ref: > U.id]
}
Table products {
id int [
pk,
increment,
note: 'Primary key'
]
name varchar(255) [
not null,
unique,
note: 'Product name must be unique'
]
price decimal(10,2) [
not null,
default: 0,
note: 'Price in USD'
]
}
Table data_types_example {
// Integer types
small_int smallint
regular_int int
big_int bigint
// Decimal types
decimal_col decimal(10,2)
float_col float
double_col double
// String types
char_col char(10)
varchar_col varchar(255)
text_col text
// Date & Time
date_col date
time_col time
datetime_col datetime
timestamp_col timestamp
// Boolean
bool_col boolean
// Binary
blob_col blob
// JSON
json_col json
}
Table users {
id int [pk] // Primary Key
id int [primary key] // Alternative syntax
username varchar [unique] // Unique constraint
username varchar [not null] // Not null constraint
status varchar [default: 'active'] // Default value
created_at timestamp [default: `now()`] // Function default
id int [increment] // Auto increment
id int [pk, increment] // Multiple settings
email varchar [note: 'User email'] // Column note
}
Table user_roles {
user_id int
role_id int
indexes {
(user_id, role_id) [pk]
}
}
DBML mendukung beberapa cara mendefinisikan relasi:
Table posts {
id int [pk]
user_id int [ref: > users.id] // Many-to-One
title varchar
}
Table users {
id int [pk]
username varchar
}
Table posts {
id int [pk]
user_id int
title varchar
}
Table users {
id int [pk]
username varchar
}
Ref: posts.user_id > users.id
// One user has many posts
Table users {
id int [pk]
username varchar
}
Table posts {
id int [pk]
user_id int [ref: > users.id] // Many-to-one from posts to users
title varchar
}
// Alternative syntax:
Ref: posts.user_id > users.id
Symbol: > atau <
posts.user_id > users.id : Many posts → One userusers.id < posts.user_id : One user ← Many postsTable users {
id int [pk]
username varchar
}
Table user_profiles {
id int [pk]
user_id int [unique, ref: - users.id] // One-to-one
bio text
avatar varchar
}
// Alternative syntax:
Ref: user_profiles.user_id - users.id
Symbol: -
Table students {
id int [pk]
name varchar
}
Table courses {
id int [pk]
name varchar
}
// Junction table
Table student_courses {
student_id int [ref: > students.id]
course_id int [ref: > courses.id]
enrolled_at timestamp
indexes {
(student_id, course_id) [pk]
}
}
Ref name_optional: table1.column > table2.column [
delete: cascade,
update: cascade
]
// Example:
Ref: posts.user_id > users.id [
delete: cascade,
update: restrict
]
Delete/Update Actions:
cascade - Cascade actionrestrict - Restrict actionset null - Set NULLset default - Set default valueno action - No actionTable users {
id int [pk]
username varchar
email varchar
created_at timestamp
indexes {
username
email [unique]
created_at [name: 'idx_created']
}
}
Table posts {
id int [pk]
user_id int
category_id int
created_at timestamp
indexes {
(user_id, created_at) [name: 'idx_user_date']
(category_id, created_at)
}
}
Table products {
id int [pk]
name varchar
description text
price decimal
indexes {
name [unique, name: 'unique_product_name']
price [name: 'idx_price', type: btree]
description [type: hash]
(name, price) [unique]
}
}
Index Types:
btree (default)hashgingistenum user_status {
active
inactive
suspended
deleted
}
enum order_status {
pending [note: 'Order placed']
processing [note: 'Being processed']
shipped [note: 'Shipped to customer']
delivered [note: 'Delivered']
cancelled [note: 'Cancelled by user or admin']
}
Table users {
id int [pk]
username varchar
status user_status [default: 'active']
}
Table orders {
id int [pk]
status order_status [default: 'pending']
total decimal
}
TableGroup user_management {
users
user_profiles
user_roles
}
TableGroup e_commerce {
products
orders
order_items
payments
}
Table users {
id int [pk]
username varchar
}
Table user_profiles {
id int [pk]
user_id int [ref: - users.id]
}
// ... other tables
dbdiagram.io adalah platform online gratis untuk membuat database diagram menggunakan DBML.
Fitur:
Website: https://dbdiagram.io
Install DBML command-line tool:
# Install via npm
npm install -g @dbml/cli
# Convert DBML to SQL
dbml2sql schema.dbml --postgres > schema.sql
dbml2sql schema.dbml --mysql > schema.sql
# Convert SQL to DBML
sql2dbml schema.sql --postgres > schema.dbml
# Validate DBML file
dbml2sql schema.dbml --validate
DBML Language Support:
Search “DBML” di VS Code Extensions Marketplace.
DBML dapat diintegrasikan dengan:
Project ecommerce_db {
database_type: 'PostgreSQL'
Note: '''
# E-Commerce Database
Complete database schema for e-commerce platform
'''
}
// Enums
enum user_role {
customer
admin
vendor
}
enum order_status {
pending
processing
shipped
delivered
cancelled
refunded
}
enum payment_status {
pending
completed
failed
refunded
}
// Users & Authentication
Table users {
id int [pk, increment]
email varchar(255) [unique, not null]
password_hash varchar(255) [not null]
first_name varchar(100)
last_name varchar(100)
role user_role [default: 'customer']
is_active boolean [default: true]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
indexes {
email
(first_name, last_name)
}
Note: 'User accounts and authentication'
}
Table user_addresses {
id int [pk, increment]
user_id int [not null, ref: > users.id]
address_line1 varchar(255) [not null]
address_line2 varchar(255)
city varchar(100) [not null]
state varchar(100)
postal_code varchar(20)
country varchar(100) [not null]
is_default boolean [default: false]
created_at timestamp [default: `now()`]
indexes {
user_id
}
}
// Products
Table categories {
id int [pk, increment]
name varchar(100) [unique, not null]
slug varchar(100) [unique, not null]
description text
parent_id int [ref: > categories.id]
created_at timestamp [default: `now()`]
indexes {
slug
parent_id
}
}
Table products {
id int [pk, increment]
category_id int [ref: > categories.id]
name varchar(255) [not null]
slug varchar(255) [unique, not null]
description text
price decimal(10,2) [not null]
compare_at_price decimal(10,2)
cost_price decimal(10,2)
sku varchar(100) [unique]
barcode varchar(100)
quantity int [default: 0]
weight decimal(8,2)
is_active boolean [default: true]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
indexes {
slug
category_id
sku
is_active
(name, is_active)
}
Note: 'Product catalog'
}
Table product_images {
id int [pk, increment]
product_id int [not null, ref: > products.id]
image_url varchar(500) [not null]
alt_text varchar(255)
position int [default: 0]
created_at timestamp [default: `now()`]
indexes {
product_id
}
}
// Orders
Table orders {
id int [pk, increment]
user_id int [not null, ref: > users.id]
order_number varchar(50) [unique, not null]
status order_status [default: 'pending']
subtotal decimal(10,2) [not null]
tax_amount decimal(10,2) [default: 0]
shipping_amount decimal(10,2) [default: 0]
discount_amount decimal(10,2) [default: 0]
total_amount decimal(10,2) [not null]
// Shipping address
shipping_address_line1 varchar(255)
shipping_address_line2 varchar(255)
shipping_city varchar(100)
shipping_state varchar(100)
shipping_postal_code varchar(20)
shipping_country varchar(100)
notes text
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
indexes {
user_id
order_number
status
created_at
}
Note: 'Customer orders'
}
Table order_items {
id int [pk, increment]
order_id int [not null, ref: > orders.id]
product_id int [not null, ref: > products.id]
product_name varchar(255) [not null]
product_sku varchar(100)
quantity int [not null]
unit_price decimal(10,2) [not null]
total_price decimal(10,2) [not null]
created_at timestamp [default: `now()`]
indexes {
order_id
product_id
}
Note: 'Line items in orders'
}
// Payments
Table payments {
id int [pk, increment]
order_id int [not null, ref: > orders.id]
payment_method varchar(50) [not null]
amount decimal(10,2) [not null]
status payment_status [default: 'pending']
transaction_id varchar(255)
payment_gateway varchar(100)
paid_at timestamp
created_at timestamp [default: `now()`]
indexes {
order_id
status
transaction_id
}
}
// Reviews
Table product_reviews {
id int [pk, increment]
product_id int [not null, ref: > products.id]
user_id int [not null, ref: > users.id]
rating int [not null, note: 'Rating from 1 to 5']
title varchar(255)
comment text
is_verified_purchase boolean [default: false]
is_approved boolean [default: false]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
indexes {
product_id
user_id
rating
(product_id, user_id) [unique]
}
}
// Shopping Cart
Table cart_items {
id int [pk, increment]
user_id int [ref: > users.id]
product_id int [not null, ref: > products.id]
quantity int [not null, default: 1]
created_at timestamp [default: `now()`]
updated_at timestamp [default: `now()`]
indexes {
user_id
product_id
(user_id, product_id) [unique]
}
}
// Relationships with settings
Ref: order_items.order_id > orders.id [delete: cascade]
Ref: order_items.product_id > products.id [delete: restrict]
Ref: payments.order_id > orders.id [delete: cascade]
// Table Groups
TableGroup user_management {
users
user_addresses
}
TableGroup product_catalog {
categories
products
product_images
product_reviews
}
TableGroup order_management {
orders
order_items
payments
}
# PostgreSQL
dbml2sql schema.dbml --postgres -o schema.sql
# MySQL
dbml2sql schema.dbml --mysql -o schema.sql
# SQL Server
dbml2sql schema.dbml --mssql -o schema.sql
-- Auto-generated from DBML
CREATE TYPE "user_role" AS ENUM (
'customer',
'admin',
'vendor'
);
CREATE TYPE "order_status" AS ENUM (
'pending',
'processing',
'shipped',
'delivered',
'cancelled',
'refunded'
);
CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"email" varchar(255) UNIQUE NOT NULL,
"password_hash" varchar(255) NOT NULL,
"first_name" varchar(100),
"last_name" varchar(100),
"role" user_role DEFAULT 'customer',
"is_active" boolean DEFAULT true,
"created_at" timestamp DEFAULT (now()),
"updated_at" timestamp DEFAULT (now())
);
CREATE TABLE "products" (
"id" SERIAL PRIMARY KEY,
"category_id" int,
"name" varchar(255) NOT NULL,
"slug" varchar(255) UNIQUE NOT NULL,
"description" text,
"price" decimal(10,2) NOT NULL,
-- ... other columns
);
-- Indexes
CREATE INDEX ON "users" ("email");
CREATE INDEX ON "products" ("slug");
-- Foreign Keys
ALTER TABLE "products" ADD FOREIGN KEY ("category_id") REFERENCES "categories" ("id");
// ✅ Good: Consistent naming
Table users {
id int [pk]
first_name varchar
last_name varchar
created_at timestamp
}
Table user_profiles {
id int [pk]
user_id int [ref: > users.id]
}
// ❌ Bad: Inconsistent naming
Table Users {
ID int [pk]
FirstName varchar
LastName varchar
createdAt timestamp
}
Rekomendasi:
snake_case untuk tabel dan kolom{table_singular}_id{table1}_{table2}Table orders {
id int [pk, note: 'Unique order identifier']
user_id int [ref: > users.id, note: 'Customer who placed the order']
status order_status [note: 'Current order status']
total_amount decimal(10,2) [note: 'Total including tax and shipping']
Note: '''
Orders table stores all customer orders.
Status workflow: pending -> processing -> shipped -> delivered
'''
}
Table posts {
id int [pk]
user_id int [ref: > users.id]
title varchar
content text
published_at timestamp
indexes {
user_id // Foreign key index
published_at // Filter/sort index
(user_id, published_at) // Composite for user's posts query
}
}
Index Guidelines:
// ✅ Good: Clear relationship with cascade
Table posts {
id int [pk]
user_id int [not null, ref: > users.id]
}
Ref: posts.user_id > users.id [delete: cascade, update: cascade]
// ✅ Good: Preventing orphaned records
Table order_items {
id int [pk]
order_id int [not null, ref: > orders.id]
product_id int [not null, ref: > products.id]
}
Ref: order_items.order_id > orders.id [delete: cascade]
Ref: order_items.product_id > products.id [delete: restrict]
// ✅ Good: Type-safe status
enum order_status {
pending
processing
shipped
delivered
cancelled
}
Table orders {
id int [pk]
status order_status [default: 'pending']
}
// ❌ Bad: String without constraints
Table orders {
id int [pk]
status varchar [default: 'pending'] // Typo-prone
}
TableGroup authentication {
users
user_sessions
password_resets
}
TableGroup content {
posts
comments
tags
}
TableGroup settings {
site_settings
user_preferences
}
# Initialize git in your project
git init
# Track DBML files
git add schema.dbml
# Commit with meaningful messages
git commit -m "Add user authentication tables"
# Create branches for schema changes
git checkout -b feature/add-notifications-table
Project my_app {
database_type: 'PostgreSQL'
Note: '''
# My Application Database
Version: 2.0
Last Updated: 2026-03-31
## Schema Overview
- User Management
- Product Catalog
- Order Processing
- Payment Integration
## Conventions
- All timestamps in UTC
- Soft deletes using is_deleted flag
- Created/updated timestamps on all tables
'''
}
Konversi SQL berikut ke DBML:
CREATE TABLE authors (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);
CREATE TABLE books (
id SERIAL PRIMARY KEY,
author_id INT NOT NULL,
title VARCHAR(200) NOT NULL,
isbn VARCHAR(13) UNIQUE,
published_date DATE,
FOREIGN KEY (author_id) REFERENCES authors(id)
);
CREATE INDEX idx_books_author ON books(author_id);
CREATE INDEX idx_books_published ON books(published_date);
Buatlah DBML schema untuk sistem management sekolah dengan requirements:
Include:
Review DBML schema berikut dan identifikasi masalah:
Table user {
id int
name varchar
email varchar
}
Table post {
id int
userid int
title varchar
body text
}
Pertanyaan:
DBML (Database Markup Language) adalah tool yang powerful untuk database design dan documentation karena:
Keuntungan Utama:
Kapan Menggunakan DBML:
Best Practices:
Tools Rekomendasi:
Dengan menguasai DBML, Anda dapat:
#dbml tagDibuat pada: 31 Maret 2026
Tags: #dbml #database #schema-design #erd #database-design #documentation #devtools