RESTful API กับ Kotlin + Spring-Boot
พอดีมี product ตัวใหม่ที่ต้องมี backend ใหม่ ทางทีมก็เลยได้ลอง research รวมถึงทำ POC ด้วย Kotlin + Spring-Boot ใช้เวลาประมาณ 1 sprint (2 weeks) ลองอะไรแปลกๆใหม่ ได้เรียนรู้อะไรเยอะเลย ไปดูกันครับ…
ในบทความนี้ก็จะมีหัวข้อที่พูดถึงอยู่ประมาณนี้
- ทำไมถึงใช้ Kotlin ทำ Backend?
- Structure Project แบบไหนดี ?
- ORM ด้วย JPA กับ data class
- Review Code
- ทำ Testing ยังไง ?
- POC แล้วถ้าได้ไปต่อใน Production ควรทำอะไรต่อ ?
- Summary
ทำไมถึงใช้ Kotlin มาทำ Backend ด้วย Spring-Boot หล่ะ?
ถ้าเคยเขียน Java มาแล้วได้ลองไปศึกษา Kotlin เพิ่มเติมจะรู้ว่า ตัวภาษานั้นได้ถูกออกแบบมาเพื่อช่วยอำนวยความสะดวก แล้วก็ลด pattern ซ้ำซ้อน ยกตัวอย่างง่ายๆ อย่างเช่น การสร้าง model class ที่จะต้องมี getter กับ setter ใน Java เป็นสิ่งที่เรารู้สึกว่ามันทำเหมือนๆกัน
ในตัว Kotlin นั้นมี data class ให้เราใช้จะช่วย wrap พวกนี้ไว้ให้ ซึ่งลดปริมาณ code ลงไปได้ค่อนข้างเยอะ ก็ยังมีวิธีการ define variable ให้เรา เลือกใช้ระหว่าง var และ val จะช่วยเรื่องการจัดการพวก immutable ของ object และก็มี Nullable type ให้เราใช้อีก ดูเพิ่มเติมเกี่ยวกับ syntax น่าสนใจของ Kotlin
จริงๆ Spring เป็น web service framework ใช้ใน production กันมายาวนาน สิ่งที่มองว่ามันเป็นข้อดีของ Spring-Boot คือ standalone ไม่ต้องใส่ config มากมายเหมือนกับสมัยก่อนที่กว่าจะทำ web service ขึ้นมาตัวนึงช่างโหดร้ายกับเรา ดูเพิ่มเติมเกี่ยวกับ Features ของ Spring-Boot
Structure Project แบบไหนดี?
ตอนวางโครงทำ Project จริงๆ ค้นคว้าทำความเห็นที่ได้รับจากที่ปรึกษาภายในทีม ทำ research หลากหลายรูปแบบก็คิดว่าการทำแบบนี้มันน่าจะตอบโจทย์นี้ที่สุด ในแง่ของการเตรียมพร้อมจะถอดเป็น microservices ย่อยได้อีกด้วย
ถ้ามองแล้วเราจะไม่ใส่ business logic ลงไปใน controller ตรงๆ แล้วเราใช้ decorator pattern มาจัดการแทน เพราะเราจะสามารถ scope การทำ testing ได้ไปในตัว รวมถึงการทำหน้าที่เป็น wrapper ในส่วนนี้ไม่ให้ไปปนกับของอื่นๆใน controller ที่มีการจัดการส่วนอื่นที่ไม่ใช่ business logic จริงๆ เช่น การจัดการในส่วนของ request /response หรือ การจัดการ role & permission เป็นต้น
ORM ด้วย JPA กับ data class
JPA (Java Persistence API) เป็น ORM ที่ค่อนข้างคุ้นเคยกันสำหรับมาตรฐานกับ model class ซึ่งเราจะใช้ JPA เป็น repository ในการจัดการ mapping ระหว่าง layer ของ database กับ object ของเราแทน ซึ่ง JPA จะมี annotations กับ methods ตัวช่วยเทียบเท่ากับการเขียน Query แบบ CRUD พื้นฐานให้เลย
หน้าตาของ data class ก็จะประมาณ snippet ด้านล่าง สังเกตได้ว่าเรา define val ให้กับ id เพื่อให้เป็น immutable field ซึ่งไม่สามารถแก้ไขทับได้ และ avatar เองเป็น Nullable type ซึ่ง syntax มันค่อนข้างเคลียร์แล้วสำหรับการสร้าง model ขึ้นมา จะช่วยเรื่อง code awareness แล้วก็ลด bug ไปบ้าง
data class User(
val id: Long,
var name: String,
var avatar: String? = null) {
}
ไปดูหน้าตา data class ไฟล์จริง ก็จะมี annotation ที่ define ว่าอันนี้เป็น entity ไป mapping กับ table ที่ชื่อนี้ใน database ของเรา แล้วก็ใช้ annotation ตัวช่วยอื่นๆตามแต่ละ field ได้เลย อย่างเช่น id ที่เป็น auto-generated value
วิธีนำ data class ไปใช้ใน repository ที่ให้สังเกตุที่ด้านหลังจาก implements JpaRepository เราใส่ที่เป็น data class ของเราไว้แทนที่ <Type, Long> ลงไปเลย ก็ใน CompanyController นี้เปิด findByName เพิ่มขึ้นมาจาก default ของ JpaRepository หรือจะเขียนอะไรพิเศษไปกว่านี้ก็ได้นะ
Review Code
Back to basic กับ OOP ด้วยการสร้าง interface และสร้าง abstract class ชื่อ BaseHandler สามารถรับ Type อะไรก็ได้มาจาก JpaRepository และ Entity ตัวเดียวกัน ขึ้นมาไว้ใช้ extends ในการทำ CRUD ด้วยพื้นฐานให้กับ model ทุกตัว ไม่ต้องแปลกใจที่ชื่อ function ดูคุ้นๆ ได้แรงบันดาลใจมาก RubyOnRails ครับ
ให้ CompanyHandler มา extends BaseHandler แล้วใส่ CompanyRepository กับ Company ลงไป ถ้ามีการทำ business logic ที่พิเศษไปกว่านี้ก็สามารถทำเพิ่มได้เลยนอกเหนือจาก CRUD พื้นฐาน
มาในส่วนของ Controller ก็เหมือนทั่วไปคือการใส่ annotation ในการ mapping request รวมไปถึงเอา Handler มาใช้งาน และการสร้าง view เพื่อ transform data class
Testing ยังไง?
ก่อนจะเข้าการ Testing ต้องบอกไว้ก่อนว่าใน Spring-Boot มีตัว library ที่ชื่อว่า H2 เป็นช่วยให้เราสามารถสร้าง database in memory ไว้ได้เลยสะดวกที่จะทำการ read/write ไปตรงๆได้เลย
- Unit testing จะโฟกัสที่ Handlerโดยที่เราสามารถทำ เป็น functional step ได้โดยการเรียกดูว่าอะไรถูก invoke ภายในบ้าง เป็น draft คร่าวๆ ถ้าเกิดว่า logic มัน complex ก็ค่อยเขียน step เพิ่มเติมเข้าไป
- Integration test ใส่ input ดู output ทำได้หลายแบบอยู่ที่มุมมองว่าจะทำที่ Controller เลยมั้ย หรือว่าจะแค่ที่ Handler ในส่วนนี้คือได้มีการ mockMvc ขึ้นมาแล้วยิงไป testing ตาม endpoint ของเราเองโดยที่เรามาดูผลลัพธ์จาก response
POC แล้วถ้าได้ไปต่อใน Production ควรทำอะไรต่อ?
- Package manager สำหรับ Spring Boot ก็ต้องมารู้จัก Gradle กับ Maven เพื่อใช้ในการจัดการ package ต่างๆ แล้วแต่ความชอบบางคนชอบ xml บางคนชอบแบบ script แล้วแต่ความถนัด
- ดูเพิ่มเติมเกี่ยวกับ ORM Relationships เช่น one-to-many, has-many รวมถึงการเขียน Query เองนอกเหนือจากการใช้งาน JPA
- ทำ Global error handling เป็น Format ที่ดูแล้ว รู้เลยจาก Stacktrace
- Migration tool กับ Database ก็จะมี library หลายตัว เช่น liquibase, flyway
- ใช้ DTO ในการคุม interface ของ input/output รวมถึงการแปลงเป็น entity เพื่อใช้งานกับ ORM ของเรา
- คิดถึงเรื่อง infra ตอนทำ deployment ว่าจะ Set .Dockerfile เพื่อ build ให้เป็น .JAR แล้วใส่ลงไปใน image เพื่อทำ testing กับ build ยังไง
Summary
หลังจากได้ใช้เวลาสองสัปดาห์เต็มๆอยู่ ถ้ามองในแง่ของการ Learning curve คิดว่าสอบผ่านนะ Java practice เป็นอะไรที่คิดว่าค่อนข้าง mendatory สำหรับ OOP ความรู้สึกคือ Kotlin ทำให้ Java ง่ายขึ้น ได้เห็น Design pattern ที่สวยๆ แต่การ setup มันก็ยังดูค่อนข้าง bulky ถึงแม้ว่าจะตัดบางส่วนออกไปแล้วก็ตาม
การทำ POC นี้มีจุดประสงค์หลักคือ ดูว่ามันตอบโจทย์กับสิ่งที่เป็น eco-system ในการ implement และ maintain รวมถึงดูว่ามันตอบโจทย์ด้าน business ขนาดไหนด้วย ทำได้ smooth ขนาดไหนในการให้ developer เข้ามาใส่ stack ใหม่ๆ ของแถมคือได้รู้อะไรในสิ่งที่ตอนแรกไม่ได้ทำเลยกับค่าย JVM หรือ Syntax ความสามารถใหม่ๆใน Kotlin ก็ยังมีเรื่องให้เรียนรู้ต่อไปอีกมากมาย
ไว้เจอกันใหม่ครับ