読者コミュニティ|Goa v3 入門
本の感想や質問をお気軽にコメントしてください。
大変参考になりました。ありがとうございます。
作っていて疑問に思ったのが、よくある1対多の関係の時にdesignを書いていてinitialization loopになってしまったことです。
下記は商品に複数の口コミ紐付いている例です。
よくあることだとは思うのですが、どうやって避けているのでしょうか?
商品
var ProductResult = ResultType("application/vnd.product+json", func() {
TypeName("ProductResult")
Description("商品")
CreateFrom(result.Product{})
Attribute("name", String, "商品名")
Attribute("reviews", ArrayOf(ReviewResult), "口コミリレーション")
Required("name", "reviews")
View("default", func() {
Attribute("name")
Attribute("reviews")
})
})
口コミ
var ReviewResult = ResultType("application/vnd.review+json", func() {
TypeName("ReviewResult")
Description("口コミ")
CreateFrom(result.Review{})
Attribute("text", String, "口コミ内容", func() {
Example("おいしい!")
})
Attribute("product", ProductResult, "商品リレーション", func() {
})
Required("text", "product")
View("default", func() {
Attribute("text")
Attribute("product")
})
})
コメントいただいた状況を私がうまく理解できているか怪しいですが、商品の定義にレビューが含まれていて、レビューの定義に商品が含まれててしまっているので、定義がループしてしまって initialization loop になってしまうのは確かにそうなのかなと思います。
商品の定義からレビューを外し、レビューには商品の情報も含めるような設計にしておくのがよさそうに思います。
エンドポイントは次のような感じを想定します。
メソッド | エンドポイント | 得られる情報 |
---|---|---|
GET | /products/{id} | 指定されて id の商品情報 |
GET | /products/{id}/reviews | 指定された id の商品情報に関連するレビュー |
レビューを指定する時には商品の情報はもう既知であれば、view でレビューだけを含める場合を default
として、extend
で商品名も含めたレビューが得られるようにしてもいいかもしれません。
何かご参考になれば幸いです。
package design
import (
. "goa.design/goa/v3/dsl"
)
var _ = Service("sample", func() {
Method("show product", func() {
Payload(func() {
Attribute("id", Int)
Required("id")
})
Result(ProductResult)
HTTP(func() {
GET("/products/{id}")
})
})
Method("show review", func() {
Payload(func() {
Attribute( "id", Int)
Required("id")
})
Result(ReviewResult)
HTTP(func() {
GET("/products/{id}/reviews")
})
})
})
var Product = Type("Product", func() {
Attribute("name", String, "商品名")
Required("name")
})
var ProductResult = ResultType("application/vnd.product+json", func() {
TypeName("ProductResult")
Description("商品")
Extend(Product)
View("default", func() {
Attribute("name")
})
})
var Review = Type("Review", func() {
Attribute("text", String, "口コミ内容", func() {
Example("おいしい!")
})
Attribute("product", Product, "商品リレーション", func() {
})
Required("text", "product")
})
var ReviewResult = ResultType("application/vnd.review+json", func() {
TypeName("ReviewResult")
Description("口コミ")
Extend(Review)
View("default", func() {
Attribute("text")
})
View("extend", func() {
Attribute("text")
Attribute("product")
})
})
大変参考になりました!ありがとうございます。
なるほどループしないように設計する感じなんですね。
別途ResultTypeを作って対応でもよいのかなあと試行錯誤しておりました。
確かに教えていただいたもののほうが、疎結合で良いなと思いました。
ありがとうございます。
var ProductResult = ResultType("application/vnd.product+json", func() {
TypeName("ProductResult")
Description("商品")
CreateFrom(result.Product{})
Attribute("name", String, "商品名")
Required("name", "reviews")
View("default", func() {
Attribute("name")
Attribute("reviews")
})
})
var ProductWithReviewsResult = ResultType("application/vnd.product_with_reviews+json", func() {
TypeName("ProductWithReviewsResult")
Description("商品とそれに紐づく口コミ")
Attribute("product", ProductResult, "商品名")
Attribute("reviews", ArrayOf(ReviewResult), "商品に紐づく口コミ")
Required("product", "reviews")
View("default", func() {
Attribute("product")
Attribute("reviews")
})
})
別途 ResultType を用意するのもよいと思います!呼ぶ側の都合もあるので、こういったところは柔軟に設計を変更したいですよね。そういうことが Goa だとデザインを変更するだけで割と簡単に書けて、その影響もサービスメソッド内にとどまるので比較的シュッと変更できる、というのがいいところだと思っています。╭( ・ㅂ・)و ̑̑
ちょっとした更新:セキュリティの章に JWT の例を追加しました
記事で書いている「Goaの更新履歴」へのリンクをまとめたページと、HTTPトランスポートでレスポンスに MapOf を含むときの Tips を追加しました
「デザインを別モジュールとして切り出す」の章を追加しました。
『Goa のカスタムエラーを理解する』というブログ記事を書いたのですが、その記事へのリンクをエラー処理関連の章に追記しました。
『(コラム)OneOf を利用してみる』の章を追加しました
Goa v3.13.0 で mux router が treemux から chi に変更されて trailing slash の扱いが変わったので、Chapter 22『HTTP: Trailing Slash について知る』の末尾に chi での trailing slash の扱われ方を追記しました。
上記に関連して、 @tchssk さんが、treemux の挙動をシミュレートしてくれるプラグインを公開してくださっています。ブランク import して利用するだけなので、便利そう。
Goa v3.13.x で mux router が treemux から chi に変更されて trailing slash の扱いがちょっと混乱していたのですが、v3.14.0 で動作が確定したので、Chapter 22『HTTP: Trailing Slash について知る』の末尾に chi での trailing slash の扱われ方を修正しました。
hello i want get the native *http.Request as the payload. what can i do for this?
Hello,
To get the native *http.Request as the payload, you have a couple of options:
- You can use the SkipRequestBodyEncodeDecode() DSL to bypass the request body encoding and decoding. This will allow you to work directly with the HTTP request.
- Alternatively, you could create middleware that captures the original *http.Request and stores it in the context (ctx) for later retrieval and use.
For more detailed questions or to delve deeper into this topic, I recommend asking in the Goa channel on the Gopher Slack (https://gophers.slack.com/messages/goa/). You'll find a community of developers there who can offer extensive guidance on the Goa framework.
「HTTP: Method からトランスポートに入出力を割り当てる」の章のステータスが複数ある場合についての記述を加筆修正しました。Goa example に追加された例について追記しています。