この記事はWanoグループ Advent Calendar 2020 20日目の記事になります。20ったら20です。
突然ですが、ここに、image_assetというモデルがあるとします。
集約です。
{
"id": 13,
"user_id": 1,
"asset_id": 28,
"asset": {
"bucket": "988323dc-42e1-11eb-9c12-8c859050389c",
"location": "852cb6b4-f0d0-4ace-a1c3-2966c7027985",
"mime_type": "test/mimetype/image/repo",
"upload_datetime": null,
"filename": "988323e6-42e1-11eb-9c12-8c859050389c",
"s3_storage_class_id": 1,
"is_exist": false,
"file_size_byte": null,
"md5_hash": "8b1a5be6-5b44-45e0-9325-ec9428e378ff"
},
"image_asset_type_id": 1,
"orig_image_asset_id": null,
"width": 66780,
"height": 12087,
"image_asset_category_id": null,
"from_tcj": false
}
image_assetはassetを持ちます。
image_assetとassetはmysqlのテーブル上も別ですが、集約なのでライフサイクル上も一緒とします。
package mysqldto
import (
"database/sql"
"gopkg.in/guregu/null.v3"
"time"
)
var (
_ = time.Second
_ = sql.LevelDefault
_ = null.Bool{}
)
type ImageAsset struct {
ID int `gorm:"primary_key;AUTO_INCREMENT;column:id;type:uint;" json:"id"`
CreateTime *time.Time `gorm:"column:create_time;type:datetime;default:CURRENT_TIMESTAMP;" json:"create_time"`
UpdateTime *time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP;" json:"update_time"`
UserID int `gorm:"column:user_id;type:uint;" json:"user_id"`
AssetID int `gorm:"column:asset_id;type:uint;" json:"asset_id"`
ImageAssetTypeID int `gorm:"column:image_asset_type_id;type:utinyint;" json:"image_asset_type_id"`
Width int `gorm:"column:width;type:uint;default:0;" json:"width"`
Height int `gorm:"column:height;type:uint;default:0;" json:"height"`
ImageAssetCategoryID null.Int `gorm:"column:image_asset_category_id;type:utinyint;" json:"image_asset_category_id"`
FromTcj bool `gorm:"column:from_tcj;type:utinyint;default:0;" json:"from_tcj"`
}
type Asset struct {
ID int `gorm:"primary_key;AUTO_INCREMENT;column:id;type:uint;" json:"id"`
CreateTime *time.Time `gorm:"column:create_time;type:datetime;default:CURRENT_TIMESTAMP;" json:"create_time"`
UpdateTime *time.Time `gorm:"column:update_time;type:timestamp;default:CURRENT_TIMESTAMP;" json:"update_time"`
Bucket string `gorm:"column:bucket;type:varchar;size:100;" json:"bucket"`
Location string `gorm:"column:location;type:varchar;size:1023;" json:"location"`
MimeType string `gorm:"column:mime_type;type:varchar;size:50;" json:"mime_type"`
UploadDatetime null.Time `gorm:"column:upload_datetime;type:datetime;" json:"upload_datetime"`
Filename null.String `gorm:"column:filename;type:varchar;size:255;" json:"filename"`
IsExist bool `gorm:"column:is_exist;type:utinyint;default:1;" json:"is_exist"`
S3StorageClassID int `gorm:"column:s3_storage_class_id;type:utinyint;default:1;" json:"s_3_storage_class_id"`
FileSizeByte null.Int `gorm:"column:file_size_byte;type:ubigint;" json:"file_size_byte"`
Md5Hash null.String `gorm:"column:md5_hash;type:varchar;size:512;" json:"md_5_hash"`
}
ただし、mysqlのdtoを表現する構造体は以上のように愚直に別テーブルでsave_associationとかのタグ魔法もないとします。
ImageAssetをAssetと共にSave,Getするにはどうするでしょうか?
まあ普通に別途クエリを書けばいいんですが...
上書き
package mysql_repositoy
type imageAssetAggregate struct {
*mysqldto.ImageAsset
Asset mysqldto.Asset `gorm:"ForeignKey:AssetID;save_associations:true"`
}
...
gormDB = gormDB.Table(`image_asset`).
Preload(`Asset`).
Joins(`INNER JOIN asset ON asset.id = image_asset.asset_id`)
gormのtipsでもなんでもないんですが、単に一時的に構造体作って埋め込むだけでも、gormの魔法タグを使った同時SaveやPreloadによる雑な子集約の収集は実現できます。
...なんでこういうことしてるかというと、modelとdtoの分離の文脈とか、dtoは純粋にmysqlテーブルの形にしておきたいとか、でもでもActiveRecordっぽいEager Loadingは欲しいときあるよね!とかいろいろ経緯や事情があるんですが、まあこういうこともできるよという話でした。