Part 01 · Backend Mindset

Understanding Business Logic

BusinessLogicRules

যে rule গুলো তোমার product কে তোমার product বানায়, আর সেগুলো কেন server এ থাকা উচিত।

⏱ 1 ঘণ্টা · Station 8 / 153

দুটো app, দুই রকম দাম

একটা food delivery company র একটা mobile app আছে, একটা website ও আছে। একজন customer এক মিনিটের ব্যবধানে দুই জায়গা থেকে একই বিরিয়ানি order করলো। App নিলো 380 টাকা। Website নিলো 440।

দুটোই "ঠিক"। দুটো আলাদা team বানিয়েছে, আর প্রত্যেক team নিয়মগুলো নিজের মতো করে লিখেছে: minimum order, delivery fee, প্রথম order এর discount। এক team discount অন্যভাবে round করেছে। আরেক team ভুলে গেছে যে weekend এ 500 এর বেশি হলে delivery free।

যে code একটা product কে এই product বানায়, তার দাম, সীমা, permission আর প্রতিশ্রুতি, তাকে বলে business logic। এই lesson এ দেখবো এটা চেনা যায় কীভাবে, এক জায়গায় রাখা যায় কীভাবে, আর বাকি সবকিছু থেকে আলাদা রাখা যায় কীভাবে।

সমস্যা: নিয়মগুলো সব জায়গায় ছড়িয়ে পড়ে

যেকোনো বড় হতে থাকা backend দেখো, একই নিয়ম কয়েক জায়গায় লেখা পাবে:

নিয়ম কোথায় লুকিয়ে থাকেওখানে গেলো কেন
Mobile app এ"Checkout এর আগে total দেখানোর জন্য"
Website এএকই কারণ, অন্য team
HTTP handler এ"একটা if বসানোর সবচেয়ে সহজ জায়গা ছিল"
একটা SQL query তেWHERE subtotal >= 200, একটা report এর ভেতরে চাপা পড়ে আছে
রাতের একটা script এRefund এর জন্য কারো discount টা দরকার ছিল

প্রতিটা copy মানে আলাদা হয়ে যাওয়ার আরেকটা সুযোগ। Marketing যখন "20% ছাড়, সর্বোচ্চ 100" কে "25% ছাড়, সর্বোচ্চ 120" করবে, কাউকে সব copy খুঁজে বের করতে হবে, আর সে সবগুলো খুঁজে পাবে না।

আরেকটা সমস্যা আছে: নিয়মগুলো যখন plumbing এর সাথে জট পাকিয়ে থাকে (HTTP পড়া, database এর সাথে কথা বলা, JSON বানানো), তখন server আর database চালু না করে নিয়ম test করা যায় না, তাই কেউ ঠিকমতো test ও করে না।

ক্রিকেটের নিয়মের বই ভাবো

একটা ক্রিকেট ম্যাচে অনেক কিছু চলে: pitch, scoreboard, TV তে সম্প্রচার, টিকেটের গেট। এর কোনোটাই ক্রিকেট না। ক্রিকেট হলো নিয়মের বই: কখন out, এক over এ কয়টা বল, কখন no-ball।

নিয়মের বই একবার লেখা হয়, একটা কর্তৃপক্ষ লেখে, আর প্রতিটা ম্যাচ সেটা মানে। TV channel নিজের মতো LBW এর নিয়ম বানায় না। আর stadium ছাড়াই নিয়মগুলো বোঝা আর check করা যায়: পড়া যায়, আলোচনা করা যায়, কাগজে পরিস্থিতি ধরে test করা যায় ("বল leg এর বাইরে pitch করেছে, not out")।

Business logic হলো তোমার product এর নিয়মের বই। HTTP, database আর user interface হলো stadium।

many doors in, one set of rulesmobile appwebsitecall centreHTTPplumbingbusiness rulesopen? min 200? discount?databaseplumbing350 taka150 takarefused: minimum order is 200600 taka
App, website আর call-centre tool থেকে order আসে। সবাই মাঝখানের একই নিয়মের ভেতর দিয়ে যায়; এক পাশে HTTP আর অন্য পাশে database শুধু plumbing। 150 টাকার order যে দরজা দিয়েই আসুক, নিয়ম সেটা ফিরিয়ে দেয়।

Analogy টা কোথায় খাটে না। ক্রিকেটের নিয়ম কালেভদ্রে আর ধীরে বদলায়। একটা business নিয়ম বদলায় সারাক্ষণ: নতুন discount, নতুন শহর, নতুন payment এর উপায়, কখনো প্রতি সপ্তাহে। ঠিক এই কারণেই নিয়মগুলো একটা পরিষ্কার চিহ্ন দেওয়া জায়গায় থাকতে হবে: কোনো কিছু যত বেশি বদলায়, তার copy থাকলে তত বেশি ভোগায়।

