- การตั้งชื่อและการทำโมเดลของทรัพยากร API เป็นส่วนที่ยากและสำคัญที่สุดในการออกแบบ API ทรัพยากรเหล่านี้ประกอบขึ้นเป็น mental model ของผู้ใช้ต่อวิธีการทำงานและความสามารถของผลิตภัณฑ์
- ทีม Increase ใช้หลักการ "ไม่มีการทำ abstraction" เพื่อช่วยกำหนดการออกแบบ API
แนวทางการออกแบบ API ของ Stripe
- สมาชิกจำนวนมากของทีม Stripe เคยทำงานที่ Stripe มาก่อน และให้ความสำคัญกับคุณค่าของการออกแบบ API ที่ทำให้ Stripe ประสบความสำเร็จ
- Stripe โดดเด่นในการสกัดฟังก์ชันหลักของโดเมนที่ซับซ้อนออกมาเป็น abstraction ที่ผู้ใช้เข้าใจและใช้งานได้ง่าย (เช่น
PaymentIntent ที่ทำ abstraction ความแตกต่างระหว่าง Visa กับ Mastercard)
- ผู้ใช้ส่วนใหญ่ของ Stripe คือสตาร์ทอัพระยะเริ่มต้นที่พัฒนาผลิตภัณฑ์ที่ไม่เกี่ยวกับการชำระเงิน จึงไม่จำเป็นต้องรู้รายละเอียดของบัตรเครดิต พวกเขาต้องการผสาน Stripe ได้อย่างรวดเร็วและมุ่งเน้นไปที่การพัฒนาผลิตภัณฑ์
ผู้ใช้ของ Increase และหลักการออกแบบ API
- ในทางกลับกัน ผู้ใช้ของ Increase มีความรู้เชิงลึกเกี่ยวกับเครือข่ายการชำระเงิน และเลือก Increase เพราะต้องการการเชื่อมต่อกับเครือข่ายโดยตรงและการผสานรวมในระดับลึก
- พวกเขาต้องการรู้เวลาที่หน้าต่าง FedACH ปิดและเวลาที่จะเกิดการโอนอย่างแม่นยำ และเข้าใจว่าการตั้งค่า SEC code ที่ต่างกันในการโอน ACH อาจส่งผลต่อช่วงเวลาของการตีกลับ
- ความพยายามที่จะซ่อนความซับซ้อนพื้นฐานของเครือข่ายเหล่านี้ไม่ได้ทำให้ชีวิตง่ายขึ้น มีแต่จะทำให้ผู้ใช้หงุดหงิด
- จากการพูดคุยกับผู้ใช้กลุ่มแรก จึงได้หลักการ "ไม่มีการทำ abstraction" และนำมาใช้กับการออกแบบ API
หลักการ "ไม่มีการทำ abstraction" ส่งผลต่อการออกแบบ API อย่างไร
- ใช้คำศัพท์ที่มีการใช้งานจริง: แทนที่จะสร้างชื่อเฉพาะขึ้นมาสำหรับทรัพยากรและพร็อพเพอร์ตีของ API ก็ใช้คำศัพท์ของเครือข่ายต้นทางโดยตรง (เช่น พารามิเตอร์ของการโอน ACH ใช้ชื่อฟิลด์ตามข้อกำหนดของ Nacha)
- ความไม่เปลี่ยนแปลง: ใช้เหตุการณ์จริงเป็นโมเดลเพื่อให้ทรัพยากร API จำนวนมากขึ้นเป็น immutable การจัดกลุ่มคลัสเตอร์ของทรัพยากรที่ immutable ให้เป็น state machine แบบ "วัตถุวงจรชีวิต" เป็นวิธีที่ได้ผล (เช่น ฟิลด์
status ของอ็อบเจ็กต์ ach_transfer และอ็อบเจ็กต์ย่อยแบบ immutable หลายตัวที่ถูกสร้างขึ้นตามวงจรชีวิตของการโอน)
- แยกทรัพยากรตามกรณีใช้งาน: หากชุดการกระทำที่ผู้ใช้ทำได้แตกต่างกันมากตามอินสแตนซ์ของทรัพยากร ก็จะแยกออกเป็นหลายทรัพยากร (เช่น การโอน ACH ขาออกกับการโอน ACH ขาเข้าแยกเป็นทรัพยากรคนละตัว)
การยึดถือแนวทางนี้ของทีมวิศวกรรม
- ทีมวิศวกรรมให้คำมั่นว่าจะยึดถือแนวทางนี้
- เมื่อออกแบบ API ที่ซับซ้อนต่อเนื่องหลายปี จำเป็นต้องตัดสินใจแบบเพิ่มทีละน้อยอยู่เสมอ การให้คำมั่นล่วงหน้าว่าจะยึดหลักการพื้นฐานช่วยลดภาระทางความคิดในการตัดสินใจเหล่านั้น
- ตัวอย่างเช่น ในกรณีของฟิลด์ Input Message Accountability Data ที่จำเป็นเมื่อโอนเงินไปยังธนาคารกลางสหรัฐฯ หากเป็น API ที่มี abstraction สูง ก็ต้องมาคิดว่าจะตั้งชื่อฟิลด์นี้อย่างไรให้ "เป็นมิตรกับผู้ใช้" แต่ที่ Increase วิศวกรสามารถตั้งชื่อฟิลด์เป็น
input_message_accountability_data แล้วไปต่อได้เลย
ความเห็นของ GN⁺
- ระดับของ abstraction ใน API อาจแตกต่างกันไปตามระดับประสบการณ์ของนักพัฒนากับโดเมนของผลิตภัณฑ์นั้น และพลังงานที่พร้อมจะทุ่มให้กับการผสานรวม ดังนั้นเมื่อต้องออกแบบ API จึงสำคัญที่จะต้องพิจารณาระดับ abstraction ที่เหมาะสมสำหรับนักพัฒนาที่จะนำไปผสานรวม
- หากจะสร้าง API ที่มี abstraction สูง ก็ควรคิดให้รอบคอบก่อนเพิ่มฟีเจอร์ใหม่ ในทางกลับกัน หากสร้าง API ที่มี abstraction ต่ำ ก็ต้องยึดแนวทางนั้นไว้และต้านทานความยั่วยวนที่จะเพิ่ม abstraction
- การใช้คำศัพท์ของเครือข่ายหรือโปรโตคอลต้นทางโดยตรงอาจช่วยให้นักพัฒนาเข้าใจ underlying system ได้ดีขึ้น แต่ในทางกลับกันก็อาจเป็นกำแพงสำหรับนักพัฒนาที่เพิ่งเริ่มต้นเช่นกัน ดังนั้นการใส่คำอธิบายหรือเอกสารประกอบให้ดีจึงน่าจะสำคัญ
- การใช้ immutable object ในการออกแบบ API อาจมีประสิทธิภาพในการรักษาความสอดคล้องของข้อมูลและป้องกัน side-effect แต่ในอีกด้านหนึ่งก็อาจไม่สะดวกเมื่อจำเป็นต้องอัปเดตข้อมูล จึงต้องพิจารณา trade-off ให้ดี
- การแยกทรัพยากรตามกรณีใช้งานอาจเพิ่มความซับซ้อนของ API แต่ในระยะยาวอาจช่วยเพิ่มความคาดเดาได้ อย่างไรก็ตาม หากแยกย่อยมากเกินไปก็อาจทำให้ใช้งานยากลง จึงสำคัญที่จะหาระดับที่เหมาะสม
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ควรมีทั้ง API ที่ใช้การนามธรรมระดับต่ำและ API ที่ใช้การนามธรรมระดับสูง
ชอบส่วนที่อธิบายว่าทำไม Increase ถึงเลือกแนวทางที่ต่างออกไป
ความสามารถที่แท้จริงของ Stripe คือการเข้าใจลูกค้าและมอบความเรียบง่ายที่ลูกค้าต้องการ
คล้ายกับแพตเทิร์นการออกแบบ "ภาษาสากล (Ubiquitous Language)" ใน Domain-Driven Design ที่ใช้คำเดียวกับคำจริงที่ผู้เชี่ยวชาญโดเมนใช้ในการทำงานจริง
ควรใช้ภาษาที่ผู้เชี่ยวชาญโดเมนเข้าใจได้
หากไม่มีนามธรรมแบบ POSIX แอปพลิเคชันก็ต้องเขียนอะแดปเตอร์สำหรับทุกไฟล์ซิสเต็มที่รองรับ
มีการบอกว่าโครงสร้างบางส่วนของ API ถูกสร้างแบบ 1:1 ตามสเปกที่ถูกกำหนดจากภายนอก
สิ่งหนึ่งที่โมเดลให้ดูสะอาดได้ยากใน Payment API คือ ระบบการชำระเงินแสดงบทบาทของผู้จ่ายและผู้รับเงินต่างกันเมื่อมีการคืนเงิน