Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
MediaDo.go #2 Clean Architectureとの付き合い方/mediado...
Search
kent-hamaguchi
September 25, 2020
Technology
2
1.9k
MediaDo.go #2 Clean Architectureとの付き合い方/mediado-go-2-clean-architecture
MediaDo.go #2 というGoの勉強会で発表したスライドです。
https://mediado-go.connpass.com/event/186625/
kent-hamaguchi
September 25, 2020
Tweet
Share
More Decks by kent-hamaguchi
See All by kent-hamaguchi
メディアドゥ Go Conference 2021 スポンサーセッション/gocon-2021-mediado
kenthamaguchi
1
12k
メディアドゥ Amazon Personalize in AWS メディアセミナー Q1/mediado-amazon-personalize-aws-media
kenthamaguchi
0
1.5k
MediaDo DynamoDB活用事例/mediado-dynamodb-usecase
kenthamaguchi
0
1.3k
Infra Study Meetup #5 メディアドゥスポンサーセッション/infra-study-meetup-5-mediado
kenthamaguchi
0
860
JAWS DAYS 2020 メディアドゥスポンサーセッション/jaws-days-2020-mediado
kenthamaguchi
1
1.9k
OOC 2020 メディアドゥ スポンサーセッション/ooc_2020_mediado
kenthamaguchi
0
580
MediaDo.go #1 レガシーに立ち向かう / mediado-go-1-vs-legacy
kenthamaguchi
0
1.3k
MediaDo.go #1 GopherCon 2019 参加レポート / mediado-go-1-gophercon-2019
kenthamaguchi
1
1.3k
Go conf 2019 spring, sponsor session "Go初導入の組織で、社内外へ貢献していくために実施した、2つのこと" / go-conf-2019-spring-sponsor-session-mediado
kenthamaguchi
1
540
Other Decks in Technology
See All in Technology
Snowflake Intelligenceという名のAI Agentが切り開くデータ活用の未来とその実現に必要なこと@SnowVillage『Data Management #1 Summit 2025 Recap!!』
ryo_suzuki
1
160
shake-upを科学する
rsakata
7
1k
Maintainer Meetupで「生の声」を聞く ~講演だけじゃないKubeCon
logica0419
0
110
データ戦略部門 紹介資料
sansan33
PRO
1
3.3k
Amazon SNSサブスクリプションの誤解除を防ぐ
y_sakata
3
190
AWS Well-Architected から考えるオブザーバビリティの勘所 / Considering the Essentials of Observability from AWS Well-Architected
sms_tech
1
110
[SRE NEXT 2025] すみずみまで暖かく照らすあなたの太陽でありたい
carnappopper
2
470
VS CodeとGitHub Copilotで爆速開発!アップデートの波に乗るおさらい会 / Rapid Development with VS Code and GitHub Copilot: Catch the Latest Wave
yamachu
3
460
Microsoft Defender XDRで疲弊しないためのインシデント対応
sophiakunii
1
320
組織内、組織間の資産保護に必要なアイデンティティ基盤と関連技術の最新動向
fujie
0
280
伴走から自律へ: 形式知へと導くSREイネーブリングによる プロダクトチームの信頼性オーナーシップ向上 / SRE NEXT 2025
visional_engineering_and_design
3
460
20250718_ITSurf_“Bet AI”を支える文化とコストマネジメント
helosshi
0
100
Featured
See All Featured
Docker and Python
trallard
45
3.5k
Why Our Code Smells
bkeepers
PRO
337
57k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
2.9k
Adopting Sorbet at Scale
ufuk
77
9.5k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
126
53k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
108
19k
YesSQL, Process and Tooling at Scale
rocio
173
14k
Fireside Chat
paigeccino
37
3.5k
The Cost Of JavaScript in 2023
addyosmani
51
8.6k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.5k
Designing for Performance
lara
610
69k
Into the Great Unknown - MozCon
thekraken
40
1.9k
Transcript
MediaDo.go #2 Go初心者向け Clean Architectureとの付き合い方
目次 • パッケージの分け方 • リポジトリ • Use Case Interactor
(書籍の)Clean Architectureは 何を伝えたいのか
優れたアーキテクチャに 共通して見られるルール
ゆえに書籍のこの言葉につながる アーキテクチャの ルールはどれも 同じである!
Clean Architectureは 何をクリーンに保ちたいのか
ビジネスロジックを クリーンに保ちたい
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
時間が経てば ビジネスも テクノロジーも 開発者も変わる それらに対応し続けるために導入する
ということで、コーディングなら このあたりの書籍や記事に求めると良い • 実践テスト駆動開発 (Steve Freeman, Nat Pryce) • Clean
Code (Robert Cecil Martin) • https://refactoring.com/catalog/
モデリングについては下記 • アナリシスパターン (Martin Fowler) • エリック・エヴァンスのドメイン駆動設計 (Eric Evans) •
実践ドメイン駆動設計 (Vaughn Vernon)
完
そうはいってもイメージが沸かないので Goを題材に設計を試す
本題
パッケージ構成
パッケージ構成 Clean Architectureの円に従い、ルートから下記のように切ってみる。 -- entity -- usecase -- adapter --
infrastructure やってみた系で結構見かける構成
パッケージ構成 技術的目線でルートを切ってしまうと、ビジネスロジックの表現力が乏しくなる -- entity -- wallet.go -- book.go -- authentication.go
パッケージプライベートなので お互いのすべてを参照可能、それを前提に作ると ビジネスロジックの凝集度が下がり、結合度が上がる
パッケージ構成 -- entity -- wallet -- wallet.go -- book --
book.go パッケージが分かれることで凝集度が上がり、結合度を下げられるが・・・
パッケージ構成 -- entity -- wallet -- wallet.go -- usecase --
wallet -- wallet.go -- adapter -- wallet -- wallet.go -- infrastructure -- wallet -- wallet.go 同じような構成のサブパッケージが 各種レイヤーに乱立しやすくなる wallet視点で見ると凝集度が低い
パッケージ構成 下記のほうが自然 -- wallet -- wallet.go (もしくはentity配下にwallet.go) -- usecase --
deposit.go -- adapter -- 〜〜〜 walletのことを豊かに表現できる幅がでる 必要に応じてパッケージ追加
パッケージ構成 他のEntityには非依存なのでbookも個別にパッケージ化 -- book -- series.go -- book.go -- usecase
-- search.go ビジネスロジックにおいて関係性が強いものは、 パッケージプライベートのオブジェクトを作用させるのも可
パッケージ構成 Entityを扱う必要がなく、技術的観点が強めなら下記でも良い -- authentication -- cognito.go -- signin.go 認証とかビジネスロジックとちょっ と遠いから、
一旦フラットに作るか・・・
パッケージ構成 モジュールについては、下記のようにモノリスで作っていても... -- wallet -- book -- authentication -- go.mod
パッケージ構成 切り出して別リポジトリにするときに、移動させるだけなので楽 -- wallet -- book -- authentication -- go.mod
別リポジトリ (fuga/wallet) -- go.mod
パッケージ構成 • あの円のレイヤにトップレベルを分ける必要はない ◦ 無理やり構成を合わせると管理しづらい (体験談含) ◦ 実装がFatになりがち、比例して開発コストは上がる ◦ ビジネスロジックを表現しづらくなる
• Goの可視性を活用する ◦ 大文字で始めればパブリック、小文字で始まるならプライベート ▪ Javaでいうクラス単位くらいの気持ちでパッケージを切っても良い ◦ パッケージプライベートを活用する • 単一のGo modulesから分離させるときも楽
リポジトリ
リポジトリ RepositoryはEntityの永続化と再構築の責務 book book repository book book book データベースや 外部のAPI
プログラムの メモリ上に展開
リポジトリ Repositoryはユースケースやドメインサービスから利用される Use Case Repository Service
リポジトリ そのためこのような構成になる -- book -- series.go -- book.go -- repositoryの何かしらの定義
リポジトリ repositoryはinterfaceで定義する -- book -- book.go -- repository -- book.go
どちらかに type BookRepository interface { FindByID(string) (book, error) }
リポジトリ 実装は別パッケージに分ける -- book -- book.go -- adapter -- repository
-- book.go import fuga/book type BookRepository struct { book.BookRepository db *sql.DB // AWSとか使うならそのAPIインターフェース } func NewBookRepository(db *sql.DB) book.BookRepository { return { db: db } }
リポジトリ adapterとか外のパッケージにせず、repositoryパッケージで完結も有り -- book -- book.go -- repository -- book.go
type BookRepository interface { findByID(string) (book, error) } // 小文字始まりなので他のパッケージから参照できない type bookRepository struct { BookRepository db *sql.DB // AWSとか使うならそのAPIインターフェース } func NewBookRepository(db *sql.DB) BookRepository { return { db: db } }
リポジトリ データベースへのマッピング処理はrepositoryの中身で完結させる -- book -- book.go -- repository -- book.go
RDBとか 間にORM用の構造体を挟むなど 引数としてEntityのbookが渡される
リポジトリ 実装が環境によって複数パターンあるなら、実装を増やして付け替える -- book -- book.go -- repository -- book.go
-- book_mock.go (Go modulesは分けたほうが良いけど ) モック用の実装と付替できるようにするなど
リポジトリ データストアのことを考えて共通化が必要ならinternalにそれを出すのもあり -- book -- book.go -- repository -- book.go
-- internal -- rdb.go アプリケーション全体へ渡り共通の処理があるなら、 外部のモジュールから importできないinternalパッケージに 実装をまとめて利用するなど
リポジトリ • まだ実装が固まらない間はインターフェースと実装が隣りにあってもよい ◦ インターフェースをパブリックにして、実装をパッケージプライベートにする ◦ interfaceの定義を組み直したりする際に、定義は一箇所のほうがシンプルに作業が楽 • Entityの構造体とRepositoryの内部で扱う構造体は別 ◦
単純にEntityの構造体をシリアライズするのもよいが、非公開フィールドを解決できない ◦ RDBやKVSや外部APIへの実装をEntityやUse Caseが意識しないようにする • Repositoryの作成単位はEntityに合わせる(ドメイン駆動設計の集約単位) ◦ 定義はEntityの隣りにあるが、実装は外側に配置 (依存性逆転の原則)
Use Case Interactor
Use Case Interactor ここから先の実装は大きくなりがちで、よほど複雑な要件でない限りコストが見合わなさ そうだが、一例を記す
Use Case Interactor Clean ArchitectureではRepositoryだけでなく、入出力についても語られている • Use Caseを入出力から分離する • 入力処理をController、出力処理をPresenterに分ける
◦ 入力と出力を分離することで、 UIに関わる複雑な関係性をシンプルに保つ 流れの通りに実装するとUse CaseがPresenterに依存しがちになるなため、 Use Case Interactorを挟み、インターフェースで依存性を管理する
Use Case Interactor type Usecase interface { Deposit(DepositInput) (DepositOutput, error)
} type DepositInput struct { UserID string Amount int } type DepositOutput struct { TotalAmount int } Use Case (walletのusecaseのつもり) EntityやRepositoryの操作を実装するUse Case自体の 入出力はシンプルな構造体 (可能な限り単純な値 )にしておく (実装内容はここでは重要ではないので省略 )
Use Case Interactor type UsecaseInteractor struct { usecase Usecase controller
Controller presenter Presenter } type Controller interface { ReadDepositInput() (DepositInput, error) } type Presenter interface { WriteDepositOutput(DepositOutput) error } Use Case Interactorの定義 入出力処理は別々のインターフェースに分離し、 UsecaseInteractorの実装の中で流れを作る。 ControllerとPresenterに入出力処理は委ねられるが、 Usecaseの実行に対する流れはここが担う。
Use Case Interactor func (u *UsecaseInteractor) Deposit() error { in,
err := u.controller.ReadDepositInput() if err != nil { return err } out, err := u.usecase.Deposit(in) if err != nil { return err } return u.presenter.WriteDepositOutput(out) } Use Case Interactorの実装 Use Case Interactorで定義したインターフェースに ControllerとPresenterは実装をするため、ここで依存性の 逆転が発生し、外部の環境に依存する実装が外側のレイ ヤにまとまる。 Use Caseはシンプルな値のやり取りで完結するため、 入出力の複雑さがビジネスロジックに介入しない。
Use Case Interactor • 入出力が複雑な場合、インターフェースで分離して実装と流れを分ける ◦ 入力処理と出力処理を別々に実装でき、テストできる ◦ Use Caseの実装のシンプルさを保つことができる
◦ テスタビリティを獲得するかわりに、実装コストは上がる
まとめ
まとめ Clean Architectureにこだわりすぎると実装が辛くなる可能性がある • ビジネスロジックをクリーンに保ちたい場合は導入の検討をする ◦ Entityレベルをいかに実装できるかが鍵、外部の実装の逃し方は前述のとおり ◦ そうではなく、ライブラリ開発などの場合は実装が Fatになりすぎる可能性あり
• アプリケーションの要件で形は変わってくるので、そこを中心に添えておく • 外部のことを考えずにEntityとUseCaseを実装すれば良い • 外のレイヤはControllerなどの枠に固めず、必要な分だけ実装すればよい
Clean Architectureは コーディングにおける銀の弾丸ではない 効果を発揮しそうな要件を見極めて 使っていきましょう
完