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.8k
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
820
JAWS DAYS 2020 メディアドゥスポンサーセッション/jaws-days-2020-mediado
kenthamaguchi
1
1.9k
OOC 2020 メディアドゥ スポンサーセッション/ooc_2020_mediado
kenthamaguchi
0
570
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.3k
Go conf 2019 spring, sponsor session "Go初導入の組織で、社内外へ貢献していくために実施した、2つのこと" / go-conf-2019-spring-sponsor-session-mediado
kenthamaguchi
1
520
Other Decks in Technology
See All in Technology
Spring Bootで実装とインフラをこれでもかと分離するための試み
shintanimoto
7
800
AWSのマルチアカウント管理 ベストプラクティス最新版 2025 / Multi-Account management on AWS best practice 2025
ohmura
4
280
生成AIによるCloud Native基盤構築の可能性と実践的ガードレールの敷設について
nwiizo
5
340
AWS Control Towerを 数年運用してきての気づきとこれから/aws-controltower-ops-tips
tadayukinakamura
0
130
いつも初心者向けの記事に助けられているので得意分野では初心者向けの記事を書きます
toru_kubota
2
310
Devinで模索する AIファースト開発〜ゼロベースから始めるDevOpsの進化〜
potix2
PRO
7
3.3k
更新系と状態
uhyo
5
730
クォータ監視、AWS Organizations環境でも楽勝です✌️
iwamot
PRO
1
290
LLM とプロンプトエンジニアリング/チューターをビルドする / LLM, Prompt Engineering and Building Tutors
ks91
PRO
1
250
ElixirがHW化され、最新CPU/GPU/NWを過去のものとする数万倍、高速+超省電力化されたWeb/動画配信/AIが動く日
piacerex
0
140
watsonx.data上のベクトル・データベース Milvusを見てみよう/20250418-milvus-dojo
mayumihirano
0
110
SREの視点で考えるSIEM活用術 〜AWS環境でのセキュリティ強化〜
coconala_engineer
1
290
Featured
See All Featured
A Tale of Four Properties
chriscoyier
158
23k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
30
2k
Building an army of robots
kneath
304
45k
YesSQL, Process and Tooling at Scale
rocio
172
14k
Designing Experiences People Love
moore
141
24k
Building Applications with DynamoDB
mza
94
6.3k
Code Reviewing Like a Champion
maltzj
522
40k
The Cult of Friendly URLs
andyhume
78
6.3k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Being A Developer After 40
akosma
91
590k
Documentation Writing (for coders)
carmenintech
69
4.7k
Code Review Best Practice
trishagee
67
18k
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は コーディングにおける銀の弾丸ではない 効果を発揮しそうな要件を見極めて 使っていきましょう
完