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.7k
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
11k
メディアドゥ Amazon Personalize in AWS メディアセミナー Q1/mediado-amazon-personalize-aws-media
kenthamaguchi
0
1.4k
MediaDo DynamoDB活用事例/mediado-dynamodb-usecase
kenthamaguchi
0
1.2k
Infra Study Meetup #5 メディアドゥスポンサーセッション/infra-study-meetup-5-mediado
kenthamaguchi
0
780
JAWS DAYS 2020 メディアドゥスポンサーセッション/jaws-days-2020-mediado
kenthamaguchi
1
1.8k
OOC 2020 メディアドゥ スポンサーセッション/ooc_2020_mediado
kenthamaguchi
0
540
MediaDo.go #1 レガシーに立ち向かう / mediado-go-1-vs-legacy
kenthamaguchi
0
1.2k
MediaDo.go #1 GopherCon 2019 参加レポート / mediado-go-1-gophercon-2019
kenthamaguchi
1
1.2k
Go conf 2019 spring, sponsor session "Go初導入の組織で、社内外へ貢献していくために実施した、2つのこと" / go-conf-2019-spring-sponsor-session-mediado
kenthamaguchi
1
490
Other Decks in Technology
See All in Technology
社外コミュニティで学び社内に活かす共に学ぶプロジェクトの実践/backlogworld2024
nishiuma
0
270
日本版とグローバル版のモバイルアプリ統合の開発の裏側と今後の展望
miichan
1
130
成果を出しながら成長する、アウトプット駆動のキャッチアップ術 / Output-driven catch-up techniques to grow while producing results
aiandrox
0
360
10個のフィルタをAXI4-Streamでつなげてみた
marsee101
0
170
新機能VPCリソースエンドポイント機能検証から得られた考察
duelist2020jp
0
230
DUSt3R, MASt3R, MASt3R-SfM にみる3D基盤モデル
spatial_ai_network
2
190
コンテナセキュリティのためのLandlock入門
nullpo_head
2
320
権威ドキュメントで振り返る2024 #年忘れセキュリティ2024
hirotomotaguchi
2
760
Wantedly での Datadog 活用事例
bgpat
1
520
非機能品質を作り込むための実践アーキテクチャ
knih
5
1.5k
WACATE2024冬セッション資料(ユーザビリティ)
scarletplover
0
210
レンジャーシステムズ | 会社紹介(採用ピッチ)
rssytems
0
200
Featured
See All Featured
Practical Orchestrator
shlominoach
186
10k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
28
4.4k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
232
17k
Large-scale JavaScript Application Architecture
addyosmani
510
110k
Building Better People: How to give real-time feedback that sticks.
wjessup
365
19k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
45
2.2k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.7k
How To Stay Up To Date on Web Technology
chriscoyier
789
250k
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は コーディングにおける銀の弾丸ではない 効果を発揮しそうな要件を見極めて 使っていきましょう
完