# API Design Quiz (VI) Old Quiz from #1 -> 27 ### Q1 Phát biểu nào dưới đây là **ĐÚNG** khi nói về **common model** được định nghĩa trong thư mục gốc `common`? - [x] Được thiết kế để có thể sử dụng cho tất cả các service - [ ] Được thiết kế để có thể sử dụng cho một vài service - [ ] Được định nghĩa trong file `common.yaml`. - [x] Mỗi model nên được định nghĩa trong từng file `yaml` riêng lẻ. ### Q2 Đâu là cách làm **ĐÚNG** khi định nghĩa `pagination` của Response khi thiết kế API? - [ ] Sử dụng model `pagination` được định nghĩa trong service hiện tại - [ ] Sự dụng model `pagination` trong thư mục con `common` của Layer tương ứng - [ ] Sử dụng model `pagination` trong bất kì thư mục `common` - [x] Sử dụng model `paginationOffsetResponse` hoặc `paginationResponse` trong thư mục gốc `common` ### Q3 Đối với một API trả về danh sách objects, khi nào nên chọn `pagination` với `offset` (keyset pagination) thay vì trả ra `page`, `pageSize`, `totalItems`? - [x] Khi cần yêu cầu về performance của API - [ ] Luôn ưu tiên dùng keyset pagination - [ ] Khi xác định được primary key của object để định nghĩa giá trị offset - [ ] Dùng loại nào cũng được #### Hint: Với API get list cần performance cao thì cần dùng keyset pagination (vì không phải count total nên sẽ nhanh hơn) Trong trường hợp dữ liệu nhỏ, thì dùng loại nào cũng được ### Q4 Trong các Reference Object bên dưới, đâu là object được định nghĩa trong thư mục gốc `common`? - [x] **`page`**, **`pageSize`** trong query param - [x] **`page`**, **`pageSize`** trong request body - [x] **`Authorization`** trong header khi xác thực qua IAM - [x] **`baseApiResponse`** - [ ] **`platformId`** #### Hint: Luôn dùng page, pageSize trong query param thay vì trong request body PlatformId không phải là object có thể dùng được cho tất cả service ### Q5 Đâu là giá trị **ĐÚNG** của `code` khi API trả về kết quả thành công? - [ ] "200" - [ ] 0 - [x] 200 - [ ] Phụ thuộc vào từng service - [ ] "00" ### Q6 Khi nào cần đánh dấu một field hoặc API là **deprecated**? - [ ] **A.** Khi field/API không còn được sử dụng bởi client nào nữa - [ ] **B.** Khi API không còn được sử dụng bởi client nào nữa; và khi field không được phép sử dụng cho các tính năng mới nhưng vẫn được sử dụng cho các tính năng cũ trước đó. - [x] **C.** Khi field/API không được dùng cho các tính năng mới nhưng vẫn cần đảm bảo backward compatibility cho các tính năng cũ. - [ ] **D.** Đáp án A và C đều đúng #### Hint: Với những field/API không còn dùng đến nữa thì nên remove luôn khỏi API docs ### Q7 Yêu cầu nào dưới đây nằm trong checklist của một MR trên repo API Documentation? - [x] Gắn thẻ Tag cho tất cả các API - [ ] Tất cả các comment thread cần được giải quyết - [x] Các API có cùng Tag cần được đặt cạnh nhau trong file - [ ] Điền đầy đủ thông tin theo template của MR - [x] Chỉ rõ link UI confluence trong phần mô tả của các BFF API ### Q8 Ai sẽ là người click **Resolve thread** trong các comment thread của MR? - [ ] Người tạo MR - [ ] Người duyệt MR - [ ] Người được assign review MR - [ ] Người merge MR - [x] Người tạo comment thread ### Q9 Những thay đổi nào nên được gắn Label **API DESIGN** cho MR? - [x] Sửa lỗi chính tả tên field - [x] Thay đổi API path - [x] Nhóm object chung tạo thành Reference Object mà không làm thay đổi API - [x] Bổ sung thêm giá trị cho Enum field - [x] Đánh dấu một filed là `required` - [ ] Bổ sung mã lỗi #### Hint: Việc gắn Label API Design và API Document để thuận tiện hơn trong việc review, cũng như có liên quan tới phân quyền cho việc Merged MR. API Design để đánh dấu các MR có những thay đổi quan trọng, cần được review kĩ. Do vậy, tất cả những thay đổi về cấu trúc, request, response API, những thay đổi liên quan tới Reference Object đều cần gắn Label API Design ### Q10 Những thay đổi nào nên được gắn Label **API DOCUMENT** cho MR? - [x] Bổ sung mã lỗi và ví dụ - [x] Thêm mô tả validation cho field - [ ] Chuyển Reference Object sang file riêng - [ ] Thêm common object `iamId` trong thư mục gốc `common` - [x] Gắn Tag cho API ### Q11 Khi nào cần tạo **Reference Object**? - [ ] Mỗi API response nên là một Reference Object - [ ] Khi Reference Object tương ứng với một DB entity - [ ] Khi có thể sử dụng lại ở ít nhất 2 nơi - [x] Khi có thể được sử dụng ở rất rất nhiều nơi - [x] Khi Reference Object lớn và có thể sử dụng lại ở ít nhất 2 nơi ### Q12 Phát biểu nào là **ĐÚNG** khi nói về API của **Core Logic Layer**? - [x] Không nên có logic về phân quyền, lựa chọn ngôn ngữ - [ ] Có thể refer đến Reference Object của một Core Service khác - [x] Không được phép refer đến Reference Object của Presentation Layer - [x] CRUD API của cùng một Business Data Object phải được gắn cùng thẻ Tag - [x] API được tổ chức quản lý theo Business Data Object #### Hint: - Logic phân quyền và translate ngôn ngữ nên để ở BFF (**liệu chỗ này có ngoại lệ không ạ**? Ví dụ như Segment list thuộc tính có lựa chọn vi/en vì dữ liệu translate này trong DB) - Nếu object giống nhau giữa các relative service nên tạo common object trong sub folder, chứ không nên refer lẫn nhau, khó quản lý quy hoạch object. - Object của BFF thay đổi theo màn hình, nên tuyệt đối không được refer tới ### Q13 Dựa vào đâu để quyết định việc tạo mới hoặc cập nhật một **Core API**? - [ ] Mục đích sử dụng của API - [ ] Request params - [x] Response data - [ ] Cách thức implement logic của API ### Q14 Trong các trường hợp bên dưới của **Core Service**, khi nào chúng ta lựa chọn cập nhật API có sẵn thay vì tạo mới API? - [x] Có cùng Reference Object, nhưng mục đích sử dụng khác nhau - [x] Có cùng Reference Object, nhưng cần bổ sung thêm field trong response - [x] Có cùng Reference Object, nhưng cần bổ sung thêm field trong cả request và response - [ ] Sử dụng Reference Object mới ### Q15 Ví dụ về yêu cầu UX như sau: Màn hình cho phép admin lựa chọn **paymentCardEncodeFormat: E1, E2** cho từng đơn vị bán. Mặc định khi không lựa chọn nghĩa là đơn vị bán đó không sử dụng thẻ để thanh toán. Đâu sẽ là lựa chọn tốt nhất khi design field cho API **upsertRetailer** trong **Core Service**? - [ ] **A.** paymentCardEncodeFormat: Enum "E1", "E2", "None" - [ ] **B.** paymentCardEncodeFormat: Enum "E1", "E2". (giá trị Null khi không sử dụng thẻ thanh toán) - [x] **C.** isUsingPaymentCard: bool; paymentCardEncodeFormat: Enum "E1", "E2" - [ ] **D.** Cả hai đáp án A và B đều đúng #### Hint: Với API của Core Service , mỗi field chỉ nên có một ý nghĩa ### Q16 Giữa đối tượng **campaign** và **promotion** có mối quan hệ N-N, hiện tại Core Service đã tồn tại Tag `campaign` và`promotion`. Nếu cần định nghĩa một API mới Lấy danh sách promotion của campaign, API này nên có Tag là gì? - [ ] `campaign` - [x] `promotion` - [ ] Tạo Tag mới `campaign-promotion` - [ ] Tạo Tag mới `campaign-promotion` trong trường hợp cũng cần có API Lấy danh sách campaign sử dụng promotion, ngược lại thì sử dụng Tag `campaign` ### Hint: Như đã nói, API Core được tổ chức theo Business Data Object, với API này Response Data là list promotion, nên sẽ được gắn Tag `promotion` Tương tự, nếu có API Get list campaign sử dụng promotion, thì response data là danh sách campaign, nên sẽ được gắn Tag `campaign` ### Q17 Ví dụ về yêu cầu UX như sau: Hiển thị danh sách các merchant (có thể tìm kiếm theo **merchantId**) trả về các thông tin cơ bản; và màn hình hiển thị chi tiết thông tin merchant kèm theo danh sách các terminal của merchant đó. Giả sử: chi tiết **merchant** không có quá nhiều thông tin. Nên thiết kế **Core API** như thế nào? - [ ] **`GET /merchants`** (filter `merchantId`, return `[merchantBaseInfo]`); **`GET /merchants/{merchantId}`** (return `merchantDetailWithTerminals`) - [ ] **`GET /merchants`** (filter `merchantIds`, return `[merchantBaseInfo]`); **`GET /merchants/{merchantId}`** (return `merchantDetail`); **`GET /terminals`** (filter `merchantId`, return `[terminalDetail]`) - [ ] **`GET /merchants`** (filter `merchantId`, return `[merchantBaseInfo]`); **`GET /merchants/{merchantId}`** (return `merchantDetail`); **`GET /terminals`** (filter `merchantId`, return `[terminalDetail]`) - [x] **`GET /merchants`** (filter `merchantIds`, return `[merchantDetail]`); **`GET /terminals`** (filter `merchantId`, return `[terminalDetail]`) ### Hint: - Với Core Service, mình sẽ không quan tâm tới màn hình, trường hợp sử dụng, mà mình sẽ quan tâm tới dữ liệu cần trả ra là gì. Ở đây cần dữ liệu danh sách merchant, và danh sách terminal, là hai Business Data Object của Core, do vậy mình cần 2 API Get List này. - Ngoài ra vì merchant info không có quá nhiều thông tin, nên mình không cần thiết có riêng 1 API GetDetailMerchant, mà trả toàn bộ merchantDetail trong API GetListMerchant luôn. - Ngoài ra, ở đây lưu ý thêm là với API Core thì có thể thiết kế cho tương lai, ví dụ yêu cầu filter theo merchantId thì nên cho phép filter theo nhiều Id luôn (Optional) ### Q18 Trong trường hợp nào cần tạo API mới mà không sử dụng lại API có sẵn đối với **Presentation Layer**? - [ ] Cùng Reference Object, cùng mục đích sử dụng nhưng được dùng trong màn hình khác nhau - [ ] Cùng mục đích sử dụng nhưng cần bổ sung thêm request param - [x] Khác phương thức xác thực - [ ] Cùng mục đích sử dụng nhưng cần bổ sung thêm field trong cả request và response #### Hint: Chắc chắn khi khác phương thức xác thực thì cần phải tạo API mới. Còn các trường hợp khác khi cùng mục đích sử dụng thì hầu hết có thể chỉ cần update API, tuy nhiên cũng phải cân nhắc trong trường hợp cụ thể hơn ### Q19 Phát biểu nào là **ĐÚNG** khi nói về **Presentation Layer**? - [x] Nhóm các API theo màn hình - [x] Có thể refer tới Reference Object của Core Service (nhưng nên hạn chế) - [ ] Có thể refer tới Reference Object của BFF khác - [ ] Nhóm các API CRUD object vào cùng Tag - [x] Không để chung API **public** với API **non-public** trong cùng folder #### Hint: Việc BFF refer tới object của Core Service thì không cấm hoàn toàn, tuy nhiên nên hạn chế. Vì việc ref tới service khác khi maintain Reference Object sẽ phức tạp hơn, khi thay đổi phải cân nhắc rất nhiều, nhiều khi gây khó trong việc cập nhật các Object này. ### Q20 -> kiểm tra đáp án Ví dụ về yêu cầu UX như sau: Màn hình cho phép admin lựa chọn **paymentCardEncodeFormat: E1, E2** cho từng đơn vị bán. Mặc định khi không lựa chọn nghĩa là đơn vị bán đó không sử dụng thẻ để thanh toán. Đâu sẽ là lựa chọn tốt nhất khi design field cho API **upsertRetailer** của **BFF**? - [ ] **A.** paymentCardEncodeFormat: Enum "E1", "E2", "None" - [x] **B.** paymentCardEncodeFormat: Enum "E1", "E2". (giá trị Null khi không dùng thẻ thanh toán) - [ ] **C.** isUsingPaymentCard: bool; paymentCardEncodeFormat: Enum "E1", "E2" - [ ] **D.** Cả hai đáp án A và B đều đúng #### Hint: - Đối với BFF, field thiết kế dựa trên màn hình, mỗi thông tin hiển thị trên màn hình chỉ nên tương ứng với một field. - Trên UI, người dùng không lựa chọn thì tương ứng field đó giá trị Null, thay vì dùng thêm 1 giá trị Enum "None" không cần thiết #### tu.nb - tu.nb này em nghĩ có thể để là D, cái này cũng ko phải bắt buộc là để null mà ko dùng none, tuỳ theo prefer phía FE mong muốn để xử lý cho tiện ### Q21 -> kiểm tra đáp án Trên màn hình chi tiết merchant, có sử dụng API Lấy danh sách các phương thức thanh toán, API đang được gắn Tag `merchant`. Sau đó, khi thiết kế API cho màn hình chi tiết shop cũng sử dụng đến API này, biết rằng các API của màn hình chi tiết shop được gắn Tag `shop`. Vậy cần làm gì với API trên trong trường hợp này? - [ ] Clone thành API mới và gắn Tag `shop` - [x] Sử dụng API cũ và không thay đổi gì cả - [ ] Chuyển API cũ sang Tag mới (Vd: `common-selections`) - [ ] Sử dụng multiple Tag cho API trên #### Hint: Cái này em chọn đáp án Chuyển API cũ sang Tag mới, Tag dành cho các API kiểu lựa chọn giá trị dropdown dùng chung. Tuy nhiên em cũng không thấy pros, cons rõ ràng, chỗ này chỉ nên là thống nhất thôi ạ. #### tu.nb Câu này em thấy khó chọn được 1 phương án đúng nhất, tuỳ theo tiêu chí mình ưu tiên. Cùng tuỳ API đấy lấy theo merchant hay là danh sách phương thức thanh toán chung. Nếu danh sách pttt chung thì dể sang tag payment cũng đc, còn chỉ cho merchant thì có thể để như cũ. Câu hỏi như này chắc mình chỉ bổ sung vào guideline, còn câu hỏi thì chắc không đưa vào vì có thể gây tranh cãi. ### Q22 Ví dụ về yêu cầu UX như sau: Hiển thị danh sách các merchant (có thể tìm kiếm theo **merchantId**) trả về các thông tin cơ bản; và màn hình hiển thị chi tiết thông tin merchant kèm theo danh sách các terminal của merchant đó. Giả sử: chi tiết **merchant** không có quá nhiều thông tin. Nên thiết kế **BFF API** như thế nào? - [x] **`GET /merchants`** (filter `merchantId`, return `[merchantBaseInfo]`); **`GET /merchants/{merchantId}`** (return `merchantDetailWithTerminals`) - [ ] **`GET /merchants`** (filter `merchantIds`, return `[merchantBaseInfo]`); **`GET /merchants/{merchantId}`** (return `merchantDetail`); **`GET /terminals`** (filter `merchantId`, return `[terminalDetail]`) - [ ] **`GET /merchants`** (filter `merchantId`, return `[merchantBaseInfo]`); **`GET /merchants/{merchantId}`** (return `merchantDetail`); **`GET /terminals`** (filter `merchantId`, return `[terminalDetail]`) - [ ] **`GET /merchants`** (filter `merchantIds`, return `[merchantDetail]`); **`GET /terminals`** (filter `merchantId`, return `[terminalDetail]`) #### Hint: Thiết kế API cho BFF cần phục vụ cho màn hình, do vậy mình sẽ gom thông tin trả về trong API tương ứng với màn hình, ở đây có 2 màn hình get list và get detail, nên mình cần 2 API tương ứng. ### Q23 Trong trường hợp nào thì nên tạo riêng API `updateMerchantStatus` thay vì gộp chung với API `upsertMerchant`? - [ ] Chỉ nên có 1 API `upsertMerchant` trong mọi trường hợp - [ ] Khi `merchantStatus` luôn được cập nhật độc lập mà không đi kèm các thông tin khác của merchant - [ ] Khi `merchantStatus` có các quy trình chuyển đổi phức tạp - [x] Khi `merchantStatus` có các quy trình chuyển đổi phức tạp và luôn được cập nhật độc lập mà không đi kèm các thông tin khác của merchant #### Hint: - Với những field như status, khi cập nhật chỉ cập nhật đơn lẻ field, và có nhiều logic phức tạp cho luồng chuyển đổi trạng thái. Ví dụ status A chỉ được chuyển sang D, E và cần có những validate riêng cho từng trạng thái, thì mình có thể tách riêng API upsertStatus - Tuy nhiên không nên thiết kế API với mỗi luồng chuyển đổi là 1 API (Vd: API approveMerchant, blockMerchant,...). Làm như vậy vừa tạo ra quá nhiều API nhỏ lẻ khó maintain, mà khi thay đổi list status này thì việc sửa đổi bổ sung API cũng phức tạp hơn ### Q24 Cần làm gì trước khi cập nhật một Reference Object? - [ ] Kiểm tra ảnh hưởng tới các API trong service hiện tại đang sử dụng Object đó - [ ] Kiểm tra ảnh hưởng tới các API/model trong service hiện tại đang sử dụng Object đó - [ ] Nếu là bổ sung field optional thì sửa đổi theo nhu cầu, ngược lại mới cần cân nhắc ảnh hưởng tới những nơi đang dùng - [x] Kiểm tra trên toàn bộ project API docs những nơi đang sử dụng Object đó, và cân nhắc ảnh hưởng trước khi sửa đổi #### Hint: Khi thay đổi Reference Object cần full search trên toàn bộ project ### Q25 Vì sao nên tạo Reference Object khi object đó phải đủ lớn và được sử dụng ở nhiều nơi? - [x] Khi tạo Reference Object nhỏ lẻ, service sẽ có rất nhiều Reference Object, khó kiểm soát và gây khó khăn trong việc thiết kế API - [x] Không nên tạo Reference Object nếu chỉ dùng ở một nơi vì sẽ khó review API design - [x] Cần tạo Reference Object khi được dùng ở nhiều nơi để dễ kiểm soát thay đổi của Object, tránh thiếu sót khi phải cập nhật ở nhiều nơi - [ ] Làm trang apidoc.teko.vn load chậm ### Q26 Vì sao khi thiết kế API cho **Core Logic Layer** cần dựa trên Response Data, và tổ chức API theo Business Data Object? - [x] API của Core Service cần quan tâm tới dữ liệu mong muốn trả ra là gì, không cần quan tâm tới nghiệp vụ dùng như thế nào - [ ] Các API cùng chung Business Data Object thường có cùng mục đích nên cần được gom cùng nhau - [x] Gom nhóm API theo Business Data Object để dễ dàng trong việc thiết kế API, quyết định thêm mới hoặc cập nhật API, mỗi Data Object chỉ nên có một bộ CRUD API - [x] Vì Business Data Object là đối tượng chính của API Core, tổ chức API theo Business Data Object giúp dễ dàng tra cứu API cần sử dụng ### Q27 Vì sao khi thiết kế API cho **Presentation Layer** cần dựa trên đối tượng sử dụng API là Client, và tổ chức API theo nhóm người dùng và màn hình? - [x] Mỗi Client có thể có phương thức xác thực khác nhau - [x] Các Client khác nhau có thể có những yêu cầu customize riêng trên từng màn hình, nhưng không muốn đưa logic tính toán về client, mà xử lý ở BFF - [x] Gom nhóm API theo chức năng nhóm người dùng và màn hình để dễ dàng trong việc tra cứu, quản lý các API sử dụng trên từng màn hình - [ ] Gom nhóm API theo chức năng nhóm người dùng và màn hình để dễ dàng trong việc quản lý tính năng trên từng màn hình ### Q28 Theo API design guideline, các câu nào sau đây là **đúng** khi thiết kế các trường cho API? - [ ] Các trường thời gian có định dạng Unix timestamp, type: integer, format int64 - [x] Cần định nghĩa số lượng tối đa với các trường dạng mảng - [x] Các trường có type là number cần định nghĩa format cụ thể (float/double) - [ ] Format mặc định của type integer là int64 ### Q29 Các câu nào là **đúng** trong quá trình tạo MR API docs? - [x] Yêu cầu bắt buộc có gắn link tài liệu API Design trên confluence với MR Design cho Epic - [ ] Tick hết các checklist trong mô tả MR mà không cần biết đã thực hiện chưa để review cho nhanh - [x] Yêu cầu TechPIC/L3/L4 review nội bộ (tối thiểu 1 approved) trước khi mark Ready để Committee review. Khi MR có thay đổi theo đề xuất của Committe cũng cần review nội bộ trước - [x] Fix merged MR: là MR fix lỗi/bổ sung thiếu sót cho merged MR trước đó, mention link MR đã merged trước đó ### Q30 Chọn các câu **đúng** trong các câu sau khi nói về phần mô tả của các trường trong API? - [ ] Chỉ cần mô tả chung chung hoặc dùng luôn tên trường làm mô tả, cho dù ý nghĩa trường đó phức tạp do mô tả nhiều thì mất công làm API, để thời gian đó code luôn khoẻ - [x] Mô tả đầy đủ giúp người đọc (dev sử dụng API, dev cài đặt API) hiểu được ý nghĩa của trường, không mất thời gian hỏi lại người tạo API - [x] Với các trường có ý nghĩa phức tạp, mô tả dài có thể gắn kèm link tài liệu mô tả chi tiết về trường đó - [x] Với các trường có tên quá rõ ràng thì có thể không cần thêm mô tả