[API Design] Phần 2 - Design API đơn giản (1)
Mọi người đều mong đợi các API có thể sử dụng được. Họ mong đợi các API có các thiết kế đơn giản và quen thuộc, các tương tác đơn giản, các đường dẫn đơn giản.
1. Thiết kế các biểu diễn đơn giản
Hãy thử nhìn vào ví dụ dưới đây, hình vẽ mô tả 2 thiết bị xem giờ là 24h Alarm Clock và WUM 3000 (Wake Up Machine 3000):
Cả 2 thiết bị đều có thể xem giờ, và đặt báo thức. Và cả 2 thiết bị này đều không để lộ sự phức tạp vận hành bên trong. Tuy nhiên, cách biểu diễn của chúng lại khác nhau, và điều này ảnh hưởng rõ rệt đến khả năng sử dụng của người dùng.
24h Alarm Clock hoàn toàn dễ sử dụng hơn vì người dùng có thể hiểu được thiết bị này là gì, trạng thái hiện tại là gì và sử dụng chúng như thế nào bằng cách đọc các thông số, các nhãn ở trên thiết bị.
Ngược lại, WUM 3000 khó sử dụng hơn nhiều do các nhãn ở trên thiết bị sử dụng các từ ngữ khó hiểu và dễ gây nhầm lẫn, các con số trên màn hình cũng gây khó khăn cho người dùng.
Qua ví dụ trên ta có thể thấy, việc đưa ra các thể hiện dễ hiểu, đơn giản cho thiết bị là rất cần thiết để người dùng có thể sử dụng chúng dễ dàng. Điều này cũng hoàn toàn tương tự đối với một API. Việc bạn lựa chọn tên, format cho dữ liệu có thể giúp nâng cao hoặc suy yếu đi khả năng sử dụng của API.
Hãy cùng nhau khám phá các cách để tạo ra những API có thể hiện đơn giản và dễ sử dụng.
1.1. Chọn tên rõ ràng
Giả sử các tài khoản của một ngân hàng có tính năng bảo vệ thấu chi. Nếu tính năng này được kích hoạt, ngân hàng sẽ không áp dụng bất kỳ khoản phí nào trong trường hợp khách hàng rút quá số dư khả dụng.
Nhìn vào tài liệu mô tả API ở hình dưới:
Ý tưởng đầu tiên là sử dụng một cái tên bkAccOverProtFtActBln để biểu diễn cho tính năng bảo vệ thấu chi. Property này có giá trị true khi tính năng được bật, và ngược lại, giá trị false khi tính năng bị tắt.
Tuy nhiên, việc sử dụng một cái tên như vậy không thực sự thân thiện với người dùng. Hãy cùng nhau đi qua các bước phân tích và cải tiến dưới đây:
Việc sử dụng các từ viết tắt thường xuyên (đặc biệt đối với các từ không phổ biến) không phải một ý tưởng tốt vì nó dẫn đến sự khó hiểu cho người sử dụng. (Tất nhiên ta vẫn có thể sử dụng từ viết tắt đối với các từ thực sự phổ biến, ví dụ min, max (minimum, maximum).
Ok, cái tên này đã mô tả và dễ hiểu đối với người sử dụng. Nó đủ clear để người dùng hiểu được ý nghĩa, nhưng nó thực sự dài, để xem có thể rút gọn được không. Hậu tố bln chỉ ra biến này có type là boolean. Tôi biết một số coding convention sử dụng các suffix như vậy để giải thích về mặt technical cho các class, property… Nhưng nếu bạn đứng từ góc độ người sử dụng API, các hậu tố này có thực sự quan trọng với họ hay không? Không hẳn. Để sử dụng được API, người sử dụng sẽ phải đọc cả API Document, và trong API Document đã mô tả rõ ràng trường này là boolean rồi. Đồng thời khi test API, người sử dụng đã có thể biết ngay được trường này là boolean vì nó trả ra 2 giá trị là true / false. Do đó, chúng ta có thể thực hiện bỏ hậu tố bln đi. Property lúc này trở thành: bankAccountOverdraftProtectionFeatureActive
Ta đã biết property này có type là boolean. Vậy hậu tố Active đứng đây mang ý nghĩa gì? Nó hoàn toàn không mang lại thông tin gì có giá trị. Do đó chúng ta cũng có thể loại bỏ hậu tốt Active. Property lúc này trở thành: bankAccountOverdraftProtectionFeature
Tiếp tục nói đến giá trị của thông tin, liệu từ Feature có mang lại thông tin gì không? Không thực sự. Từ mặt chức năng, nó chỉ nói lên đây là một tính năng của tài khoản ngân hàng (Và điều này đã được giải thích trong tài liệu). Do đó chúng ta cũng có thể rút gọn từ Feature. Property lúc này trở thành: bankAccountOverdraftProtection
Và cuối cùng, do property này thuộc vào một đối tượng BankAccount. Do đó chúng ta hoàn toàn có thể bỏ prefix bankAccount mà không làm mất đi ngữ nghĩa của nó. Property lúc này trở thành: overdraftProtection
TIP: Tôi recommend bạn không nên sử dụng quá 3 từ để đặt tên cho 1 đối tượng, bất kể mục đích của đối tượng đó là gì.
Về cơ bản, khi đặt tên cho một đối tượng, chúng ta cần sử dụng từ ngữ một cách khôn khéo và thống nhất, đồng thời tận dụng ngữ cảnh xung quanh để chọn lựa một cái tên đủ ngắn gọn và vừa dễ hiểu.
1.2. Chọn data type và format dễ sử dụng
Có thể chúng ta sẽ đặt câu hỏi tại sao API lại cần phải có data type và format dễ sử dụng, trong khi các API đó được sử dụng bởi phần mềm, và phần mềm hoàn toàn có thể thông dịch các format đó trong code?
Nhưng hãy nhớ rằng, API cũng là 1 giao diện người dùng. Các developer không chỉ dựa vào tên, mà còn dựa vào data type và format của dữ liệu để hiểu được cách sử dụng API. Các developer thường phân tích API dựa trên các ví dụ mẫu, thử call một vài API thủ công, phân tích response trả về…. Và để làm được tất cả điều đó, họ cần hiểu dữ liệu của API. Vì vậy, cũng giống như việc đặt tên, cách thức biểu diễn dữ liệu cũng cần được rõ ràng và dễ hiểu.
Hãy cùng xem ví dụ sau đây:
Hãy nhìn vào 2 property là balanceDate (cũng như creationDate), dựa vào suffix Date và giá trị 1534960860, những developer có kinh nghiệm sẽ nhận ra ngay giá trị này là UNIX timestamp.
Nhưng liệu họ có thể hiểu được giá trị này chỉ bằng cách nhìn vào chúng không? Tôi dám cá là không. Thay vào đó, thể hiện theo chuẩn ISO 8601, giá trị balanceDate = "2018-08-22T18:01:00z” rõ ràng và dễ hiểu hơn rất nhiều.
Thêm nữa, thuộc tính type có giá trị số (type=1), đây là giá trị mang lại sự khó hiểu. Thay vào đó, có thể sử dụng dữ liệu string (type=checking) sẽ rõ ràng và dễ hiểu hơn. Việc sử dụng một giá trị số trong trường hợp này là một ý tưởng tồi vì chúng không thân thiện với người dùng, các developer sẽ phải liên tục tham khảo document để hiểu được ý nghĩa của giá trị số đó.
Cuối cùng, có 1 trick nhỏ, nếu Account Number có giá trị string với các số 0 ở đầu (thay vì giá trị số) có thể giúp người dùng dễ sử dụng và tránh được sự cố khi làm việc với số.
Chốt lại, khi lựa chọn data type và format, bạn cần hướng đến sự thân thiện với người dùng. Khi sử dụng các format phức tạp, cố gắng cung cấp đủ thông tin. Và nếu có thể, cố gắng làm dữ liệu có thể được hiểu mà không cần quan tâm đến ngữ cảnh của chúng.
1.3. Chọn data sẵn sàng để sử dụng
Hãy cùng xem ví dụ dưới đây:
Ở phần trước chúng ta đã biết, sẽ tốt hơn nếu trường type có dạng string (type = “checking”). Nhưng nếu như, vì một vài lý do nào đó, chúng ta bắt buộc phải sử dụng dữ liệu dạng số (type = 1), chúng ta có thể thêm 1 trường bổ sung typeName (typeName = “checking”) để giải thích ý nghĩa cho giá trị số tương ứng.
Bởi vì số dư tài khoản là 1 giá trị số, người dùng khó có thể biết được đơn vị của số dư này là đô-la hay VND, hay một đơn vị tiền tệ nào đó khác. Lúc này, chúng ta nên thêm thông tin currency = “USD” để thể hiện rõ đơn vị tiền tệ của tài khoản.
Hãy giả sử, tính năng bảo vệ thấu chi này có 1 giới hạn: chủ tài khoản khi chi tiêu vượt số dư hiện tại 1 mức nhất định, phí sẽ được áp dụng. Ví dụ, giới hạn overdraftLimit = 100$, nếu số dư tài khoản còn 500$, thì chủ tài khoản có thể tiêu xài nhiều nhất là 500$ + 100$ = 600$ để không bị trừ phí. Khi đó, chúng ta có thể cung cấp 1 property safeToSpend = 600$, nhờ đó, người dùng có thể sử dụng luôn mà không cần biết công thức để tính toán lại giá trị này.
Sẽ là 1 ý tưởng tốt khi thay thế những dữ liệu cơ bản được lưu trữ trong CSDL bằng những data mang tính thông tin hơn. Ví dụ, trường creationDate sẽ không thực sự sẵn sàng để sử dụng trong trường hợp khách hàng chỉ muốn biết tài khoản này đã được mở mấy năm. Do đó ta có thể sử dụng trường yearsOpen thay vì trường creationDate.
Khi sử dụng REST API, mỗi resource được định danh bởi 1 path duy nhất. Cụ thể ở ví dụ này sẽ là /accounts/{accountId}. Nó có thể là 1 UUID như là 473e3283-a3b3-4941-aa48-d8163ead9ffc. Đây là 1 cách hiệu quả để đảm bảo path là unique, nhưng nhìn vào path, người dùng không thể xác định được đây là account nào. Thay vào đó, hãy nghĩ đến việc sử dụng /accounts/0001234567 vừa vẫn đảm bảo unique và vừa mang ý nghĩa rõ ràng.
1.4. Kết luận
Như bạn đã thấy, khi bạn biết bạn cần quan tâm đến khía cạnh nào, thì việc thiết kế các biểu diễn đơn giản thì tương đối giản đơn phải không. Hãy lựa chọn những cái tên đơn giản, các data type và format dễ hiểu, dễ sử dụng và sẵn sàng để sử dụng (cho cả người dùng và chương trình).
Ở phần tiếp theo, mình sẽ tiếp tục trình bày các khía cạnh khác của việc thiết kế API đơn giản.
Subscribe to my newsletter
Read articles from Sơn Phạm Quang directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Sơn Phạm Quang
Sơn Phạm Quang
Experienced Software Engineer Love, want to learn and share about Data Structures & Algorithm, Design Pattern, Optimize Performance... Strong passion for exploration and travel