Commerce database
commerce menangani state belanja dan transaksi. Product/service hanya direferensikan melalui ID dan snapshot.
Aggregate transaksi
Order memiliki orderNumber unik dalam organization, status order/payment/fulfillment, totals, items, payments, invoices, dan shipments. Totals harus dihitung server-side dari line items, bukan dipercaya dari client.
Lifecycle
Cart -> Order draft -> confirmed -> processing -> completed
| -> cancelled
Payment: unpaid -> pending -> paid / failed / refunded
Fulfillment: unfulfilled -> partially_fulfilled -> fulfilledCartItem dan OrderItem menyimpan quantity, unit price, subtotal, serta product/service snapshot. Validasi product atau service aktif pada catalog sebelum membuat item.
Customer, invoice, payment, shipment
Semua model memiliki organizationId. Payment harus memakai idempotency key untuk mencegah double charge. Invoice dan Shipment memakai nomor unik per organization. Status transition harus finite-state dan dicatat dengan actor serta request id.
Karena transaksi ini hidup pada satu database commerce tetapi reference berasal dari runner/catalog, lakukan preflight existence checks sebelum menulis dan gunakan retry/compensation untuk workflow lintas database.
Architecture
Relasi domain yang saya kunci:
CATALOG
ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāā
ā ā ā
Product Variant Service
ā ā ā
āāāāāāāāāāāāāāā¼āāāāāāāāāāāāāā
ā
scalar IDs
ā
ā¼
COMMERCE
ā
āāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāā
ā ā ā
Customer Cart Order
ā ā
CartItem OrderItem
ā
āāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāā
ā ā ā
Invoice Payment Shipment
ā ā ā
InvoiceItem PaymentAllocation ShipmentItem
ā
RefundCatalog tetap authoritative untuk catalog data. Commerce authoritative untuk transaction data.
Relasi
Catalog - Commerce
Catalog memiliki:
Product
ProductVariant
ServiceDefinition
Servicedan bahkan secara eksplisit memisahkan:
ServiceDefinition
= catalog definition
Service
= operational service/work recordMaka Commerce cukup:
serviceId String?pada:
CartItem
OrderItem
InvoiceItemJika transaksi berkaitan dengan sebuah service, Commerce menunjuk ke Service di Catalog.
Historical truth tetap disimpan:
serviceSnapshot ServiceSnapshot?Dengan demikian boundary menjadi sangat bersih.
Perlu diingat bahwa Product dan Variant adalah scalar reference
Misalnya:
productId String?
variantId String?bukan:
product Product
variant ProductVariantKarena Catalog dan Commerce adalah database berbeda.
Cross-database references remain scalar IDs.
InvoiceItem tidak sekadar menggunakan OrderItem
Order
āāā OrderItem
Invoice
āāā InvoiceItemkarena:
Order
= commercial transaction
Invoice
= financial documentSatu Order dapat menghasilkan beberapa Invoice.
Misalnya:
Order 10.000.000
Invoice #1
DP 3.000.000
Invoice #2
Remaining 7.000.000Ini tidak bisa dimodelkan dengan baik kalau Invoice hanya mirror Order.
Payment Allocation adalah bagian yang sangat penting
Payment
ā
ā¼
PaymentAllocation
ā
ā¼
InvoiceSehingga:
Payment A = 500.000
Payment B = 300.000
Payment C = 200.000
Invoice = 1.000.000semuanya bisa dialokasikan.
Dan juga:
Payment = 1.000.000
Invoice A = 600.000
Invoice B = 400.000juga valid.
Ini membuat Commerce siap untuk:
- DP
- installment
- split payment
- retry payment
- multiple invoices
- partial payment
- enterprise billing
Refund bukan sekadar status
Refundsebagai entity tersendiri.
Contoh:
Payment = 1.000.000
Refund #1 = 200.000
Refund #2 = 100.000
Refund #3 = 150.000
total refunded = 450.000Payment dapat berada pada:
partially_refundeddan akhirnya:
refundedShipment Item mendukung partial fulfillment
Misalnya:
Order
āāā Product A Ć 10
āāā Product B Ć 5Shipment 1:
A Ć 10
B Ć 2Shipment 2:
B Ć 3Maka:
Order.fulfillmentStatus
ā
āāā partially_fulfilled
ā
āāā fulfilledSnapshot bukan sekadar Json
type ProductSnapshot {
productId
variantId
name
sku
barcode
code
...
snapshot Json?
}Jadi ada dua lapisan:
- Frequently queried historical fields
name
sku
barcode
code- Extension/raw historical data
snapshot JsonPromotion adalah milik Catalog
Order hanya memiliki:
appliedPromotionIds String[] @db.ObjectIddan:
discountSnapshots DiscountSnapshot[]Artinya:
Catalog
āāā Promotion
ā
ā¼
Commerce
āāā promotionId
āāā historical discount snapshotCommerce tidak perlu memiliki Promotion engine.
Ini menjaga konsistenzi dengan Catalog schema.
Customer berada di Commerce
Catalog:
Product
Variant
ServiceDefinition
Service
Price
PromotionCommerce:
Customer
Cart
Order
Invoice
Payment
Refund
Shipmentkarena purchase history harus diperoleh dari:
Customer
ā
Order
ā
OrderItem3-Level Historical Truth
- Level 1 ā Reference
productId
variantId
serviceId
promotionIdUntuk traceability dan analytics.
- Level 2 ā Typed snapshot
ProductSnapshot
ServiceSnapshot
DiscountSnapshot
TaxSnapshot
AddressSnapshotUntuk historical business truth.
- Level 3 ā Extension snapshot
snapshot Json
metadata JsonUntuk data tambahan yang tidak layak menjadi typed core field.
Status dipisahkan
Order memiliki tiga state axis:
OrderStatus
OrderPaymentStatus
OrderFulfillmentStatusContoh:
status = processing
paymentStatus = paid
fulfillmentStatus = partially_fulfilledatau:
status = completed
paymentStatus = partially_refunded
fulfillmentStatus = fulfilledBoundary
- Catalog + Commerce:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā CATALOG ā
ā ā
ā Product ā
ā ProductVariant ā
ā ProductComponent ā
ā Category ā
ā Brand ā
ā ServiceDefinition ā
ā Service ā
ā ServiceDefinitionComponent ā
ā PriceList ā
ā Price ā
ā Promotion ā
ā PromotionRule ā
ā PromotionTarget ā
ā PromotionBenefit ā
ā PromotionUsage ā
āāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
scalar references + snapshots
ā
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā COMMERCE ā
ā ā
ā Customer ā
ā ā
ā Cart āāāāāāāāāāāāāāā CartItem ā
ā ā
ā Order āāāāāāāāāāāāā OrderItem ā
ā ā ā
ā āāāāāāāāāāāāāāāā Invoice āāāāā InvoiceItem ā
ā ā ā ā
ā ā āāā PaymentAllocation ā
ā ā ā ā
ā āāāāāāāāāāāāāāāā Payment āāāāāāā ā
ā ā ā ā
ā ā āāāāāāāāā Refund ā
ā ā ā
ā āāāāāāāāāāāāāāāā Shipment āāāā ShipmentItem ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā