Business Logic Template
Template untuk mendokumentasikan business logic, rules, validations, dan decision flows secara sistematis.
📋 Business Rules Documentation
Rule Template
## Rule: [Rule Name]
**ID**: BR-[MODULE]-[NUMBER]
**Priority**: High | Medium | Low
**Status**: Active | Draft | Deprecated
### Description
[Penjelasan lengkap rule bisnis]
### Conditions
- Kondisi 1: [Detail kondisi]
- Kondisi 2: [Detail kondisi]
### Actions
- Action 1: [Apa yang terjadi]
- Action 2: [Apa yang terjadi]
### Exceptions
- Exception 1: [Kondisi exception]
- Exception 2: [Kondisi exception]
### Examples
**Scenario 1**: [Deskripsi]
- Input: [Data input]
- Expected Output: [Hasil yang diharapkan]
**Scenario 2**: [Deskripsi]
- Input: [Data input]
- Expected Output: [Hasil yang diharapkan]
### Related Rules
- BR-[MODULE]-[NUMBER]: [Rule name]
### Implementation
- Service: [Service name]
- Method: [Method name]
- File: [File path]
🎯 Complete Example: Order Processing
Rule: Order Minimum Amount
ID: BR-ORDER-001
Priority: High
Status: Active
Description
Setiap order harus memenuhi minimum purchase amount untuk dapat diproses. Minimum amount berbeda berdasarkan user tier dan payment method.
Conditions
- User tier: Regular, Silver, Gold, Platinum
- Payment method: Credit Card, Bank Transfer, E-Wallet, COD
- Order subtotal (sebelum discount dan shipping)
Business Logic
IF user.tier == "Regular" THEN
IF payment_method == "COD" THEN
min_amount = 100000
ELSE
min_amount = 50000
END IF
ELSE IF user.tier == "Silver" THEN
min_amount = 25000
ELSE IF user.tier IN ["Gold", "Platinum"] THEN
min_amount = 0 // No minimum
END IF
IF order.subtotal < min_amount THEN
RETURN ERROR "Minimum order amount not met"
END IF
Decision Table
| User Tier | Payment Method | Minimum Amount |
|---|
| Regular | Credit Card | Rp 50,000 |
| Regular | Bank Transfer | Rp 50,000 |
| Regular | E-Wallet | Rp 50,000 |
| Regular | COD | Rp 100,000 |
| Silver | Any | Rp 25,000 |
| Gold | Any | Rp 0 |
| Platinum | Any | Rp 0 |
Examples
Scenario 1: Regular user with COD
Input:
user.tier = "Regular"
payment_method = "COD"
order.subtotal = 75000
Expected Output:
ERROR: "Minimum order amount for COD is Rp 100,000"
Scenario 2: Gold user
Input:
user.tier = "Gold"
payment_method = "Credit Card"
order.subtotal = 10000
Expected Output:
SUCCESS: Order can be processed
Implementation
// internal/order/service/order_service.go
func (s *orderService) ValidateMinimumAmount(
ctx context.Context,
user *model.User,
order *model.Order,
) error {
minAmount := s.getMinimumAmount(user.Tier, order.PaymentMethod)
if order.Subtotal < minAmount {
return errors.New(fmt.Sprintf(
"Minimum order amount for %s is Rp %s",
order.PaymentMethod,
formatCurrency(minAmount),
))
}
return nil
}
func (s *orderService) getMinimumAmount(
tier string,
paymentMethod string,
) decimal.Decimal {
switch tier {
case "Regular":
if paymentMethod == "COD" {
return decimal.NewFromInt(100000)
}
return decimal.NewFromInt(50000)
case "Silver":
return decimal.NewFromInt(25000)
case "Gold", "Platinum":
return decimal.Zero
default:
return decimal.NewFromInt(50000)
}
}
Rule: Stock Reservation
ID: BR-ORDER-002
Priority: High
Status: Active
Description
Saat order dibuat, stock product harus di-reserve untuk mencegah overselling. Stock reservation berlaku selama 30 menit atau sampai payment confirmed.
State Machine
Business Logic
FUNCTION CreateOrder(order):
BEGIN TRANSACTION
FOR EACH item IN order.items:
product = GET Product WHERE id = item.product_id
// Check available stock
available_stock = product.stock - product.reserved_stock
IF available_stock < item.quantity THEN
ROLLBACK TRANSACTION
RETURN ERROR "Insufficient stock for " + product.name
END IF
// Reserve stock
product.reserved_stock += item.quantity
UPDATE product
END FOR
// Create order with status "pending"
order.status = "pending"
order.reservation_expires_at = NOW() + 30 MINUTES
INSERT order
// Schedule auto-cancel job
SCHEDULE_JOB(CancelExpiredOrder, order.id, 30 MINUTES)
COMMIT TRANSACTION
RETURN order
END FUNCTION
FUNCTION ConfirmPayment(order_id):
BEGIN TRANSACTION
order = GET Order WHERE id = order_id
IF order.status != "pending" THEN
ROLLBACK TRANSACTION
RETURN ERROR "Order cannot be confirmed"
END IF
FOR EACH item IN order.items:
product = GET Product WHERE id = item.product_id
// Move from reserved to sold
product.reserved_stock -= item.quantity
product.stock -= item.quantity
UPDATE product
END FOR
order.status = "paid"
order.paid_at = NOW()
UPDATE order
COMMIT TRANSACTION
RETURN order
END FUNCTION
FUNCTION CancelExpiredOrder(order_id):
BEGIN TRANSACTION
order = GET Order WHERE id = order_id
IF order.status == "pending" AND NOW() > order.reservation_expires_at THEN
// Release reserved stock
FOR EACH item IN order.items:
product = GET Product WHERE id = item.product_id
product.reserved_stock -= item.quantity
UPDATE product
END FOR
order.status = "cancelled"
order.cancel_reason = "Payment timeout"
order.cancelled_at = NOW()
UPDATE order
END IF
COMMIT TRANSACTION
END FUNCTION
Flow Diagram
🔄 Validation Rules
## Validation: [Field Name]
**Field**: [field_name]
**Type**: [data type]
**Required**: Yes | No
### Rules
- Rule 1: [Validation rule]
- Rule 2: [Validation rule]
### Error Messages
- Error 1: [Error message]
- Error 2: [Error message]
### Examples
**Valid**:
- `value1`
- `value2`
**Invalid**:
- `value1` → Error: [message]
- `value2` → Error: [message]
Example: Email Validation
Field: email
Type: string
Required: Yes
Rules
- Must be valid email format (RFC 5322)
- Maximum length: 255 characters
- Must be unique in database
- Must not contain spaces
- Domain must have valid MX record (optional)
Error Messages
INVALID_EMAIL_FORMAT: “Email format is invalid”
EMAIL_TOO_LONG: “Email must not exceed 255 characters”
EMAIL_ALREADY_EXISTS: “Email already registered”
EMAIL_CONTAINS_SPACES: “Email must not contain spaces”
Implementation
// internal/shared/validator/email_validator.go
func ValidateEmail(email string) error {
// Check empty
if strings.TrimSpace(email) == "" {
return errors.New("email is required")
}
// Check length
if len(email) > 255 {
return errors.New("email must not exceed 255 characters")
}
// Check spaces
if strings.Contains(email, " ") {
return errors.New("email must not contain spaces")
}
// Check format (regex)
emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
if !emailRegex.MatchString(email) {
return errors.New("email format is invalid")
}
return nil
}
🎲 Decision Tables
Template
| Condition 1 | Condition 2 | Condition 3 | Action | Result |
|---|
| Value A | Value X | Value 1 | Action 1 | Result 1 |
| Value A | Value Y | Value 2 | Action 2 | Result 2 |
| Value B | Value X | Value 1 | Action 3 | Result 3 |
Example: Shipping Cost Calculation
| Weight (kg) | Distance (km) | User Tier | Shipping Cost | Free Shipping |
|---|
| 0-1 | 0-10 | Any | Rp 10,000 | No |
| 0-1 | 11-50 | Any | Rp 15,000 | No |
| 0-1 | 51-100 | Any | Rp 25,000 | No |
| 0-1 | >100 | Any | Rp 50,000 | No |
| 1-5 | 0-10 | Regular | Rp 15,000 | No |
| 1-5 | 0-10 | Gold | Rp 15,000 | Yes (if order > 100k) |
| 1-5 | 11-50 | Regular | Rp 25,000 | No |
| 1-5 | 11-50 | Gold | Rp 25,000 | Yes (if order > 100k) |
| >5 | Any | Any | Calculate | Contact CS |
🧮 Calculation Rules
Template
## Calculation: [Calculation Name]
**Formula**:
result = formula
**Variables**:
- `var1`: [Description]
- `var2`: [Description]
**Constraints**:
- Constraint 1
- Constraint 2
**Examples**:
Input: var1 = X, var2 = Y
Output: result = Z
Example: Order Total Calculation
Formula:
total = subtotal - discount + tax + shipping_cost
Where:
subtotal = SUM(item.quantity * item.unit_price)
discount = coupon_discount + member_discount
tax = subtotal * tax_rate
shipping_cost = calculated_shipping_cost
Variables:
subtotal: Total harga items sebelum discount
discount: Total discount dari coupon dan membership
tax: Pajak (PPN 11%)
shipping_cost: Biaya pengiriman
Constraints:
subtotal >= 0
discount <= subtotal
tax_rate = 0.11 (11% PPN)
total >= 0
Examples:
Example 1: Regular order
Items:
- Product A: 2 x Rp 50,000 = Rp 100,000
- Product B: 1 x Rp 75,000 = Rp 75,000
subtotal = Rp 175,000
discount = Rp 0 (no coupon)
tax = Rp 175,000 * 0.11 = Rp 19,250
shipping_cost = Rp 15,000
total = Rp 175,000 - Rp 0 + Rp 19,250 + Rp 15,000
total = Rp 209,250
Example 2: With coupon
Items:
- Product A: 3 x Rp 100,000 = Rp 300,000
subtotal = Rp 300,000
coupon = 10% discount = Rp 30,000
member_discount = Rp 0
discount = Rp 30,000
tax = (Rp 300,000 - Rp 30,000) * 0.11 = Rp 29,700
shipping_cost = Rp 0 (free shipping)
total = Rp 300,000 - Rp 30,000 + Rp 29,700 + Rp 0
total = Rp 299,700
Implementation:
// internal/order/service/order_calculator.go
type OrderCalculator struct {
taxRate decimal.Decimal
}
func NewOrderCalculator() *OrderCalculator {
return &OrderCalculator{
taxRate: decimal.NewFromFloat(0.11), // 11% PPN
}
}
func (c *OrderCalculator) CalculateTotal(order *model.Order) error {
// Calculate subtotal
subtotal := decimal.Zero
for _, item := range order.Items {
itemTotal := item.UnitPrice.Mul(decimal.NewFromInt(int64(item.Quantity)))
subtotal = subtotal.Add(itemTotal)
}
order.Subtotal = subtotal
// Calculate discount
discount := c.calculateDiscount(order)
order.DiscountAmount = discount
// Calculate tax (after discount)
taxableAmount := subtotal.Sub(discount)
tax := taxableAmount.Mul(c.taxRate)
order.TaxAmount = tax
// Get shipping cost
shippingCost := c.calculateShipping(order)
order.ShippingCost = shippingCost
// Calculate total
total := subtotal.Sub(discount).Add(tax).Add(shippingCost)
order.TotalAmount = total
return nil
}
func (c *OrderCalculator) calculateDiscount(order *model.Order) decimal.Decimal {
discount := decimal.Zero
// Apply coupon discount
if order.Coupon != nil {
if order.Coupon.Type == "percentage" {
couponDiscount := order.Subtotal.Mul(order.Coupon.Value).Div(decimal.NewFromInt(100))
if order.Coupon.MaxDiscount.GreaterThan(decimal.Zero) {
couponDiscount = decimal.Min(couponDiscount, order.Coupon.MaxDiscount)
}
discount = discount.Add(couponDiscount)
} else if order.Coupon.Type == "fixed" {
discount = discount.Add(order.Coupon.Value)
}
}
// Apply member discount
memberDiscount := c.calculateMemberDiscount(order)
discount = discount.Add(memberDiscount)
// Discount cannot exceed subtotal
if discount.GreaterThan(order.Subtotal) {
discount = order.Subtotal
}
return discount
}
Template Usage: Copy template yang sesuai dan isi dengan business logic spesifik untuk fitur Anda. Pastikan semua rules, validations, dan calculations terdokumentasi dengan jelas dan disertai contoh.
Pro Tip: Dokumentasikan business logic sebelum implementasi untuk memastikan semua stakeholder aligned dengan requirements!