confidential
GoとDDDでモバイルオーダープラットフォームを
型安全に作り直した話
takuya kikuchi @ Showcase Gig
1
2022-02-22 GeekGig 『DONUTS×Showcase Gig』〜Goでゼロから作り直し
た話〜
confidential
©Showcase Gig
2
発表者

- capybara (takuya kikuchi)
- twitter: @_pochi
- Engineer Group Manager @ Showcase Gig
confidential
©Showcase Gig
3
GoとDDDでモバイルオーダープラットフォームを型安
全に作り直した話

confidential
©Showcase Gig
4
GoとDDDでモバイルオーダープラットフォームを型安
全に作り直した話

confidential
©Showcase Gig
5
モバイルオーダープラットフォーム

モバイルオーダーと一言でいっても、色々あります
confidential
©Showcase Gig
6
モバイルオーダープラットフォーム

モバイルオーダーと一言でいっても、色々あります
注文シーン 受け取りシーン
confidential
©Showcase Gig
7
モバイルオーダープラットフォーム

モバイルオーダーと一言でいっても、色々あります
自宅から
注文シーン 受け取りシーン
confidential
©Showcase Gig
8
モバイルオーダープラットフォーム

モバイルオーダーと一言でいっても、色々あります
自宅から
注文シーン 受け取りシーン
店舗で受け取り
confidential
©Showcase Gig
9
モバイルオーダープラットフォーム

モバイルオーダーと一言でいっても、色々あります
自宅から
注文シーン 受け取りシーン
店舗で受け取り
ロッカーで受け取り
confidential
©Showcase Gig
10
モバイルオーダープラットフォーム

モバイルオーダーと一言でいっても、色々あります
自宅から
注文シーン 受け取りシーン
店舗で受け取り
ロッカーで受け取り
店内で食べながら
confidential
©Showcase Gig
11
モバイルオーダープラットフォーム

モバイルオーダーと一言でいっても、色々あります
自宅から
注文シーン 受け取りシーン
店舗で受け取り
ロッカーで受け取り
店内で食べながら
テーブルまで配膳
confidential
©Showcase Gig
12
モバイルオーダープラットフォーム

O:der Table: 店内飲食
O:der Kiosk: Kiosk端末からの注文(イートイン・テイクアウト)
O:der ToGo: テイクアウト
The Label Fruit: テイクアウト(ロッカー受け取り)
O:der Platform: 各プロダクトの共通基盤
and more
confidential
©Showcase Gig
13
モバイルオーダープラットフォーム

O:der Table: 店内飲食
O:der Kiosk: Kiosk端末からの注文(イートイン・テイクアウト)
O:der ToGo: テイクアウト
The Label Fruit: テイクアウト(ロッカー受け取り)
O:der Platform: 各プロダクトの共通基盤
and more
confidential
©Showcase Gig
14
GoとDDDでモバイルオーダープラットフォームを型安
全に作り直した話

confidential
©Showcase Gig
15
モバイルオーダープラットフォームを作り直すぞ!

DDDをやるぞ!

confidential
©Showcase Gig
16
DDD (ドメイン駆動設計)

ドメイン駆動設計とはソフトウェアの設計手法である。
「複雑なドメインの設計は、モデルベースで行うべき」であり、また「大半のソフト
ウェアプロジェクトでは、システムを実装するための特定の技術ではなく、ドメイ
ンそのものとドメインのロジックに焦点を置くべき」であるとする。
出典:
https://ja.wikipedia.org/wiki/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E9
%A7%86%E5%8B%95%E8%A8%AD%E8%A8%88
confidential
©Showcase Gig
17
わからん

confidential
©Showcase Gig
18
なんとなくやってみよう

書籍などを参考にしつつ、チームとしてこれくらいの理解でやってみた
● いいプロダクトにはいいドメインモデルがあるらしい
● ユビキタス言語を定めて、それを使って対話をするべきらしい
● エンジニアだけでなく、ドメインエキスパートを集めてモデリングをするとい
いらしい
● ドメインモデルと実装は一致しているといいらしい
● 整合性を保ちたい範囲を集約とかいうらしい
confidential
©Showcase Gig
19
ドメインモデリングした

● 登場人物
○ プロダクトマネージャー(プロダクトに詳しい)
○ CS(飲食店のオペレーションに超詳しい)
○ エンジニア(詳しくなりたい)
● ホワイトボードツールつかって
おしゃべりした
○ 「「注文」って何?」
○ 「商品って?メニューって?」
○ ユビキタス言語を定めていく
confidential
©Showcase Gig
20
注文って何

● お金を払う行為は伴うの?
● ファミレスでの「注文」と、デリバリーサービスでの「注文」って一緒なの?
● 注文の単位ってなんだろう。
○ 3人で1つずつ頼んだ料理は3注文?
○ 1人で3つ料理を頼んだらそれは3注文?
Oxford Languagesの定義
ちゅうもん
【注文・註文】
《名・ス他》
1. 1.
品質・数量・形・寸法等を指定して、作らせたり送らせたりすること。
 「―を取る」
