Go言語で構造体と構造体をマッピングする、いわゆるObject mapperを生成するCLIを書きました。
経緯
READMEに書いてる通りですが、いわゆるClean architectureなど多層構造のアプリケーションではどうしても似たようなオブジェクトを定義せざるを得ないことがあります。
特にキツい(?)のがgRPCを採用した場合ですね。 protoc
が吐く構造体は完全にprotobuf
に依存したものになっておりある程度層をまたいで同じオブジェクトを持っていくのは許容しよう、と割り切ってもこれをいわゆるドメイン層にそのままもっていくのは結構ハードルが高いのではないかと思います。
#あと、gogo/protobuf
亡き今、protoc-gen-goが非標準命名規則でソースコードを出力するのもキモチワルイ。。。
WEB上にいっぱいあるGo + gRPCのクリーンアーキテクチャサンプル的なものでもここはかならず似たオブジェクトに詰め替えています。GoにはJavaにおけるDTOのような歴史はあまりないですし、アーキテクチャ上必要なことなんだから手で書くべき、という人もいますがとはいっても項目数が多くなってくるとつらいですよね。
これをある程度自動で詰め替えるライブラリはリフレクションを使ったものはそれなりにあるのですが、経験上
- リフレクションを使ったものはデバッグがつらい
- 項目が多くなってきたときに、1つだけなんかマッピングされてない!というときになんでマッピングされてないの? というのがわかりづらい
- 性能が微妙
- いくらGoのリフレクションが早いといってもリフレクション使わないコードよりは当たり前だが遅い
ということで、コード生成してくれるタイプのものが欲しかったんですがいいのが無かったので書いた、という次第です。
できること
構造体と構造体をマッピングするインタフェースと実装を生成できます。マッピング定義はYAMLに記載、結構柔軟に定義できます。 ネストしたものも再帰的にマッピングできます。
- 例:
string
とtime.Time
をマッピングするものを登録しておけば自動的にこれらのフィールドを持つ構造体を変換可能
比較的にきれいな(自分的には)インタフェースを吐くので最悪、このツール使うのやめようとなってもインタフェースに対する実装を自分で書けばよいだけです。生成するソースはこんな感じ
1package mapper
2
3import (
4 pkg00002 "time"
5
6 pkg00001 "example.com/testmod/domain"
7 pkg00000 "example.com/testmod/model"
8)
9
10type TodoMapperHelper interface {
11 ModelToEntity(*pkg00000.TodoModel, *pkg00001.Todo) error
12 EntityToModel(*pkg00001.Todo, *pkg00000.TodoModel) error
13}
14
15type TodoMapper interface {
16 ModelToEntity(*pkg00000.TodoModel) (*pkg00001.Todo, error)
17 EntityToModel(*pkg00001.Todo) (*pkg00000.TodoModel, error)
18}
19
20// ... (TodoMapper default implementation)
Goでの構造体のマッピング、結構どうにかしたいと思いつつとりあえず全部手でやってました、という人も多いと思うのでもしよければ使ってみてください。