মূল জিনিসগুলো

  1. Business logic হলো সেই code যেটা business এর নেওয়া সিদ্ধান্তগুলো ধরে রাখে: কে কী করতে পারবে, কিসের দাম কত, কী চলবে আর কী চলবে না, এরপর কী হবে। Company র programmer না এমন কারো যদি এ নিয়ে মতামত থাকে ("500 থেকে delivery free"), তাহলে এটা business logic।
  2. Plumbing (infrastructure ও বলে) হলো বাকি সবকিছু: HTTP parse করা, database এ পড়া-লেখা, email পাঠানো, logging, retry। এগুলো লাগবেই, কিন্তু এগুলো তোমার product না। অন্য company র backend এর plumbing ও প্রায় একই।
  3. যতটা পারো নিয়মগুলো pure রাখো। যে function সাধারণ value নেয় (subtotal, সময়, নতুন user কিনা) আর একটা সিদ্ধান্ত ফেরত দেয়, network, ঘড়ি বা database না ছুঁয়ে, সেটা পড়া, test করা আর আবার ব্যবহার করা সহজ। Plumbing input জোগাড় করে, নিয়মকে ডাকে, আর উত্তর অনুযায়ী কাজ করে।
  4. এক ঘর, অনেকে ডাকে। Mobile app, website, admin tool আর রাতের script সবাই backend কে জিজ্ঞেস করে; backend জিজ্ঞেস করে নিয়মকে। Client একটা আনুমানিক অঙ্ক দেখাতে পারে, কিন্তু যে টাকা কাটা হবে সেটা আসে ওই এক ঘর থেকে (lesson 2)।
  5. নিয়মের কারণ থাকে। ভালো নিয়ম শুধু "না" বলে না, বলে কেন ("minimum order 200 টাকা"), যাতে client একটা কাজের message দেখাতে পারে আর support এর লোক বুঝিয়ে বলতে পারে।
আগে অনুমান করো

একজন নতুন user দুপুর 1টায় 400 টাকার খাবার order করলো, cash এ দেবে। নিয়ম: minimum 200; delivery 60, 500 থেকে free; নতুন user পায় 20% ছাড়, সর্বোচ্চ 100 টাকা; cash চলবে 3000 পর্যন্ত। সে কত দেবে?

ভেতরে কী হয়: একটা order, অনেকগুলো নিয়ম

ধাপে ধাপে দেখো

নিয়মের বইয়ের ভেতর দিয়ে একটা order

AppHTTP handlerRulesDatabase

ধাপ 1/5 App order পাঠায়

User যা বেছেছে app সেটা পাঠায়: item গুলো, payment এর উপায়। আনুমানিক total দেখাতে পারে, কিন্তু পাঠায় না।

items
biryani x2
payment
cash

মাথায় রাখার ছবি

তিনটা বৃত্ত আঁকো:

  • মাঝখানে: business এর নিয়ম। সাধারণ function আর type: quote(order), canCancel(order, now), Plan.noteLimit। কোনো HTTP না, কোনো SQL না।
  • তার চারপাশে: application, যে নিয়মের যা লাগে তা নিয়ে আসে, নিয়মকে ডাকে, আর ফলাফল save করে।
  • একদম বাইরে: HTTP, database, queue, email provider, ঘড়ি।

নির্ভরতা যায় ভেতরের দিকে: বাইরের জিনিস নিয়মের কথা জানে, নিয়ম বাইরের কিছুই জানে না। এজন্যই নিয়মের বইয়ে হাত না দিয়ে database বদলানো যায়, নতুন client যোগ করা যায়, বা প্রতিটা নিয়ম কয়েক millisecond এ test করা যায়। Module 5 এ এই কাঠামো ঠিকমতো বানাবো।

চেষ্টা করো: business logic নাকি plumbing?

প্রতিটা case চালিয়ে দেখো

এটা কি business এর নিয়ম?

if subtotal < 200 { refuse }
কাজ করে: Business logic। Company 200 বেছে নিয়েছে; কাল 150 করতেও পারে। Sales এর কারো এ নিয়ে মতামত থাকবেই।

গোড়া থেকে code

একটা food delivery দোকানের নিয়মগুলো একটা pure function এ, quote। এটা সাধারণ value নেয় (subtotal, ঘণ্টা, প্রথম order কিনা, payment এর উপায়) আর ফেরত দেয় হয় কত দিতে হবে, নয়তো order কেন ফেরত গেলো তার কারণ। HTTP নেই, database নেই, ঘড়ি নেই: ঘণ্টাটা যে ডাকে সে পাঠায়।

Program টা এর ভেতর দিয়ে সাতটা order চালায়, test case এর একটা table এর মতো।

package main