2. 2.
こうしてほしいと指図(さしず)をし、希望すること。
 「―をつける」
confidential
©Showcase Gig
21
モデルの例

※簡略化しています
confidential
©Showcase Gig
22
GoとDDDでモバイルオーダープラットフォームを型安
全に作り直した話

confidential
©Showcase Gig
23
Goで実装するよ

● 全体設計
○ Onion Architecture
● 今日は主にドメイン層とユースケース層の話をします
confidential
©Showcase Gig
24
こいつを忠実に実装する

confidential
©Showcase Gig
25
ドメイン層: Order struct 定義

// Order 注文モデル
type Order struct {
orderID types.OrderID
restaurant order_vo.Restaurant
orderItems []*order_vo.OrderItem
orderedAt order_vo.OrderedAt
orderState order_vo.OrderState
}
● すべてValue Objectとし、プリミティブ型は扱わない
confidential
©Showcase Gig
26
ドメイン層: CreteOrder
 // NewOrder 注文を新規に作成する
func NewOrder(
orderID types.OrderID,
restaurant order_vo.Restaurant,
orderItems []*order_vo.OrderItem,
orderedAt order_vo.OrderedAt,
state order_vo.OrderState,
) (*Order, error) {
// 注文時間から、店舗で注文受付可能か判断す
る
accept, err :=
restaurant.IsOrderAcceptable(orderedAt)
if err != nil {
// 何からのエラーが発生したので注文失敗
return nil, err
}
if !accept {
// 店舗設定により注文不可なので注文失敗
return nil, errors.New("注文受付時間
外")
}
// 注文オブジェクト生成
return &Order{
orderID: orderID,
restaurant: restaurant,
orderItems: orderItems,
orderedAt: orderedAt,
orderState: state,
}, nil
}
● Entity / ValueObjectの生成は、必ず
専用のコンストラクタを利用する。
● 注文作成条件を満たさないような、
不正な注文オブジェクトは
生成できないようにする
● 引数なども正しいものしか
渡ってこない前提で実装できる
confidential
©Showcase Gig
27
ユースケース層: CreateOrder

ユースケース層の実装
● Entity / VOを生成して
● 永続化する
それだけを行う。
「この場合注文できていいんだっ
け?」というロジックはすべてドメ
インモデルの中に含まれているの
で、Usecase層は何もしなくてよ
い。
// Create 注文を受け付ける
func (o *CreateUseCase
) Create(ctx context.Context, input CreateInput
) (orderId
types.OrderID, err error) {
// DBトランザクション内で処理する
err = o.dbClients.WritableClient.
RunInWriteTx
(略) (err error) {
// ~~~~ 略 ~~~~
// 取得した商品情報から注文商品情報を組み立てる
orderItems
, err := vo.NewOrderItems
(orderRequestItems
, items)
if err != nil {
// 注文商品情報が不正
return
}
// 注文ステータス作成
initialState
, err := vo.NewOrderState
(input.InitialOrderState)
if err != nil {
// 注文ステータスが不正
return
}
// 店舗情報取得
restaurant
, err := o.masterRepository.
GetRestaurant
(ctx, queryable,
types.RestaurantID
(input.RestaurantId))
if err != nil {
// 店舗が不正
return
}
// 注文エンティティ生成
orderId = types.OrderID(numberingService.
Generate())
orderEntity
, err := entity.NewOrder(
orderId
,
restaurant
,
orderItems
,
orderedAt,
initialState
,
)
if err != nil {
// 注文作成に失敗
return
}
// 注文を永続化
err = o.orderRepository.
Persist(ctx, queryable, orderEntity)
if err != nil {
// ~~~~ 略 ~~~~
}
return
})
return orderId, nil
}
confidential
©Showcase Gig
28
Goで実装したよ

● DDDに従い、モデルを忠実に実装してみた
○ モデリングが終わった頃にはほぼ実装内容も決まっていて新感覚。
○ ドメイン層はピュアなGo言語の世界なので、ユニットテストを書きやすい。プログラミングし
てる!って気持ちになって楽しい。
● Goはゴリゴリのオブジェクト指向言語というわけではないが、十分に書ける。
○ Defined Typeがあるので、プリミティブ型に別名をつけるのが楽で快適
● 唯一気になるのは
○ コンストラクタを経由しないオブジェクト生成を禁止できない
○ (「こうすればいけるよ」募集中です )
confidential
©Showcase Gig
29
GoとDDDでモバイルオーダープラットフォームを型安
全に作り直した話

confidential
©Showcase Gig
30
おしまい

GoとDDDでモバイルオーダープラットフォームを 型安全に作り直した話