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.3k
MediaDo DynamoDB活用事例/mediado-dynamodb-usecase
kenthamaguchi
0
1.2k
Infra Study Meetup #5 メディアドゥスポンサーセッション/infra-study-meetup-5-mediado
kenthamaguchi
0
760
JAWS DAYS 2020 メディアドゥスポンサーセッション/jaws-days-2020-mediado
kenthamaguchi
1
1.8k
OOC 2020 メディアドゥ スポンサーセッション/ooc_2020_mediado
kenthamaguchi
0
520
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
480
Other Decks in Technology
See All in Technology
Aurora_BlueGreenDeploymentsやってみた
tsukasa_ishimaru
1
120
[PyCon Korea 2024] Lightning Talk: PyPI패키지를 의심하세요
studioego
PRO
0
120
強すぎるIAMをCloudTrailを使って適正化した話
yjszk
0
230
AIを使って小説を書こう!【2024/10/25講演資料】
kamomeashizawa
0
160
omakaseしないための.rubocop.yml のつくりかた / How to Build Your .rubocop.yml to Avoid Omakase #kaigionrails
linkers_tech
3
170
WINTICKETアプリで実現した高可用性と高速リリースを支えるエコシステム / winticket-eco-system
cyberagentdevelopers
PRO
1
150
AWS SAW(AWS Support Automation Workflows)をもっと広めたい
kazzpapa3
2
170
OpenAIのAssistants API(Beta)の概要と使い方
takaakikakei
0
230
CyberAgent 生成AI Deep Dive with Amazon Web Services / genai-aws
cyberagentdevelopers
PRO
1
320
生成AIの活用パターンと継続的評価
asei
10
1.4k
Emacs x Nostr
hakkadaikon
1
120
内製化によるシステムモダナイゼーションの実践
kazokmr
3
520
Featured
See All Featured
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
3
360
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
Docker and Python
trallard
40
3k
A designer walks into a library…
pauljervisheath
202
24k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
364
22k
Code Reviewing Like a Champion
maltzj
519
39k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
13
1.9k
For a Future-Friendly Web
brad_frost
174
9.4k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Making Projects Easy
brettharned
115
5.9k
Making the Leap to Tech Lead
cromwellryan
132
8.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は コーディングにおける銀の弾丸ではない 効果を発揮しそうな要件を見極めて 使っていきましょう
完