import "fmt"

type order struct {
	subtotal int    // taka, already worked out from the menu prices
	hour     int    // when it was placed, 0-23
	newUser  bool   // first order ever?
	payment  string // "card" or "cash"
}

// quote holds the shop's business rules, and nothing else: no HTTP, no database.
// It returns the amount to pay, or the reason the order can't be placed.
func quote(o order) (int, string) {
	if o.hour < 10 || o.hour >= 22 {
		return 0, "we deliver 10:00-22:00 only"
	}
	if o.subtotal < 200 {
		return 0, "minimum order is 200 taka"
	}
	if o.payment == "cash" && o.subtotal > 3000 {
		return 0, "cash on delivery only up to 3000"
	}
	delivery := 60
	if o.subtotal >= 500 {
		delivery = 0 // free delivery from 500
	}
	discount := 0
	if o.newUser {
		discount = min(o.subtotal*20/100, 100) // 20% off the first order, at most 100
	}
	return o.subtotal - discount + delivery, ""
}

func main() {
	cases := []struct {
		name string
		o    order
	}{
		{"small lunch", order{150, 13, false, "card"}},
		{"regular lunch", order{350, 13, false, "card"}},
		{"family dinner", order{600, 20, false, "cash"}},
		{"first order, big", order{600, 20, true, "card"}},
		{"first order, small", order{400, 13, true, "cash"}},
		{"office party", order{3500, 12, false, "cash"}},
		{"midnight snack", order{300, 23, false, "card"}},
	}
	fmt.Printf("%-19s %8s %5s %-4s %-5s %s\n", "order", "subtotal", "time", "new", "pay", "result")
	for _, c := range cases {
		total, reason := quote(c.o)
		result := fmt.Sprintf("pay %d", total)
		if reason != "" {
			result = "refused: " + reason
		}
		isNew := map[bool]string{true: "yes", false: "no"}[c.o.newUser]
		fmt.Printf("%-19s %8d %02d:00 %-4s %-5s %s\n", c.name, c.o.subtotal, c.o.hour, isNew, c.o.payment, result)
	}
}

Output (পাঁচটা language এই একই):

order               subtotal  time new  pay   result
small lunch              150 13:00 no   card  refused: minimum order is 200 taka
regular lunch            350 13:00 no   card  pay 410
family dinner            600 20:00 no   cash  pay 600
first order, big         600 20:00 yes  card  pay 500
first order, small       400 13:00 yes  cash  pay 380
office party            3500 12:00 no   cash  refused: cash on delivery only up to 3000
midnight snack           300 23:00 no   card  refused: we deliver 10:00-22:00 only

Output টা পড়ি:

  • small lunch / midnight snack / office party: প্রতিটা আলাদা আলাদা নিয়মে ফেরত গেছে, এমন কারণ সহ যা মানুষ পড়ে বুঝতে পারে। নিয়মগুলো জেনেবুঝে একটা ক্রমে check হয়: আগে দোকান খোলা কিনা, তারপর minimum, তারপর payment এর সীমা।
  • regular lunch: 350 + 60 delivery = 410।
  • family dinner: 600, delivery free কারণ subtotal অন্তত 500।
  • first order, big: 600 এর 20% হলো 120, সীমা 100, তাই 500; delivery free।
  • first order, small: 400 − 80 + 60 = 380, predict প্রশ্নের উত্তর।
  • খেয়াল করো কী লাগেনি: server, database বা আসল ঘড়ি। সাতটা পরিস্থিতি, কয়েক microsecond এ check হয়ে গেলো। নিয়ম pure রাখার লাভ এটাই।

আসল engineering

  • নিয়মগুলোকে একটা ঘর দাও। অনেক team business এর নিয়মের জন্য একটা domain বা core package রাখে, আর handler কে পাতলা রাখে: parse করো, নিয়মকে ডাকো, উত্তর দাও। (Module 5, lesson 6 আর 11।)
  • নিয়ম test করো table এর মতো। ঠিক উপরের output এর মতো: input দাও, প্রত্যাশিত উত্তর মেলাও, কয়েক ডজন সারি। Bug এর জন্য সবচেয়ে বেশি টাকা যায় নিয়মে, আর pure function test করা সবচেয়ে সস্তা।
  • সময়কে input বানাও। যে নিয়ম ভেতরে now() ডাকে তাকে test করা কঠিন ("রাত 9:59 এ কী হয়?")। quote এর মতো সময়টা বাইরে থেকে পাঠাও।
  • শুধু input না, ফলাফলও save করো। একটা order আসলে কত total, discount আর fee পেয়েছিল তা রাখো। নিয়ম বদলালে পুরনো order যেন সাথে বদলে না যায় (lesson 4)।
  • কিছু নিয়ম configuration হয়ে যায়। যে সীমা প্রতি সপ্তাহে বদলায় (500 থেকে delivery free), সেটা একটা settings table এ সরানো যায় যেখান থেকে নিয়ম পড়ে, তাহলে বদলাতে deploy লাগে না। নিয়ম থাকে code এ; সংখ্যাটা হয়ে যায় data।

