Driving Through Domains: A Deep Dive into Uber/Grab’s System with Domain Driven Design (Ver. Thai)
สวัสดีครับ ทุกคนเคยสงสัยกันมั้ย Application Uber หรือ Grab Delivery มีอะไรเกิดขึ้นเบื้องหลังบ้าง ตั้งแต่คุณกดจองคนขับไปจนถึงการให้คะแนนระบบ ในบทความนี้ผมจะพาทุกคนมาวิเคราะห์การทำงานเหล่านั้นกันโดยจะทำ Concept ของ Domain Driven Design มาใช้กันครับ
User Story
User Story คือการแปลงเรื่องราวของผู้ใช้ออกมาเป็น requirement เพื่อง่ายสำหรับการอ่านและทำความเข้าใจ ตัวอย่าง As a customer, I need … so that…
Customer
As a customer, I need a map interface so that I can pin a pick-up point.
As a customer, I need to know delivery options so that I can pick the one that works for me.
As a customer, I need cleary price for each options so that I can know how much cost I need to pay.
As a customer, I need to choose how to pay so that I can use my preferred payment method.
As a customer, I need to request a ride so that I can be matched with a driver.
As a customer, I need a rating system so I that can provide feedback on both the system and the driver.
Driver
As a driver, I need to be available in the system so that I can get matched with a customer.
As a driver, I need an information on the distance between myself, the customer, and the destination so that I can plan my route effectively.
As a driver, I need a way to accept customer requests so that I can respond promptly to ride requests.
As a driver, I need a payment system so that I can receive income for providing rides.
As a driver, I need a feedback system so that I can receive ratings and comments from customers.
เอาจากที่เราได้ User Story ของทั้ง 2 stakeholders แล้ว เราลองย่อย text เป็น Sticky Note ง่าย ๆ เพื่อ visualize ออกมาเป็นข้อ ๆ กันครับ
Domain Event
Domain Event คือสิ่งที่เกิดขึ้นในเหตุการณ์นั้น ๆ ภายใน Domain มักจะเขียนในรูปแบบของ past tense หรือสิ่งที่เกิดขึ้นไปแล้ว
หลังจากที่เราได้ User Journey ใน section ก่อนหน้ามาแล้ว เราจะลองนำมาเปลี่ยนเป็น Domain Event เพื่ออธิบายเหตุการณ์ที่เกิดขึ้นกันครับ
เรามาลองไล่ทีละตัวกันครับว่าจากการทำ User Journey สามารถเปลี่ยนไปเป็น Domain Event อะไรได้บ้าง
.
เริ่มจาก Customer เลือกจุด pickup และ destination (Route Selected) จากนั้นระบบจะทำการคำนวณระยะทางระหว่างจุดหมายและปลายทาง (Trip Evaluated) และอาจจะจะมี hidden event ข้างหลังเป็นการคำนวณราคาจาก Type ของรถที่เราเลือก เช่น Motocycle, Car, Van, etc… (Vehicle Consumption Calculated)
.
ในการเรียกรถนั้นจะเกิดการ Request, Get request และ Confirmed ดังนั้น ในด้าน Event เราจะใช้คำว่า “Offer” เนื่องจากเป็นการยื่นการหา Driver จาก Customer (Offer Created) และระบบเป็นตัวยื่นข้อเสนอให้ Driver หลาย ๆ คน (Nearby Driver Matched/Notified) และสามารถตอบรับข้อเสนอนั้นได้ (Offer Accepted) และระบบจะทำการ confirmed trip ระหว่าง customer และ driver (Offer Confirmed)
.
หลังจากที่เกิด event “Offer Confirmed” แล้ว ระบบน่าจะทำการจอง Customer Credit (Credit Reserved) ไว้เพื่อที่จะ release ให้กับ driver ในอนาคต
.
ในการที่จะทำส่งผู้โดยสารให้ถึงที่ ระบบจะต้องทำการค้นหาเส้นทางให้ Driver (Route Mapped) และจบการเดินทางเมื่อถึงจุดหมาย (Trip Finished)
.
เมื่อเกิด Event “Trip Finished” ระบบจะต้อง Release credit ที่ถูก reserve ไว้ (Credit Charged) และอาจจะมี hidden event สำหรับหักค่าบริการก่อนจะนำจ่ายให้ Driver (Fare Deducted) จากนั้นเงินจะถูกโอนให้ Driver (Payment Transferred) และจบการนำส่งเงิน (Payment Completed)
.
หลังจากจบการนำส่งผู้โดยสาร จะมีการให้ customer ให้ดาวและคำแนะนำกับระบบและ Driver (Driver/Service Rated) สำหรับการนำไปพัฒนาต่อในอนาคต
Role
Role ในทาง Event Storming คือบุคคลที่ทำให้เกิด Event น้ัน ๆ
จากนั้นให้ลองเพิ่ม Actor เข้าไปใน Domain Event ของเราเพื่อที่เราจะได้รู้ว่าใครเป็นคนทำให้เกิด Event ไหนครับ
Bounded Context
Bounded Context คือการกำหนดขอบเขตการทำงานของ Domain
ส่วนสำคัญต่อไปคือการแบ่ง Bounded Context ให้ระบบเรากันครับ ซึ่งจะได้ตามรูปนี้ข้างล่างนี้ครับ
Booking Context
ใน context นี้เราจะ focus ไปที่การจองเป็นหลัก ตั้งแต่ customer เลือกจุดหมายปลายถึงไปจนถึง driver กด accept offer ของคนเรียกครับ
Trip Context
ในส่วนนี้ผมตั้งใจวางทุกอย่างมีการคิดคำนวณเกี่ยวกับการเดินทางทั้งหมดมาไว้ที่นี่ จะเห็นว่า มีการคำนวณค่าใช้จ่ายในการเดินทางหรือการค้นเส้นทางเป็นต้น
Matching Pool Context
ใน context นี้เกิดขึ้นจากระบบที่ต้องมีการคำนวณสำหรับการจับคู่ระหว่าง Driver ใกล้เคียงกับ Customer ที่กำลังหารถโดยสาร และแจ้งเตือนให้ Driver
Payment Context
context นี้ค่อนข้างตรงตัวครับ มีหน้าที่จัดการทุกอย่างเกี่ยวกับเรื่องเงินตั้งแต่การ reserve เงินของ customer ไปจนถึงแจกจ่ายเงินให้ Driver เพิ่มเติมคือ ผมแอบใส่ note เพิ่มเติมลงไปคือ
สีม่วงคือ policy เพื่อเป็น trigger point ว่า event ต่อไปจะเกิดขึ้นถ้าเข้าเงื่อนไขอะไร
สีชมพูคือ External System แน่นอนว่าเราคงจะไม่ทำระบบ payment เพื่อเพิ่มความซับซ้อนให้ระบบเรา ดังนั้นเราจะเลือกใช้ Payment Gateway ที่มีอยู่แล้วแทนครับ
Rating Context
สุดท้ายคือการให้คะแนน และแน่นอนครับ scope การทำงานของมันค่อนข้างตรงตัวคือการให้ rating ครับ
Domain, Subdomain, and Core domain
Core Domain คือสิ่งที่มีค่าที่สุดในทาง Business
Supporting Domain คือส่วนสนับสนุนช่วยให้ core domain ดำเนินต่อไปได้ แต่ไม่ได้มีความสำคัญเท่า
Generic Domain คือระบบมาตรฐานที่สามารถหาซื้อได้ทั่วไปและนำมาประยุกต์ใช้สำหรับระบบเราได้
หลังจากที่เราได้แบ่ง Bounded Context กันเสร็จแล้ว เราก็จะมาจัดแบ่งประเภทของ context เพื่อที่จะเรียงลำดับความสำคัญและเลือกวิธีที่จะ attack ปัญหาเหล่านี้ได้เหมาะสมที่สุด
เพื่อให้ง่ายต่อความเข้าใจ ผมจะแบ่งเป็น 3 สี
- สีแดง = Core Domain
- สีเหลือง = Supporting Domain
- สีเทา = Generic Domain
เริ่มจาก Core Domain เหตุผลที่ผมให้ Trip Context และ Matching Pool Context เป็น Core Domain เพราะว่าสิ่งที่เราจะทำให้ Product เรา Unique กว่าคนอื่นนั้น เราจะต้องมีระบบการ Matching ระหว่าง customer และ driver ที่แม่นยำมากกว่าทั้งในด้านความรวมเร็วและความสมเหตุสมผล และในการคำนวณค่าใช้จ่ายในการเดินทางและแผนที่นั้น เราจะ optimized ให้ที่สุดเพื่อที่จะทำกำไรให้ได้มากที่สุดต่อรอบการเดินทาง ด้วยเหตุผลที่ผมกล่าวมานั้น จึงคิดว่า 2 สิ่งนี้เป็นส่วนที่สำคัญที่สุดในระบบในด้าน Business
.
ต่อมาคือ Supporting Domain เนื่องจากว่าการเดินทางจะเกิดขึ้นไม่ได้ถ้าไม่มีการ booking ก่อน ดังนั้น Booking Context จะช่วยเสริมทำให้ Core Domain ของเรานั้นทำงานได้แต่ไม่ได้มีส่วนสำคัญมากเท่ากับ Core Domain หลักของเรา ฉะนั้นเราอาจจะไม่ลง resourse หลักของเราไปที่ส่วนนี้ แต่ว่าในอนาคต ถ้าสิ่งนี้มี business value มากพอ เราอาจจะ promote มันขึ้นไปเป็น Core Domain ก็ได้เหมือนกันครับ
.
สุดท้ายคือ Generic Domain คือสิ่งที่เราสามารถใช้ระบบที่มีอยู่แล้วได้ เช่น ระบบ Payment ซึ่งผมเชื่อว่ามี Payment หลายเจ้ามากที่ทำระบบรองรับไว้แล้ว ดังนั้นก็อาจจะเป็นการดีที่เราไปใช้สิ่งที่ถูกพัฒนาไว้แล้ว และไปให้ความสำคัญกับสิ่งที่ Business ให้ value มากกว่า
ปล. ถ้าหาผู้อ่านมีไอเดียหรือแนวคิดที่ต่างจากผม ผมรับฟังข้อติชมจากทุกท่านครับ ถ้ามีข้อมูลส่วนไหนผิดถูกหรือแก้ไขตรงไหน สามารถ comment ไว้คุยกันได้เลยครับ
ขอบคุณครับ