iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
1

header

今天想要介紹 Mutation 的一些設計上的習慣與技巧!


設計符合商業邏輯的 Mutation

接續前面講的 Anemic Object 問題,在 Mutation 這邊也是一樣的。如果沒有注意,其實我們很容易設計出「資料導向」而非「行為導向」的 Mutation API 。

請注意, GraphQL 不是只是單純的 CRUD ,一開始的確可以先以 CRUD 作為出發點去設計 create, update, delete 等,但一個 update 可以做的事情包山包海,可以修改各項不同概念的欄位、子物件,比如一個訂單可以調整金額、調整商品規格、調整狀態、調整折扣等等,但只用一個 updateOrder 來實作這麼多功能,一方面提升後端實作的複雜度,另一方面也會造成前端理解的困難。

type Mutation {
  updateOrder(
    id: ID!
    productsToDelete: [ID!]
    productsToAdd: [ProductInput!]
    productsToUpdate: [ProductInput!]
    ...
  ): Order
}

因此首先  要以商業邏輯需求來分離這些過勞的 API ,比如以更新 order 的 product 來說就可以再分成底下幾種 mutation :

  • deleteOrderProducts
  • removeOrderProducts
  • updateOrderProducts
  • addProductsToOrder

將 Mutation 依動作分得越特定越好,不必被 CRUD 的習慣給綁住。

將 Object type 與 field 之間的關聯納入考量

當一個 mutation 的動作 (新增、刪除、更新) 足夠明確時,還會遇到一個問題:該如何處理 list 結構的更新呢 ? 在 Shopify 的教學文件裡面,他將 mutation 的設計以物件之間 (這邊是 Order 與 Products) 的關係分為三類型。

  1. 一次對上所有,比如 updateOrderProducts (products: [Product!]!)
    但當數值太大時不好管理且功能太複雜。

  2. 一次對上部分,比如 deleteOrderProducts(ids: [ID!]!) 只需傳入需要改變的變量。

  3. 一次對一個,直接切分成個別的 mutation ,比如 addProductToOrder (product: Product!), removeProduct (id: ID!)
    這種方式彈性最高,機動性也最佳,可以應付的場景最多。

經驗上來說,可以考量以下幾點作為選擇依據:

  • 當資料量大、甚至使用分頁時,第一種方式就不太適合,而第二三就就比較實用 ; 但如果資料量不大且結構相對簡單很多,那第一種會是最簡單的實作方式。

  • 一次要做大量簡單操作時,第一種跟第三種皆可以,但第二種就比較難用 ids 來表達操作內容。

  • 在資料設計上,兩邊是否各自獨立還是有強制的依存關係。

    • 如果各自獨立的話,那麼 mutation 只是修改兩邊的關係,這樣一二三種方式都可以。
    • 如果有強制的依存關係,如 user.ordersorders 必須要依附 user 底下,那就強烈建議使用第三種,因為這邊不只是改變關係也實際改變到子物件資料內容,因此越精確越好。

命名慣例

Mutation 在命名上只要想好團隊規範,然後持續遵守就可以了。以 Shopify 為例,因為 GraphQL mutation 沒有一個很好的管理機制,幫助我們在 documentation 裡把各 domain 的 mutation 群聚起來,比如把 updateOrder, createOrder, removeOrder 放在一塊,因此他們 mutation 的命名規則就是: TypeName<action> , 前面的 mutation 就會變成 orderCreate, orderUpdate, orderRemove 或是更細節的 orderProductAdd

不過這部分就見仁見智,我個人還是比較喜歡動名詞的結構,看起來比較易懂。

另外命名時跟 query 一樣越特定越好,就算日後用不到 deprecate 掉也容易,或是也可以在後面加個 V2 來表示是第二代 API 。


上一篇
Think in GraphQL: Schema Query 設計守則 - 2
下一篇
Think in GraphQL - Schema Mutation 設計守則 - 2
系列文
Think in GraphQL30

尚未有邦友留言

立即登入留言