যখন ভাঙে

প্রতিটা case চালিয়ে দেখো

একটা failure বাছো

app: 380 taka
website: 440 taka
একই cart
ভাঙে: প্রতিটা client নিজে দাম হিসাব করেছে। হিসাবটা backend এ সরাও; client একটা quote চাইবে আর যা আসে তাই দেখাবে।

পড়ার আগে ভাবো

Mobile team জিজ্ঞেস করলো: "Discount এর নিয়মের একটা copy কি app এ রাখতে পারি, যাতে server এর জন্য অপেক্ষা না করে সাথে সাথে total দেখানো যায়?" এটা কি চলবে?

উত্তর দেখো

হ্যাঁ, আনুমানিক হিসেবে। দ্রুত একটা total দেখানো user এর জন্য ভালো। কিন্তু app এর copy কখনো ঠিক করবে না কত টাকা কাটা হবে: backend নিজের নিয়ম দিয়ে আবার হিসাব করে, আর app তার আনুমানিক অঙ্কের জায়গায় backend এর উত্তর বসায়। আর copy গুলো যাতে আলাদা না হয়ে যায়, অনেক team app এ নিয়ম hard-code না করে backend থেকে নিয়মের বর্তমান value (বা একটা quote endpoint) পাঠায়।

নিজেকে check করো

1. কোনো code business logic কিনা, সেটা দ্রুত বোঝার উপায় কী?

উত্তর

Business এর কেউ (sales, finance, support, কোনো manager) এ নিয়ে মতামত দেবে বা বদলাতে চাইবে কি? দাম, সীমা, permission আর প্রতিশ্রুতি হলো business logic; parse করা, retry আর database access হলো plumbing।

2. নিজে database আর ঘড়ি পড়ে এমন function এর চেয়ে একটা pure quote(order) নিয়ে কাজ করা সহজ কেন?

উত্তর

সাধারণ value দিয়ে কয়েক microsecond এ test করা যায়, রাত 9:59 বা ঠিক 500 টাকার মতো সীমার পরিস্থিতি সহ, আর I/O সাথে না টেনেই যেকোনো client এর পথ থেকে ডাকা যায়।

3. নিয়ম দিয়ে আবার হিসাব করা গেলেও order কেন নিজে কত discount পেয়েছিল তা save করবে?

উত্তর

কারণ নিয়ম বদলাবে। Order টা অতীতের একটা fact; পরের মাসের নিয়ম দিয়ে আবার হিসাব করলে customer আসলে কত দিয়েছিল সেটাই বদলে যাবে।

4. App নিজের নিয়মের copy দিয়ে একটা আনুমানিক total দেখায়। Backend কে তবুও কী করতে হবে?

উত্তর

নিজের নিয়ম দিয়ে আবার total হিসাব করা, আর সেই টাকাই কাটা, client এর সংখ্যা পাত্তা না দিয়ে (lesson 2)।

তোমার challenge: শুক্রবারের একটা নিয়ম

Business একটা নিয়ম যোগ করলো: শুক্রবারে delivery free 300 টাকা থেকে, 500 এর বদলে।

  • সপ্তাহের দিনকে quote এর একটা input বানাও, ঘণ্টা যেভাবে আছে।
  • Table এ দুটো সারি যোগ করো: শুক্রবারে একটা 350 টাকার order, আর মঙ্গলবারে একই order।

চালানোর আগে দুটো total আন্দাজ করো।

সফল হয়েছো যখন শুক্রবারের order দেয় 350 আর মঙ্গলবারের order দেয় 410, আগের প্রতিটা সারি হুবহু আগের মতোই print হয়, আর quote তখনো নিজে ঘড়ি পড়ে না।

Mental model

Business logic হলো তোমার product এর নিয়মের বই: দাম, সীমা, permission আর প্রতিশ্রুতি। Plumbing হলো এর চারপাশে data নাড়াচাড়া করার বাকি সবকিছু। নিয়মগুলোকে backend এ একটা ঘর দাও, input এর সাধারণ function বানাও, আর প্রতিটা client আর script কে এর ভেতর দিয়ে যেতে দাও। Client আন্দাজ করতে পারে; সিদ্ধান্ত নেয় নিয়ম।

Station stamp

Chapter শেষ করো

পড়া শেষ হলে Complete চাপো, progress save হবে। XP শুধু Pro তে — Pro থাকলে এই chapter এ +10 XP পেতে।

Pro নিয়ে XP পাও →