Build E-commerce Golang dari Nol: Dari Kebutuhan Bisnis ke Spec SDD
Bangun Santekno Shop dari nol menggunakan Specification-Driven Development. Dari user story ke spec file yang terstruktur, siap menjadi panduan implementasi seluruh seri.
Dari Kebutuhan Bisnis ke Spesifikasi: Santekno Shop dari Nol
Membangun e-commerce Golang dari nol dengan Specification-Driven Development dimulai bukan dari kode, tapi dari kebutuhan bisnis. Banyak tutorial e-commerce langsung masuk ke kode — “buat struct Product, buat handler, buat repository.” Tapi di sini kita melakukan hal yang berbeda: kita mulai dari kebutuhan bisnis, turunkan ke spec yang terstruktur, lalu baru ke kode. Ini adalah flagship project dari seluruh seri AI-Driven Golang.
Santekno Shop bukan proyek demo. Ini adalah aplikasi e-commerce yang akan terus di-extend di topik-topik berikutnya (LLM features, RAG, AI Agents). Di akhir seri, kamu punya satu aplikasi production-grade yang fully AI-powered. Mari mulai dengan memahami apa yang kita bangun.
01.01 Apa itu Santekno Shop?
Sebelum menulis satu baris spec pun, kita perlu potret utuh produk yang akan dibangun — visi, stack teknis, domain, dan target skala. Ringkasan berikut menjadi acuan setiap keputusan desain selanjutnya.
1Santekno Shop — B2C E-Commerce Platform Indonesia
2
3Visi: Platform belanja online yang AI-powered,
4 memberikan pengalaman personal untuk setiap pembeli.
5
6Stack teknis:
7 Backend: Go 1.22, Echo v4
8 Database: PostgreSQL 15 + pgvector (AI-ready)
9 Cache: Redis 7
10 Events: Apache Kafka
11 Infra: Docker + Kubernetes
12 Auth: JWT + refresh token
13
14Domain:
15 - Product Catalog (produk, kategori, inventory)
16 - Order Management (cart, checkout, order lifecycle)
17 - User Management (auth, profile, address)
18 - Payment (integrasi payment gateway)
19 - Notification (email, push notification)
20
21Scale target:
22 100,000 MAU, 10,000 orders/hari
23 P99 response time < 200ms
24 99.9% uptimePotret ini memberi tahu kita dua hal penting: target skala menengah (bukan hyperscale) dan kebutuhan AI-ready sejak awal (pgvector). Kedua batasan inilah yang nanti mengarahkan keputusan arsitektur.
01.02 Kenapa Mulai dari Bisnis, Bukan Kode?
Pertanyaan wajar: kenapa repot menulis spec kalau bisa langsung ngoding? Perbandingan anti-pattern versus pola SDD berikut menunjukkan biaya nyata dari melewatkan tahap spesifikasi.
1ANTI-PATTERN (yang sering dilakukan):
2 Developer: "Kita perlu entity User, Product, Order."
3 Langsung buat tabel, langsung buat struct, langsung buat API.
4
5 Masalah yang muncul bulan ke-3:
6 - "Eh, ternyata satu product bisa punya banyak variant"
7 - "Hm, cancel order ternyata ada window 30 menit"
8 - "Loh, payment kita perlu support installment"
9 → Refactoring besar, migration painful
10
11SDD PATTERN (yang kita gunakan):
12 PO + Developer: "Customer perlu bisa cancel order PENDING"
13 Tulis spec: Given/When/Then yang lengkap
14 Implementasi SESUAI spec
15
16 Benefit:
17 - Tidak ada ambiguitas waktu implementasi
18 - AI code review bisa validasi spec compliance
19 - Test case langsung jelas dari spec
20 - Breaking change terdeteksi saat spec berubahIntinya, spec memindahkan penemuan requirement dari bulan ke-3 (mahal) ke hari pertama (murah). Setiap ambiguitas yang ditangkap di spec adalah refactoring besar yang tidak perlu terjadi nanti.
01.03 Domain Analysis: Stakeholders dan Core Needs
Kebutuhan bisnis selalu berasal dari stakeholder. Pemetaan berikut mengidentifikasi tiga aktor utama beserta kebutuhan intinya, sekaligus menentukan fokus untuk topik ini.
1Stakeholder 1: CUSTOMER (pembeli)
2 Kebutuhan utama:
3 - Temukan produk yang dicari dengan mudah
4 - Proses checkout yang cepat dan aman
5 - Track status pesanan real-time
6 - Batalkan pesanan jika berubah pikiran
7 - Terima notifikasi di setiap perubahan status
8
9Stakeholder 2: SELLER/MERCHANT
10 Kebutuhan utama:
11 - Upload dan manage produk dengan mudah
12 - Lihat order yang masuk dan perlu diproses
13 - Update stok secara real-time
14 - Laporan penjualan yang jelas
15
16Stakeholder 3: ADMIN PLATFORM
17 Kebutuhan utama:
18 - Monitor health platform
19 - Moderasi produk dan review
20 - Manage user dan fraud detection
21
22Untuk topik ini, fokus ke: CUSTOMER journey dan core order flow.
23Seller dan Admin features di-extend di topik berikutnya.Dengan membatasi scope ke customer journey, kita menghindari over-engineering: spec yang kita tulis fokus pada alur yang benar-benar dipakai lebih dulu, bukan semua kemungkinan sekaligus.
01.04 Event Storming: Menemukan Domain Events
Setelah tahu kebutuhan, kita gali kejadian (event) apa saja yang terjadi di sistem. Hasil event storming ringan berikut menjadi bahan mentah untuk menentukan aggregate dan bounded context.
1Proses Event Storming ringan untuk Santekno Shop:
2
3Domain Events yang teridentifikasi:
4 [ORANGE = domain events]
5
6 UserRegistered
7 UserLoggedIn
8 ProductCreated
9 ProductStockUpdated
10 CartItemAdded
11 CartItemRemoved
12 OrderPlaced
13 OrderConfirmed
14 OrderShipped
15 OrderDelivered
16 OrderCancelled
17 PaymentInitiated
18 PaymentCompleted
19 PaymentFailed
20 NotificationSent
21
22Commands yang memicu events:
23 RegisterUser → UserRegistered
24 PlaceOrder → OrderPlaced → PaymentInitiated
25 ConfirmPayment → PaymentCompleted → OrderConfirmed
26 CancelOrder → OrderCancelled → (notify customer)
27 ShipOrder → OrderShipped → (notify customer)
28
29Aggregates yang teridentifikasi:
30 User (UserID)
31 Product (ProductID)
32 Cart (UserID — 1 cart per user)
33 Order (OrderID)
34 Payment (PaymentID)Event dan aggregate ini adalah “kosakata domain” kita. Semua spec, tabel, dan Kafka topic nantinya akan konsisten memakai nama-nama ini — sumber kebenaran tunggal untuk penamaan.
01.05 Bounded Contexts untuk Santekno Shop
Dari daftar event dan aggregate, kita kelompokkan tanggung jawab ke dalam bounded context yang terisolasi. Pemetaan berikut menentukan batas modul dan pola integrasinya.
1Bounded Context 1: USER MANAGEMENT
2 Entities: User, Address, Session
3 Responsibilities: auth, profile, address book
4 Events consumed: -
5 Events produced: UserRegistered, UserLoggedIn
6
7Bounded Context 2: PRODUCT CATALOG
8 Entities: Product, Category, Inventory
9 Responsibilities: catalog, search, stock management
10 Events consumed: OrderPlaced (untuk reserve stock)
11 Events produced: ProductCreated, StockUpdated, StockReserved
12
13Bounded Context 3: ORDER MANAGEMENT
14 Entities: Cart, Order, OrderItem
15 Responsibilities: cart, checkout, order lifecycle
16 Events consumed: PaymentCompleted, PaymentFailed
17 Events produced: OrderPlaced, OrderConfirmed, OrderCancelled, OrderShipped
18
19Bounded Context 4: PAYMENT
20 Entities: Payment, Transaction
21 Responsibilities: integrasi gateway, reconciliation
22 Events consumed: OrderPlaced
23 Events produced: PaymentInitiated, PaymentCompleted, PaymentFailed
24
25Bounded Context 5: NOTIFICATION
26 Entities: NotificationLog, Template
27 Responsibilities: email, push notification
28 Events consumed: semua event yang butuh notifikasi
29 Events produced: NotificationSent
30
31Untuk topik ini: implementasi BC 1, 3 (core), 4 (simplified).
32BC 2 dan 5 dibangun parallel dan diintegrasikan.Batas yang jelas antar context inilah yang membuat modular monolith kita tetap rapi — setiap context bisa di-extract menjadi microservice nanti tanpa merombak domain logic.
01.06 Format Spec yang Kita Gunakan
Agar spec konsisten dan bisa divalidasi mesin, kita pakai template Gherkin-inspired Given/When/Then. Kerangka berikut adalah cetakan yang dipakai untuk semua fitur.
1# Format Spec Santekno Shop
2# Menggunakan Gherkin-inspired Given/When/Then
3
4## Feature: [Nama Fitur]
5
6### Context
7[Paragraf konteks bisnis — kenapa fitur ini ada]
8
9### Actors
10- [Actor 1]: [role dan kapabilitas]
11- [Actor 2]: [role dan kapabilitas]
12
13### Acceptance Criteria
14
15**AC[N]: [Nama Scenario]**
16- Given: [precondition]
17- When: [action / HTTP method + endpoint]
18- Then: [expected HTTP response code]
19- Then: [expected response body]
20- Then: [expected side effect — DB, event, etc.]
21- Constraints: [business rules, limits, timing]
22
23### Business Rules
24- [Rule 1]
25- [Rule 2]
26
27### Data Contracts
28Request:
29{...}
30Response:
31{...}
32
33### Implementation Notes
34- [Technical note yang relevant]
35
36### Links
37- Handler: `delivery/http/handler/[file].go`
38- UseCase: `internal/usecase/[domain]/[file].go`
39- Repository: `internal/repository/postgres/[file].go`Template terstruktur ini penting: setiap AC punya Given/When/Then yang eksplisit sehingga bisa langsung dipetakan ke test case dan divalidasi oleh CI. Berikutnya, kita isi template ini untuk tujuh fitur inti.
01.07 Spec 1: User Registration
Fitur pertama yang paling fundamental adalah registrasi user. Spec berikut mendefinisikan empat skenario — dari happy path hingga semua kasus error yang mungkin.
1## Feature: User Registration
2
3### Context
4Customer baru perlu mendaftarkan akun sebelum bisa melakukan pembelian.
5Email digunakan sebagai primary identifier. Password di-hash menggunakan bcrypt.
6
7### Actors
8- Guest User: belum memiliki akun
9
10### Acceptance Criteria
11
12**AC1: Registrasi berhasil**
13- Given: email belum terdaftar di sistem
14- Given: password memenuhi syarat (min 8 karakter, ada angka)
15- When: POST /api/v1/auth/register
16 {"email": "user@example.com", "password": "Secret123", "name": "John Doe"}
17- Then: 201 Created
18- Then: Response body contains: {"id": "uuid", "email": "...", "name": "..."}
19- Then: Token TIDAK di-return di registration (user perlu login terpisah)
20- Then: Event `UserRegistered` di-publish ke Kafka
21
22**AC2: Email sudah terdaftar**
23- Given: email sudah ada di sistem
24- When: POST /api/v1/auth/register
25- Then: 409 Conflict
26- Then: {"error": "EMAIL_ALREADY_REGISTERED"}
27
28**AC3: Password tidak memenuhi syarat**
29- Given: password kurang dari 8 karakter ATAU tidak ada angka
30- When: POST /api/v1/auth/register
31- Then: 400 Bad Request
32- Then: {"error": "WEAK_PASSWORD", "detail": "..."}
33
34**AC4: Email format tidak valid**
35- When: POST /api/v1/auth/register dengan email "bukan-email"
36- Then: 400 Bad Request
37- Then: {"error": "INVALID_EMAIL"}
38
39### Business Rules
40- Email harus di-lowercase sebelum disimpan
41- Password di-hash dengan bcrypt cost 12
42- Name: 2-100 karakter
43- Tidak ada rate limit di registrasi (tapi ada di login)
44
45### Implementation Notes
46- Repository return nil, nil jika email tidak ditemukan
47- Jangan expose apakah email sudah terdaftar di error message produksi
48 (untuk mencegah email enumeration attack) — tapi untuk dev, OKPerhatikan bahwa error case (AC2-AC4) didefinisikan sama detailnya dengan happy path. Inilah kekuatan spec: setiap error yang mungkin sudah punya kode dan status HTTP yang pasti, jauh sebelum implementasi.
01.08 Spec 2: User Login
Setelah registrasi, user perlu bisa login dan menerima JWT. Spec login berikut mencakup autentikasi, keamanan credential, hingga rate limiting.
1## Feature: User Login (JWT Authentication)
2
3### Context
4User yang sudah terdaftar perlu bisa login dan mendapatkan JWT token
5untuk mengakses endpoint yang butuh autentikasi.
6
7### Acceptance Criteria
8
9**AC1: Login berhasil**
10- Given: email dan password valid, user aktif
11- When: POST /api/v1/auth/login
12 {"email": "user@example.com", "password": "Secret123"}
13- Then: 200 OK
14- Then:
15 {
16 "access_token": "eyJ...",
17 "refresh_token": "...",
18 "expires_in": 3600,
19 "token_type": "Bearer"
20 }
21- Then: access_token valid selama 1 jam
22- Then: refresh_token valid selama 7 hari, disimpan di DB
23
24**AC2: Email tidak ditemukan**
25- When: POST /api/v1/auth/login dengan email yang tidak ada
26- Then: 401 Unauthorized
27- Then: {"error": "INVALID_CREDENTIALS"}
28- Note: JANGAN bedakan error "email tidak ada" dan "password salah"
29
30**AC3: Password salah**
31- When: POST /api/v1/auth/login dengan password yang salah
32- Then: 401 Unauthorized
33- Then: {"error": "INVALID_CREDENTIALS"}
34
35**AC4: Rate limiting — terlalu banyak percobaan**
36- Given: 5 login gagal dalam 15 menit dari IP yang sama
37- When: percobaan ke-6
38- Then: 429 Too Many Requests
39- Then: {"error": "TOO_MANY_ATTEMPTS", "retry_after": 900}
40
41### Business Rules
42- Access token: HS256, expire 1 jam
43- Refresh token: random 32 bytes, expire 7 hari
44- Rate limit: 5 attempts per 15 menit per IP (di Redis)
45- Tidak ada perbedaan response untuk email tidak ada vs password salah
46
47### Data Contracts
48Request: {"email": string, "password": string}
49Response: {"access_token": string, "refresh_token": string, "expires_in": int}Detail keamanan seperti “jangan bedakan error email vs password” (mencegah enumeration) dan rate limit tercantum eksplisit di spec. Keamanan di-desain masuk, bukan ditambahkan belakangan.
01.09 Spec 3: Place Order (Core Flow)
Inilah jantung dari e-commerce: membuat order. Spec berikut mengatur pembuatan order dengan reservasi stok yang atomik dan inisiasi payment.
1## Feature: Place Order
2
3### Context
4Customer yang sudah login bisa melakukan pemesanan. Order dibuat
5dengan status PENDING, lalu system memulai proses payment.
6Stock di-reserve saat order dibuat dan dikembalikan jika payment gagal.
7
8### Acceptance Criteria
9
10**AC1: Order berhasil dibuat**
11- Given: user sudah login (valid JWT)
12- Given: semua product_id valid dan stok cukup
13- Given: payment_method valid (BANK_TRANSFER | QRIS | CREDIT_CARD)
14- When: POST /api/v1/orders
15 {
16 "items": [
17 {"product_id": "uuid", "quantity": 2}
18 ],
19 "shipping_address_id": "uuid",
20 "payment_method": "QRIS"
21 }
22- Then: 201 Created
23- Then: Response includes order_id, status: "PENDING", total_amount_idr, payment_url
24- Then: Stock di-reserve (dikurangi dari available, masuk reserved)
25- Then: Event `OrderPlaced` di-publish ke Kafka
26- Then: Payment initiated (payment_url tersedia)
27
28**AC2: Salah satu product habis stok**
29- Given: quantity yang diminta melebihi stok tersedia untuk salah satu product
30- When: POST /api/v1/orders
31- Then: 422 Unprocessable Entity
32- Then: {"error": "INSUFFICIENT_STOCK", "product_id": "uuid"}
33- Then: Stock TIDAK berubah (atomic check)
34
35**AC3: Product tidak ditemukan**
36- Given: salah satu product_id tidak ada
37- When: POST /api/v1/orders
38- Then: 404 Not Found
39- Then: {"error": "PRODUCT_NOT_FOUND", "product_id": "uuid"}
40
41**AC4: User belum login**
42- When: POST /api/v1/orders tanpa JWT header
43- Then: 401 Unauthorized
44- Then: {"error": "UNAUTHORIZED"}
45
46**AC5: Keranjang kosong**
47- When: POST /api/v1/orders dengan items: []
48- Then: 400 Bad Request
49- Then: {"error": "EMPTY_ORDER"}
50
51### Business Rules
52- Order amount dalam IDR sebagai int64 (cents: 1 IDR = 100 cents)
53 → Rp 50.000 = 5_000_000 cents
54- Stock reservation dan order creation harus atomic (satu DB transaction)
55- Payment URL expire dalam 24 jam
56- Maximum 10 items per order
57- Maximum quantity per item: 100
58
59### Data Contracts
60total_amount_idr: int64 (cents, bukan rupiah langsung)Business rule “atomic stock reservation” dan “int64 cents” di sini bukan detail sepele — keduanya mencegah dua bug klasik e-commerce: overselling saat race condition dan pembulatan uang. Spec menegakkannya sebelum kode ada.
01.10 Spec 4: Cancel Order
Customer kadang berubah pikiran. Spec cancel order berikut menetapkan window waktu, restore stok, dan semua kondisi ketika pembatalan ditolak.
1## Feature: Cancel Order
2
3### Context
4Customer bisa cancel order yang masih PENDING dalam window 30 menit.
5Setelah 30 menit, order tidak bisa di-cancel karena sudah masuk proses.
6
7### Acceptance Criteria
8
9**AC1: Cancel order PENDING dalam window**
10- Given: order status = PENDING
11- Given: order dibuat < 30 menit yang lalu
12- Given: request dari user yang punya order ini
13- When: DELETE /api/v1/orders/{order_id}
14- Then: 204 No Content
15- Then: Order status berubah ke CANCELLED
16- Then: Stock di-restore (reserved → available)
17- Then: Event `OrderCancelled` di-publish ke Kafka
18- Note: Stock restore dan status update harus atomic (satu transaksi)
19
20**AC2: Order sudah bukan PENDING**
21- Given: order status = CONFIRMED, SHIPPED, atau DELIVERED
22- When: DELETE /api/v1/orders/{order_id}
23- Then: 409 Conflict
24- Then: {"error": "ORDER_NOT_CANCELLABLE"}
25
26**AC3: Window cancel sudah lewat**
27- Given: order status = PENDING
28- Given: order dibuat > 30 menit yang lalu
29- When: DELETE /api/v1/orders/{order_id}
30- Then: 409 Conflict
31- Then: {"error": "CANCEL_WINDOW_EXPIRED"}
32
33**AC4: Order tidak ditemukan / bukan milik user**
34- When: DELETE /api/v1/orders/{order_id} untuk order yang tidak ada
35 atau milik user lain
36- Then: 404 Not Found
37- Then: {"error": "ORDER_NOT_FOUND"}
38
39### Business Rules
40- Window cancel: 30 menit dari Order.CreatedAt
41- Stock restore dalam transaksi yang sama dengan status update
42- Kafka event publish boleh async (tidak block response)
43- Setelah cancel, payment URL menjadi invalidDua status conflict yang berbeda (ORDER_NOT_CANCELLABLE vs CANCEL_WINDOW_EXPIRED) memberi frontend pesan yang tepat ke user. Spec yang membedakan keduanya mencegah UX yang membingungkan.
01.11 Spec 5: Get Order List
User perlu melihat riwayat pesanannya. Spec berikut mengatur pagination, filter status, dan isolasi data antar user.
1## Feature: Get Order List
2
3### Context
4Customer bisa melihat list order mereka, dengan pagination dan filter status.
5
6### Acceptance Criteria
7
8**AC1: List order dengan pagination**
9- Given: user sudah login
10- When: GET /api/v1/orders?page=1&limit=10
11- Then: 200 OK
12- Then:
13 {
14 "data": [{...order objects...}],
15 "pagination": {
16 "page": 1,
17 "limit": 10,
18 "total": 25,
19 "total_pages": 3
20 }
21 }
22
23**AC2: Filter by status**
24- When: GET /api/v1/orders?status=PENDING
25- Then: hanya return orders dengan status PENDING
26
27**AC3: Default pagination**
28- When: GET /api/v1/orders (tanpa query param)
29- Then: page=1, limit=10 sebagai default
30
31**AC4: User hanya lihat order sendiri**
32- User A tidak bisa melihat order User B
33- Filter by user_id dilakukan di query, bukan di aplikasi layer
34
35### Business Rules
36- Max limit per page: 100
37- Default limit: 10
38- Order sorted by created_at DESC (terbaru dulu)
39- Hanya return order milik authenticated userPerhatikan AC4: filter user_id dilakukan di level query, bukan di aplikasi. Ini keputusan keamanan yang ditegakkan spec — kebocoran data antar user jadi mustahil, bukan sekadar “diusahakan”.
01.12 Spec 6: Product Catalog
Tanpa katalog produk, tidak ada yang bisa dibeli. Spec berikut mendefinisikan listing, search, dan detail produk beserta aturan bisnisnya.
1## Feature: Product Catalog
2
3### Context
4User bisa browse produk, search, dan lihat detail produk.
5Product listing mendukung filter dan sort.
6
7### Acceptance Criteria
8
9**AC1: List products dengan filter**
10- When: GET /api/v1/products?category=electronics&min_price=100000&max_price=500000
11- Then: 200 OK dengan list produk yang sesuai filter
12- Then: Setiap produk include: id, name, price_idr, stock, category, images[0]
13
14**AC2: Search produk**
15- When: GET /api/v1/products?q=sepatu+lari
16- Then: 200 OK dengan produk yang relevan
17- Then: Sorted by relevance score
18
19**AC3: Detail produk**
20- When: GET /api/v1/products/{product_id}
21- Then: 200 OK dengan detail lengkap:
22 id, name, description, price_idr, stock, category, images[], specs{}
23- Given: product tidak ada
24- Then: 404 Not Found
25
26**AC4: Produk habis stok**
27- When: GET /api/v1/products/{product_id}
28- Then: 200 OK, stock = 0
29- Note: Produk out of stock masih di-return, tidak di-hide
30
31### Business Rules
32- Price dalam IDR sebagai int64 cents
33- Images: array of URL, max 10 images
34- Search: full-text search menggunakan PostgreSQL tsvector
35 (akan di-upgrade ke pgvector semantic search di topik berikutnya)
36- Default sort: created_at DESC
37- Max 50 items per pageBusiness rule search yang memakai tsvector sekarang, tapi disiapkan untuk upgrade ke pgvector, adalah contoh spec yang memikirkan evolusi. Fondasi AI-ready ditanam sejak fitur paling dasar.
01.13 Spec 7: Payment Webhook
Fitur terakhir menghubungkan kita ke dunia luar: payment gateway. Spec webhook berikut mengatur verifikasi signature, update status order, dan idempotency.
1## Feature: Payment Webhook Handler
2
3### Context
4Payment gateway (simulasi) akan mengirim webhook ketika payment
5berhasil atau gagal. System perlu handle webhook ini dan update
6status order accordingly.
7
8### Acceptance Criteria
9
10**AC1: Payment berhasil**
11- When: POST /api/v1/webhooks/payment
12 {
13 "order_id": "uuid",
14 "payment_id": "pay_xxx",
15 "status": "PAID",
16 "amount_idr": 5000000,
17 "signature": "hmac-sha256-hash"
18 }
19- Given: signature valid (HMAC-SHA256 dengan shared secret)
20- Then: 200 OK
21- Then: Order status berubah ke CONFIRMED
22- Then: Payment record updated dengan payment_id
23- Then: Event `PaymentCompleted` di-publish ke Kafka
24- Then: Event `OrderConfirmed` di-publish ke Kafka
25
26**AC2: Payment gagal**
27- When: POST /api/v1/webhooks/payment dengan status: "FAILED"
28- Then: 200 OK
29- Then: Order status berubah ke CANCELLED
30- Then: Stock di-restore
31- Then: Event `PaymentFailed` di-publish ke Kafka
32
33**AC3: Signature tidak valid**
34- When: POST /api/v1/webhooks/payment dengan signature yang salah
35- Then: 401 Unauthorized
36- Then: {"error": "INVALID_SIGNATURE"}
37- Note: Log attempt untuk security monitoring
38
39**AC4: Idempotency — webhook duplikat**
40- Given: webhook untuk payment_id yang sama sudah diprocess
41- When: webhook yang sama diterima lagi
42- Then: 200 OK (idempoten)
43- Then: Tidak ada perubahan state
44
45### Business Rules
46- Signature: HMAC-SHA256(payload_json, WEBHOOK_SECRET)
47- Idempotency key: payment_id
48- Timeout untuk proses webhook: 10 detik
49- Simpan webhook log untuk auditAC4 (idempotency) krusial karena payment gateway sering mengirim webhook yang sama dua kali. Dengan payment_id sebagai idempotency key di spec, duplikasi tidak akan pernah menggandakan konfirmasi order.
01.14 Architecture Decision Records (ADR)
Beberapa keputusan teknis lintas fitur perlu dicatat resmi agar konsisten. Kelima ADR berikut menjadi hukum tak tertulis yang mengikat seluruh implementasi.
1ADR 001: Monetary Values menggunakan int64 cents
2 Status: ACCEPTED
3 Context: Floating point arithmetic tidak aman untuk financial data
4 Decision: Semua monetary value disimpan sebagai int64 (IDR cents)
5 1 IDR = 100 cents → Rp 50.000 = 5,000,000 cents
6 Consequences: Semua display/formatting dilakukan di client side
7
8ADR 002: Repository Not-Found menggunakan nil, nil
9 Status: ACCEPTED
10 Context: Bedakan "not found" dari error infrastruktur
11 Decision: Repository return (nil, nil) untuk not-found
12 Usecase interpret nil result sebagai "not found"
13 Consequences: Perlu nil check di setiap usecase setelah repo call
14
15ADR 003: Stock Management menggunakan optimistic locking
16 Status: ACCEPTED
17 Context: Race condition pada stock deduction
18 Decision: Gunakan UPDATE ... WHERE stock >= quantity RETURNING *
19 Jika 0 rows affected = insufficient stock
20 Consequences: Tidak perlu SELECT lalu UPDATE (race-free)
21
22ADR 004: Event publishing via Kafka Outbox Pattern
23 Status: ACCEPTED
24 Context: Pastikan event published jika transaksi DB sukses
25 Decision: Simpan event ke outbox table dalam transaksi yang sama
26 Background worker polling outbox dan publish ke Kafka
27 Consequences: At-least-once delivery; consumer harus idempoten
28
29ADR 005: pgvector untuk future AI features
30 Status: ACCEPTED
31 Context: Topik berikutnya akan butuh semantic search
32 Decision: Install pgvector extension dari awal
33 Tabel products punya kolom embedding VECTOR(1536)
34 Consequences: Slight overhead di sekarang, zero migration cost nantiADR membuat keputusan bisa ditelusuri: enam bulan lagi, saat ada yang bertanya “kenapa uang pakai int64?”, jawabannya sudah tercatat lengkap dengan konteks dan konsekuensinya.
01.15 Error Code Convention
Agar client bisa menangani error secara konsisten, kita definisikan registry kode error terpusat. Kode Go berikut adalah satu-satunya sumber kebenaran untuk semua error code.
1// internal/domain/errors.go
2// Standard error codes untuk Santekno Shop
3
4package domain
5
6// Auth errors
7const (
8 ErrCodeEmailAlreadyRegistered = "EMAIL_ALREADY_REGISTERED"
9 ErrCodeWeakPassword = "WEAK_PASSWORD"
10 ErrCodeInvalidEmail = "INVALID_EMAIL"
11 ErrCodeInvalidCredentials = "INVALID_CREDENTIALS"
12 ErrCodeTooManyAttempts = "TOO_MANY_ATTEMPTS"
13 ErrCodeUnauthorized = "UNAUTHORIZED"
14 ErrCodeForbidden = "FORBIDDEN"
15)
16
17// Order errors
18const (
19 ErrCodeOrderNotFound = "ORDER_NOT_FOUND"
20 ErrCodeOrderNotCancellable = "ORDER_NOT_CANCELLABLE"
21 ErrCodeCancelWindowExpired = "CANCEL_WINDOW_EXPIRED"
22 ErrCodeInsufficientStock = "INSUFFICIENT_STOCK"
23 ErrCodeProductNotFound = "PRODUCT_NOT_FOUND"
24 ErrCodeEmptyOrder = "EMPTY_ORDER"
25)
26
27// Payment errors
28const (
29 ErrCodeInvalidSignature = "INVALID_SIGNATURE"
30 ErrCodePaymentNotFound = "PAYMENT_NOT_FOUND"
31)
32
33// Generic
34const (
35 ErrCodeInternalError = "INTERNAL_ERROR"
36 ErrCodeValidationFailed = "VALIDATION_FAILED"
37 ErrCodeNotFound = "NOT_FOUND"
38)
39
40// HTTP error response format
41type ErrorResponse struct {
42 Error string `json:"error"`
43 Detail string `json:"detail,omitempty"`
44}Dengan semua kode error di satu file, tidak ada lagi typo “order_not_found” vs “ORDER_NOT_FOUND”. Frontend pun bisa generate enum error langsung dari konstanta ini.
01.16 API Design Principles
Semua endpoint harus berperilaku konsisten. Konvensi desain API berikut menjadi kontrak gaya yang berlaku di seluruh service.
1Santekno Shop API Design Conventions:
2
3BASE URL: /api/v1/
4
5AUTHENTICATION:
6 Header: Authorization: Bearer {access_token}
7 Endpoints yang tidak perlu auth: POST /auth/register, POST /auth/login,
8 GET /products, GET /products/:id
9
10RESPONSE FORMAT:
11 Success (list): {"data": [...], "pagination": {...}}
12 Success (single): {object fields langsung, tidak di-wrap}
13 Error: {"error": "ERROR_CODE", "detail": "..."}
14
15HTTP STATUS CODES:
16 200: OK (GET berhasil)
17 201: Created (POST berhasil membuat resource)
18 204: No Content (DELETE berhasil)
19 400: Bad Request (validasi input gagal)
20 401: Unauthorized (tidak ada / invalid token)
21 403: Forbidden (token valid tapi tidak punya akses)
22 404: Not Found (resource tidak ada)
23 409: Conflict (state conflict — order tidak bisa dicancel)
24 422: Unprocessable Entity (business logic fail — stok habis)
25 429: Too Many Requests (rate limit)
26 500: Internal Server Error
27
28NAMING:
29 Endpoint: snake_case, plural noun
30 Query params: snake_case
31 Response body: snake_case
32
33MONETARY VALUES:
34 Semua dalam integer cents (IDR)
35 Field name suffix: _idr (e.g., total_amount_idr, price_idr)
36
37PAGINATION:
38 Query: ?page=1&limit=10
39 Response: {"data": [...], "pagination": {"page": 1, "limit": 10, "total": 100}}Konvensi ini menghilangkan “kejutan per-endpoint”. Sekali developer paham polanya, mereka bisa menebak bentuk response endpoint mana pun tanpa membaca dokumentasi tiap kali.
01.17 Spec Validation dengan CI
Spec yang bagus tak ada gunanya kalau tak ditegakkan. Konfigurasi CI berikut menjalankan audit spec compliance otomatis pada setiap perubahan.
1# Setiap spec yang dibuat akan di-validate oleh CI
2# menggunakan specify audit dari topik sebelumnya
3
4# .specify/config.yml
5version: '1.0'
6project:
7 name: Santekno Shop
8 language: go
9 framework: echo
10
11audit:
12 threshold: 80
13 scope:
14 include:
15 - 'internal/usecase/**'
16 - 'delivery/http/handler/**'
17
18# Spec files yang sudah kita buat di artikel ini:
19# .specify/features/user-registration/spec.md
20# .specify/features/user-login/spec.md
21# .specify/features/place-order/spec.md
22# .specify/features/cancel-order/spec.md
23# .specify/features/get-orders/spec.md
24# .specify/features/product-catalog/spec.md
25# .specify/features/payment-webhook/spec.mdDengan threshold 80, setiap PR yang menurunkan spec compliance di bawah ambang akan otomatis ditolak. Spec berubah dari dokumen pasif menjadi gerbang kualitas yang aktif.
01.18 Sprint Planning dari Spec
Spec juga langsung menjadi rencana kerja. Pemetaan berikut menunjukkan bagaimana tujuh spec diterjemahkan ke empat sprint dua-mingguan.
1Sprint 1 (2 minggu): Auth + User Foundation
2 Spec: user-registration, user-login
3 Deliverable:
4 - Database schema: users, sessions, refresh_tokens
5 - Handler: POST /auth/register, POST /auth/login, POST /auth/refresh
6 - UseCase: RegisterUser, LoginUser, RefreshToken
7 - Middleware: JWT authentication
8 - Tests: semua AC dari spec
9
10Sprint 2 (2 minggu): Product Catalog
11 Spec: product-catalog
12 Deliverable:
13 - Database schema: products, categories, product_images
14 - Handler: GET /products, GET /products/:id
15 - UseCase: ListProducts, GetProduct, SearchProducts
16 - Full-text search dengan tsvector
17 - Tests: semua AC dari spec
18
19Sprint 3 (2 minggu): Order Management
20 Spec: place-order, cancel-order, get-orders
21 Deliverable:
22 - Database schema: orders, order_items, inventory_reservations
23 - Handler: POST /orders, DELETE /orders/:id, GET /orders
24 - UseCase: PlaceOrder, CancelOrder, GetOrders
25 - Kafka event publishing
26 - Tests: semua AC dari spec
27
28Sprint 4 (2 minggu): Payment + Integration
29 Spec: payment-webhook
30 Deliverable:
31 - Database schema: payments, webhook_logs, outbox
32 - Handler: POST /webhooks/payment
33 - UseCase: HandlePaymentWebhook
34 - Outbox pattern implementation
35 - E2E test: full order flowKarena setiap deliverable diturunkan langsung dari AC spec, estimasi sprint jadi jauh lebih akurat — tidak ada “requirement tersembunyi” yang muncul di tengah sprint.
01.19 Tools yang Digunakan
Sebelum sprint pertama, lingkungan kerja harus siap. Perintah berikut memasang seluruh tool yang dibutuhkan sepanjang topik ini sekaligus memverifikasinya.
1# Install semua tools yang dibutuhkan
2
3# Go (sudah ada)
4go version # v1.22+
5
6# Database
7docker pull postgres:15 # dengan pgvector
8
9# specify CLI (dari topik sebelumnya)
10npm install -g @santekno/spec-kit
11
12# Database migration
13go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
14
15# Mock generation
16go install go.uber.org/mock/mockgen@latest
17
18# API testing
19brew install httpie # atau: curl + jq
20
21# Development tools
22go install github.com/air-verse/air@latest # hot reload
23go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
24
25# Verify semua ready:
26go version && migrate -version && mockgen --version
27echo "✅ All tools ready"Menjalankan blok verifikasi di akhir memastikan tidak ada tool yang hilang saat implementasi dimulai — lebih baik gagal di sini daripada di tengah coding session.
01.20 Ringkasan: Fondasi yang Solid Sebelum Baris Kode Pertama
Spec yang sudah kita tulis di artikel ini adalah kontrak antara bisnis dan engineering. Setiap baris kode di artikel-artikel berikutnya akan di-derive dari spec ini.
7 spec yang sudah didefinisikan:
- User Registration (4 ACs)
- User Login (4 ACs)
- Place Order (5 ACs)
- Cancel Order (4 ACs)
- Get Order List (4 ACs)
- Product Catalog (4 ACs)
- Payment Webhook (4 ACs)
Total: 29 Acceptance Criteria yang akan menjadi test cases.
Dengan spec ini, CI pipeline dari topik sebelumnya bisa langsung enforce compliance — setiap PR yang menyentuh implementasi akan di-audit terhadap spec ini. Spec adalah kebenaran; kode adalah implementasinya.
Di artikel berikutnya, kita design arsitektur multi-service Santekno Shop dengan bantuan Claude — dari keputusan monolith vs microservice hingga dependency graph yang lengkap.