SlideShare a Scribd company logo
1 of 119
Goのサーバサイド実装における
レイヤ設計とレイヤ内実装について
考える
自己紹介
twitter
pospome
読み方
ポスポメ
職種
サーバサイドエンジニア
興味
クラス設計全般, DDD
ここら辺の技術に興味ある方は
  フォローしてくださると嬉しいです
発表する前に
・基本的にはレイヤ構造の話なのでDDDは関係ありませんが、
 微妙にDDDに関する単語や概念が出てきます
・マサカリ歓迎です
 「これは良い」「これは間違っている」などなど
 twitterでご意見いただければと思います
・後からスライド単体でも見直せるように文字多めです
目次
レイヤとは?
レイヤ設計について
レイヤ内実装について
目次
レイヤとは?
レイヤ設計について
レイヤ内実装について
コードを性質別にザックリと分けるモジュール戦略
代表例
・レイヤアーキテクチャ
・クリーンアーキテクチャ
・ヘキサゴナルアーキテクチャ
なぜレイヤが必要なのか?
説明不要だと思いますが・・・
・コードの依存関係を整理できる
・レイヤ間の差し替えを担保することができる
・レイヤ内のパッケージの凝集度を高めることができる
コード量が多くなってくると、
何がどこに影響するのか? を管理しづらくなり、
可読性も低下する
システムの保守性が低下しないようにするためのアプローチ
目次
レイヤとは?
レイヤ設計について
レイヤ内実装について
どういうレイヤ設計が適切なのか?
UI
レイヤアーキテクチャ(DIP適用)
アプリケーション
モデル
インフラストラクチャ
クリーンアーキテクチャ
Entities
Use Cases
Controllers, Gateways, Presenters
UI, DB, Web, Devices
ヘキサゴナルアーキテクチャ
Application
Adapter
大体どれも似たような感じなので、
そんなに悩まなくて良い印象
世の中には色々あるが、
大体どれを採用しても以下は担保されると思う
・モデルはどのレイヤにも依存しない
・似たような責務を持ったレイヤがある
・各レイヤ間の依存が単一方向依存になる
pospome が個人的に利用するレイヤ構造を紹介
handler
レイヤ構造
usecase
domain
infra
レイヤ(ディレクトリ)構造
handler
presenter
usecase
input
output
domain
model
service
repository
registry
infra
dao
mail
レイヤアーキテクチャをベースに
色々付け足したもの
クリーンアーキテクチャになってきた感があるので、
それに寄せようかと思っている
レイヤ(ディレクトリ)構造
adapter
handler
presenter
cmd
presenter
registry
usecase
input
output
domain
model
service
repository
infra
dao
mail
adapter を置くこともあるが、
Webサーバ実装だと handler があれば事足りる事が多
いので、
最近は handler だけ置いて、
adapter 欲しくなったら adapter を置いている
ネット上にレイヤ設計の記事が溢れているので、
それを参考に考えていくことができる
チームのスキルや要件によって
多少カスタマイズすると思うが、
大体同じような構造に落ち着くと思う
今さらレイヤ構造について説明しても仕方ない
各レイヤの具体的な実装に踏み込んだ話にしたい
目次
レイヤとは?
レイヤ設計について
レイヤ内実装について(今日のメイン)
以下を保証できない実装になっていると
レイヤ構造を導入した意味がない
・コードの依存関係を整理できる
・レイヤ間の差し替えを担保することができる
・レイヤ内のパッケージの凝集度を高めることができる
実装する時に意識している点を紹介
・レイヤ責務と改修起点とレイヤで扱う概念
・レイヤ間の実装差し替え
・infraを抽象として扱う
・infraの抽象とレイヤ構造
・infra実装のDI
・レイヤの必要性
・レイヤ責務と改修起点とレイヤで扱う概念
・レイヤ間の実装差し替え
・infraを抽象として扱う
・infraの抽象とレイヤ構造
・infra実装のDI
・レイヤの必要性
handler
レイヤ構造
usecase
domain
infra
レイヤには責務がある
handler
・HTTPを受け取り、usecase を呼び、結果を出力する
・結果は json だったり、HTML だったりする
・基本的に薄い実装になるが、
 HTTPの body, header のパース処理など
 場合によっては薄くならない可能性もある
usecase
・いわゆるアプリケーションレイヤ
・システム仕様上のユースケースを表現する
 ex. ユーザー登録、アイテム消費、チーム一覧表示
・handler から呼び出される
 1つの handler に対応する専用 usecase が1つ存在する
・基本的に domain を触る
domain
・いわゆるモデルレイヤ
・ドメイン(業務領域)に関する値と振る舞いを持つ
・他のレイヤに依存してはいけない
・DDDの実装パターン別に model, repository, service
 というパッケージが存在する
infra
・技術的関心事を扱うレイヤ
 ex. DB, Mail, MessageQueue
・直接 handler, usecase から呼ばれることもあるが、
 基本的に domain の interface によって抽象化される
レイヤの責務
handler
HTTPを受け取り、usecase を呼び、結果を出力する
usecase
システムのユースケースを満たす処理の流れを実装する
domain(model)
システムが扱う業務領域に関するコードを置く
infra
具体的な技術に関するコードを置く
レイヤの責務 = 改修起点
・HTTP POST が PUT に変わる
 →handler が起点
・データ保存先が RDB から API になる
 →infra が起点
・ユーザー登録後にメール送信する処理を追加する
 →model が起点
handler
改修起点から他レイヤに改修影響が波及していくはず
usecase
domain
infra
責務があいまいだとどうなる?
例えば
domainレイヤまで http.Request を引き回してしまう場合
type User struct {
Name string
Address string
}
func NewUser(r *http.Request) *User {
//省略
}
http.Request を元に User モデルを生成してしまうかも
handler
HTTPに関する変更が全レイヤに影響する可能性
usecase
domain
infra
HTTP を domain まで引き回してしまうと、
HTTP を触るコードが各レイヤに散ってしまう
レイヤのコード量に応じて、
実装対象レイヤを変えるという謎実装になる可能性も
type User struct {
Name string
Address string
}
func NewUser(name, address string) *User {
//省略
}
domain で扱っても問題ない概念に依存させた方がいい
レイヤの責務があいまいだと改修影響を制御できなくなる
レイヤの責務を意識して、
レイヤ内で触るオブジェクト(概念)を見極める必要がある
・レイヤ責務と改修起点とレイヤで扱う概念
・レイヤ間の実装差し替え
・infraを抽象として扱う
・infraの抽象とレイヤ構造
・infra実装のDI
・レイヤの必要性
レイヤは依存する側が変更されても、
依存される側は影響を受けないのが基本
handler
レイヤ構造
usecase
domain
infra
func AddUser(r *http.Request) error {
//実装は省略するが、http.Request を触っている想定
}
例えば
usecase の引数で http.Request を受け取ってる場合に
デバッグツール用の コマンドラインツール でも
usecase を利用したいと思ったら???
handler
レイヤ構造
usecase
domain
infra
cmd
func AddUser(r *http.Request) error {
//実装は省略するが、http.Request を触っている想定
}
cmd で http.Request を生成する必要がある
わざわざ http.Request を生成するのは不自然では?
usecase が暗黙的に handler に依存している証拠
func AddUser(i input.AddUser) error {
//実装は省略する
}
個人的に usecase の引数は専用の struct にする
package input
type UserAdd struct {
Name string
Address string
TEL string
}
func (u *UserAdd) CreateUser() (*model.User, error) {
//Userモデルを生成する
}
input struct は可能な限り外部レイヤに依存しない値にする
モデルを生成するメソッドも生やす
package input
type UserAdd struct {
User *model.User
}
usecase に戻り値がある場合、
同じような理由で専用の struct を用意する
戻り値の struct はモデルを格納するだけの DTO
レイヤ(ディレクトリ)構造
handler
presenter
usecase
input
output
domain
model
service
repository
registry
infra
dao
mail
時間ないので細かく説明しません
ちなみに
input, output 用の struct は interface にしません
具体的な struct に依存させるようにしています
・外部レイヤに影響されない実装が前提
 影響されるのであれば、実装が間違っている
・振る舞いに着目し、抽象化するものでもない
 実装差し替えは不要
過度な抽象化はしない
handler が扱う HTTP という概念に
usecase が依存しているのが原因で
レイヤがレイヤとして機能していなかった
標準パッケージに依存しているコードは
一見問題ないように思えてしまうことがあるが、
そんなことはない
・レイヤ責務と改修起点とレイヤで扱う概念
・レイヤ間の実装差し替え
・infraを抽象として扱う
・infraの抽象とレイヤ構造
・infra実装のDI
・レイヤの必要性
技術的関心事を扱うコード群(infra)は interface で
差し替え可能にするテクニックは有名
具体的な実装に依存しないインターフェースを提供できる
結果的にテストもしやすくなる
機械的にできる実装パターン
interface は抽象として扱う対象がポイント
対象の粒度によってパッケージの安定度が変わる
type UserTable interface {
Get(id int) *model.User
Add(u *model.User) *model.User
Update(u *model.User) *model.User
Remove(id int) *model.User
}
例えば、ユーザーの CRUD を表現する interface
一見問題なさそう?
type UserTable interface {
Get(id int) *model.User
Add(u *model.User) *model.User
Update(u *model.User) *model.User
Remove(id int) *model.User
}
UserTable = RDBの概念?
実装が RDB から変更されると interface 名を修正する?
interface の実装内で memcache とか叩けない?
interface によって抽象を扱うはずが、
命名が具体的なためにいまいち抽象とし扱いきれていない
この interface が扱っている抽象は
RDBにおけるCRUDの実装であって、
CRUD自体を抽象として扱っているわけではない
仮に RDB の CRUD 実装のみを抽象化する目的であれば、
この interface 粒度でも問題ない
ex. MySQL → PostgreSQL
type UserRepository interface {
Get(id int) *model.User
Add(u *model.User) *model.User
Update(u *model.User) *model.User
Remove(id int) *model.User
}
抽象的な Repository という命名に変更する
CRUD できれば実装は何でもいい
type UserRepository interface {
Get(con *db.Con, id int) *model.User
}
interface の引数、戻り値にも注意
例えば、引数にRDBのコネクションを渡してしまうと、
interface とRDBの結合度が高くなる
・interface を実装する struct に持たせる
・実装内に持たせる
*とはいえ、引数とか戻り値は特定の技術に依存してしまうこともあるのでな
かなか面倒だったりする。
以下に気をつけることでinterface の安定度が増し、
interface を利用するコードは変更に強くなる
・interface の命名粒度
・interface の引数と戻り値
*変更に強くなるだけで変更不要になるわけではない
「テストをしやすくする」という目的しか持たない人が
interface を扱うとこーなる印象
interface を扱う以上、
何を抽象として表現するかを意識するべき
おまけ
Repository 以外の技術的関心事が忘れられがち???
ex. Mail
技術的関心事を interface として扱うのであれば、
具体的な命名は避けた方がいい
SendGrid interface -> Mail interface
レイヤ(ディレクトリ)構造
handler
presenter
usecase
input
output
domain
model
service
repository
registry
infra
dao
mail
service or infra に interface を置く
細かい説明は省略
repository 以外の技術的関心事も
忘れないようにした方がいいよというお話
おまけ
infra で発生する error を
そのままハンドリングするのはやめた方がいい
package dao
type User struct {
}
func (user *User) Get(id int) (*model.User, error) {
return memcache.Get(id)
}
memcache にアクセスする DAO のコード
これだけだと問題なさそう
func GetUser(id int) (*model.User, error) {
u, err := dao.User{}.Get(id)
if err == memcache.ErrCacheMiss {
}
}
DAOを利用する側のコードが
memcache のエラーに依存してしまっている
interface 上は error で抽象化されているが、
利用する側のコードで依存してしまうこともあるので注意
package dao
type User struct {
}
func (user *User) Get(id int) (*model.User, error) {
u, err := memcache.Get(id)
if err == memcache.ErrCacheMiss {
return domain.ErrNoSuchModel
}
}
アプリケーション独自の error でラップすればOK
独自 error の定義で気をつける点は以下
・error を捕捉するレイヤにとって問題ない抽象度にする
 独自例外で ErrMemcache 的なやつを
 再定義しても意味がない
・各レイヤの責務ごとに適切な error を考える
・ラップする必要のない error はラップしない
・レイヤ責務と改修起点とレイヤで扱う概念
・レイヤ間の実装差し替え
・infraを抽象として扱う
・infraの抽象とレイヤ構造
・infra実装のDI
・レイヤの必要性
レイヤ構造を扱うのであれば、
技術的関心事に関する interface と
その実装の置き場所は意識しなければならない
type UserRepository interface {
Get(id int) *model.User
}
UserRepository(interface)と・・・
package dao
type User struct {
}
func (user *User) Get(id int) *model.User {
}
UserRepository(interface)の実装をどこに置く?
レイヤの責務
handler
HTTPを受け取り、usecase を呼び、結果を出力する
usecase
システムのユースケースを満たす処理の流れを実装する
domain(model)
システムが扱う業務領域に関するコードを置く
 ↑
 interfaceを置く
infra
具体的な技術に関するコードを置く
 ↑
 interfaceの実装を置く
handler
infra を domain に依存させている
usecase
domain
infra
interfaceと実装を domain に置く場合
・実装が domain に存在するので、
 MySQL を WebAPI にするという変更で
 domain に修正が必要になる
・domain は業務領域に関するコードを置くレイヤなので、
 MySQL が業務領域に関するものでない限り、
 domain に修正が入るのはおかしい
 修正は infra に閉じられるべき
・実装が infra に存在すれば、これを回避できる
interfaceと実装を infra に置く場合
・システム仕様に「ユーザー削除」が追加されると、
 infra に修正が必要になる
・業務領域に関するコードを置くレイヤである domain に
 影響がない
・本来であれば、domain に修正影響が発生し、
 その影響が infra に伝搬するはず
・interface が domain に存在すれば、これを回避できる
レイヤの責務を考慮すると、
interface は domain に置き、
実装は infra に置くことになる
ただし、
interface、実装共に infra に置く場合もあるし、
interface すら用意しない場合もある
なかなか難しいところ
レイヤの責務と抽象の必要性を考慮して
一貫性を持った実装方針であれば問題ないはず
・レイヤ責務と改修起点とレイヤで扱う概念
・レイヤ間の実装差し替え
・infraを抽象として扱う
・infraの抽象とレイヤ構造
・infra実装のDI
・レイヤの必要性
infra に実装を置き、domain に interface を置き、
実際に usecase から interface を利用してみる
interface と実装を結びつけるために registry を利用する
レイヤ(ディレクトリ)構造
handler
presenter
usecase
input
output
domain
model
service
repository
registry
infra
dao
mail
package registry
type Repository interface {
NewUser() reposiroty.User
}
type repositoryImpl struct {
}
func NewRepository() Repository {
return &repositoryImpl{}
}
//引数は interface だが、戻り値は実装になっている
func (r *repositoryImpl) NewUser() reposiroty.User {
return &dao.User{}
}
repository 用の registry で
各種 repository を取得できる
registry 自体を interface にする
ことで、テスト時に必要なメ
ソッドだけ実装すればいい
service 用の registry もある
レイヤ(ディレクトリ)構造
handler
presenter
usecase
input
output
domain
model
service
repository
registry
repository.go
service.go
infra
dao
mail
メソッド名で取得対象の repository を明示できるのも利点
実装の関係上、
以下のような Master, Slave の使い分けが必要な場合に役立つ
package registry
func (r *repositoryImpl) NewUserMaster() reposiroty.User {
return &dao.User{“master”}
}
func (r *repositoryImpl) NewUserSlave() reposiroty.User {
return &dao.User{“slave”}
}
具体的な実装を紹介
package handler
type User struct {
repo registry.Repository
}
func NewUser(repo registry.Repository) *User {
return &User{repo}
}
func (u *User) Add(w http.ResponseWriter, r *http.Request) {
userRepo := u.repo.NewUser()
userUsecase := usecase.NewUser(userRepo)
}
handler は registry を持ち、
registry から repository を取得することができる
取得した repository は usecase にセットする
package main
func init() {
repo := registry.NewRepository()
userHandler := handler.NewUser(repo)
http.HandleFunc("/add", userHandler.Add())
}
handler の registry は main でセットする
*GAEのコードなので、純粋なGo実装とは main の書き方が異なります
package usecase
type User struct {
userRepo repository.User
}
func NewUser(u repository.User) *User {
return &User{u}
}
func (user *User) Add() *output.UserAdd {
// repository の Add() を呼ぶことができる
user.userRepo.Add()
}
usecase は repository を持つ
registry を持たない
handler
handler は registry に依存し、
registry は infra に依存している
usecase
domain
infra
registry
package usecase
type User struct {
userRepo repository.User
}
func NewUser(u repository.User) *User {
return &User{u}
}
func (user *User) Add() *output.UserAdd {
// repository の Add() を呼ぶことができる
user.userRepo.Add()
}
なぜ usecase は registry を持たない????
usecase が registry を持つと usecase が依存する repository,
service が大きくなってしまう
依存が明確ではなくなってしまう
usecase は処理の流れを表現するので、
サービスロケータに依存させたくない
package handler
type User struct {
repo registry.Repository
}
func NewUser(repo registry.Repository) *User {
return &User{repo}
}
func (u *User) Add(w http.ResponseWriter, r *http.Request) {
userRepo := u.repo.NewUser()
userUsecase := usecase.NewUser(userRepo)
}
なぜ handler は registry を持つのか?
本来であれば、具体的な repository, service を
DIする方がいーとは思うけど
handler は domain に依存しないので、
registry に依存させても問題なさそう
usecase で利用する repository, service に引っ張られて
main & handlerを修正するのが面倒
要は実害なさそうだし、
面倒だから registry を突っ込んでる
こんな感じで infra の実装と domain の interface を
紐付けています
おまけ
package usecase
type User struct {
userRepo repository.User
}
func NewUser(u repository.User) *User {
return &User{u}
}
func (user *User) Add() *output.UserAdd {
// repository の Add() を呼ぶことができる
user.userRepo.Add()
}
以下の実装だと、
User の CRUD を User struct に紐付ける感じになり、
User struct が持つ repository, service が肥大化しそうなので、
input struct に持たせよーかなと思ってる
・レイヤ責務と改修起点とレイヤで扱う概念
・レイヤ間の実装差し替え
・infraを抽象として扱う
・infraの抽象とレイヤ構造
・infra実装のDI
・レイヤの必要性
今まで説明してきたポイントを
考えながら実装できていますか?
「これ考えてなかったな」とかありませんか?
レイヤ実装は予想以上に難易度が高い
システム仕様の複雑さと規模に見合ったリターンを
得ることができなければ
単なる複雑なアーキテクチャで終わってしまう
理解した気で導入すると確実に失敗するので、
事前にプロトタイプ実装とかした方がいいです
最初は薄いレイヤ構造にして、
耐えられなくなってから改修すればいいのでは?
あとから改修はなかなか上手くいかない印象
・改修範囲が大きい
・コードが密結合すぎてレイヤに散らすことができない
・改修のタイミングでレイヤの学習コストがかかる
 チームにレイヤ知識があれば、問題ないですが
・既存コードがカオスすぎて触りたくない
・新規コードのみレイヤ構造を導入すると、
 場所によってアーキテクチャが異なってしまう
システムとチームは時間と共に成長するので、
初期実装時に適切なレイヤを選定するのはなかなか難しい
色々な要素に左右されてしまう
・チームメンバーのレベル
・チームの規模
・実装言語の仕様 & フレームワークとの相性
・システム仕様
伝えたいこと
今回の発表が皆さんのチームにとって最適とは限らない
レイヤ設計が必ずしも必要とは限らない
まとめ
・レイヤ設計におけるルールを可能な限り紹介したつもり
・それらを時間内に全て説明できないので、
 スライド内の記述には正確ではない点がある
 ex. レイヤの依存関係の図は正確ではない
・それでもなるべく矛盾しないように作ったつもり
・レイヤ構造は実装によって破綻する
・破綻しててもコードは動くのでなかなか気づきにくい
・このスライドがあらゆるケースで正しい保証もない
・レイヤの責務、依存方向を意識してコードを書こう
・レイヤ関連の実装パターンは機械的に扱えるので、
 エンジニアの実装スキルとしてはあまり価値がないと思う
 知ってればいいだけ
・実装スキルが試されるのはドメインレイヤを
 どう実装するかだと思っている
・今日は時間なくて、そーゆーところには一切触れてない
・どこかで話す機会あれば話します
おわり

More Related Content

What's hot

SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021Hiroshi Tokumaru
 
実運用して分かったRabbit MQの良いところ・気をつけること #jjug
実運用して分かったRabbit MQの良いところ・気をつけること #jjug実運用して分かったRabbit MQの良いところ・気をつけること #jjug
実運用して分かったRabbit MQの良いところ・気をつけること #jjugYahoo!デベロッパーネットワーク
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織Takafumi ONAKA
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)Yoshitaka Kawashima
 
組織にテストを書く文化を根付かせる戦略と戦術
組織にテストを書く文化を根付かせる戦略と戦術組織にテストを書く文化を根付かせる戦略と戦術
組織にテストを書く文化を根付かせる戦略と戦術Takuto Wada
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)Takuto Wada
 
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門Tadahiro Ishisaka
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメYoji Kanno
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 
イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意Yoshitaka Kawashima
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法についてYuji Otani
 
MySQLで論理削除と正しく付き合う方法
MySQLで論理削除と正しく付き合う方法MySQLで論理削除と正しく付き合う方法
MySQLで論理削除と正しく付き合う方法yoku0825
 
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかDDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかKoichiro Matsuoka
 
コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」Masahito Zembutsu
 
Mercari JPのモノリスサービスをKubernetesに移行した話 PHP Conference 2022 9/24
Mercari JPのモノリスサービスをKubernetesに移行した話 PHP Conference 2022 9/24Mercari JPのモノリスサービスをKubernetesに移行した話 PHP Conference 2022 9/24
Mercari JPのモノリスサービスをKubernetesに移行した話 PHP Conference 2022 9/24Shin Ohno
 
ドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解するドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解する増田 亨
 
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Springドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring増田 亨
 
Pythonによる黒魔術入門
Pythonによる黒魔術入門Pythonによる黒魔術入門
Pythonによる黒魔術入門大樹 小倉
 

What's hot (20)

SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021SPAセキュリティ入門~PHP Conference Japan 2021
SPAセキュリティ入門~PHP Conference Japan 2021
 
実運用して分かったRabbit MQの良いところ・気をつけること #jjug
実運用して分かったRabbit MQの良いところ・気をつけること #jjug実運用して分かったRabbit MQの良いところ・気をつけること #jjug
実運用して分かったRabbit MQの良いところ・気をつけること #jjug
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織
 
イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)イミュータブルデータモデル(世代編)
イミュータブルデータモデル(世代編)
 
Marp Tutorial
Marp TutorialMarp Tutorial
Marp Tutorial
 
組織にテストを書く文化を根付かせる戦略と戦術
組織にテストを書く文化を根付かせる戦略と戦術組織にテストを書く文化を根付かせる戦略と戦術
組織にテストを書く文化を根付かせる戦略と戦術
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門
関数型・オブジェクト指向 宗教戦争に疲れたなたに送るGo言語入門
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意
 
Docker Tokyo
Docker TokyoDocker Tokyo
Docker Tokyo
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
MySQLで論理削除と正しく付き合う方法
MySQLで論理削除と正しく付き合う方法MySQLで論理削除と正しく付き合う方法
MySQLで論理削除と正しく付き合う方法
 
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのかDDDのモデリングとは何なのか、 そしてどうコードに落とすのか
DDDのモデリングとは何なのか、 そしてどうコードに落とすのか
 
コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」
 
Mercari JPのモノリスサービスをKubernetesに移行した話 PHP Conference 2022 9/24
Mercari JPのモノリスサービスをKubernetesに移行した話 PHP Conference 2022 9/24Mercari JPのモノリスサービスをKubernetesに移行した話 PHP Conference 2022 9/24
Mercari JPのモノリスサービスをKubernetesに移行した話 PHP Conference 2022 9/24
 
ドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解するドメイン駆動設計 基本を理解する
ドメイン駆動設計 基本を理解する
 
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Springドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
ドメインロジックに集中せよ 〜ドメイン駆動設計 powered by Spring
 
Pythonによる黒魔術入門
Pythonによる黒魔術入門Pythonによる黒魔術入門
Pythonによる黒魔術入門
 

Viewers also liked

Goji とレイヤ化アーキテクチャ
Goji とレイヤ化アーキテクチャGoji とレイヤ化アーキテクチャ
Goji とレイヤ化アーキテクチャShiroyagi Corporation
 
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -Shuji Kikuchi
 
Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4Fabien Potencier
 
やさしいGemパッチの作り方
やさしいGemパッチの作り方やさしいGemパッチの作り方
やさしいGemパッチの作り方Maki Toshio
 
Test::Kantan - Perl and Testing
Test::Kantan - Perl and TestingTest::Kantan - Perl and Testing
Test::Kantan - Perl and TestingTokuhiro Matsuno
 
How to Begin to Develop Ruby Core
How to Begin to Develop Ruby CoreHow to Begin to Develop Ruby Core
How to Begin to Develop Ruby CoreHiroshi SHIBATA
 
Quine・難解プログラミングについて
Quine・難解プログラミングについてQuine・難解プログラミングについて
Quine・難解プログラミングについてmametter
 
Cookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
Cookpad 17 day Tech internship 2017 言語処理系入門 RubyをコンパイルしようCookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
Cookpad 17 day Tech internship 2017 言語処理系入門 RubyをコンパイルしようKoichi Sasada
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)Yoshitaka Kawashima
 
Esoteric, Obfuscated, Artistic Programming in Ruby
Esoteric, Obfuscated, Artistic Programming in RubyEsoteric, Obfuscated, Artistic Programming in Ruby
Esoteric, Obfuscated, Artistic Programming in Rubymametter
 
Perfect Norikra 2nd Season
Perfect Norikra 2nd SeasonPerfect Norikra 2nd Season
Perfect Norikra 2nd SeasonSATOSHI TAGOMORI
 
Ruby and Distributed Storage Systems
Ruby and Distributed Storage SystemsRuby and Distributed Storage Systems
Ruby and Distributed Storage SystemsSATOSHI TAGOMORI
 
片手間MySQLチューニング戦略
片手間MySQLチューニング戦略片手間MySQLチューニング戦略
片手間MySQLチューニング戦略yoku0825
 
If文から機械学習への道
If文から機械学習への道If文から機械学習への道
If文から機械学習への道nishio
 
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3 データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3 Hiroshi Ito
 

Viewers also liked (16)

Goji とレイヤ化アーキテクチャ
Goji とレイヤ化アーキテクチャGoji とレイヤ化アーキテクチャ
Goji とレイヤ化アーキテクチャ
 
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
[AKIBA.AWS] EC2の基礎 - パフォーマンスを100%引き出すオプション設定 -
 
Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4
 
やさしいGemパッチの作り方
やさしいGemパッチの作り方やさしいGemパッチの作り方
やさしいGemパッチの作り方
 
Test::Kantan - Perl and Testing
Test::Kantan - Perl and TestingTest::Kantan - Perl and Testing
Test::Kantan - Perl and Testing
 
RSpec Performance Turning
RSpec Performance TurningRSpec Performance Turning
RSpec Performance Turning
 
How to Begin to Develop Ruby Core
How to Begin to Develop Ruby CoreHow to Begin to Develop Ruby Core
How to Begin to Develop Ruby Core
 
Quine・難解プログラミングについて
Quine・難解プログラミングについてQuine・難解プログラミングについて
Quine・難解プログラミングについて
 
Cookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
Cookpad 17 day Tech internship 2017 言語処理系入門 RubyをコンパイルしようCookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
Cookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう
 
イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)イミュータブルデータモデル(入門編)
イミュータブルデータモデル(入門編)
 
Esoteric, Obfuscated, Artistic Programming in Ruby
Esoteric, Obfuscated, Artistic Programming in RubyEsoteric, Obfuscated, Artistic Programming in Ruby
Esoteric, Obfuscated, Artistic Programming in Ruby
 
Perfect Norikra 2nd Season
Perfect Norikra 2nd SeasonPerfect Norikra 2nd Season
Perfect Norikra 2nd Season
 
Ruby and Distributed Storage Systems
Ruby and Distributed Storage SystemsRuby and Distributed Storage Systems
Ruby and Distributed Storage Systems
 
片手間MySQLチューニング戦略
片手間MySQLチューニング戦略片手間MySQLチューニング戦略
片手間MySQLチューニング戦略
 
If文から機械学習への道
If文から機械学習への道If文から機械学習への道
If文から機械学習への道
 
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3 データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
データ履歴管理のためのテンポラルデータモデルとReladomoの紹介 #jjug_ccc #ccc_g3
 

Similar to Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える

Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)VOYAGE GROUP
 
社内 DDD 勉強会 #2
社内 DDD 勉強会 #2社内 DDD 勉強会 #2
社内 DDD 勉強会 #2shingo suzuki
 
ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計Tadayoshi Sato
 
Application Architecture for Enterprise Win Store Apps with DDD Pattern
Application Architecture for Enterprise Win Store Apps with DDD PatternApplication Architecture for Enterprise Win Store Apps with DDD Pattern
Application Architecture for Enterprise Win Store Apps with DDD PatternAtsushi Kambara
 
20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdfKazuaki Ishizaki
 
[visasQ] 2017-04-26 ビザスクを支えるアーキテクチャ
[visasQ] 2017-04-26 ビザスクを支えるアーキテクチャ[visasQ] 2017-04-26 ビザスクを支えるアーキテクチャ
[visasQ] 2017-04-26 ビザスクを支えるアーキテクチャvisasQ - ビザスク
 
アプリケーションコードにおける技術的負債について考える
アプリケーションコードにおける技術的負債について考えるアプリケーションコードにおける技術的負債について考える
アプリケーションコードにおける技術的負債について考えるpospome
 
fastTextの実装を見てみた
fastTextの実装を見てみたfastTextの実装を見てみた
fastTextの実装を見てみたYoshihiko Shiraki
 
ソフトウェア設計原則【SOLID】を学ぶ #2 インターフェイス分離の原則.pdf
ソフトウェア設計原則【SOLID】を学ぶ #2 インターフェイス分離の原則.pdfソフトウェア設計原則【SOLID】を学ぶ #2 インターフェイス分離の原則.pdf
ソフトウェア設計原則【SOLID】を学ぶ #2 インターフェイス分離の原則.pdf耕二 阿部
 
【JJUG CCC 2016 Fall 公開版】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
【JJUG CCC 2016 Fall 公開版】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜【JJUG CCC 2016 Fall 公開版】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
【JJUG CCC 2016 Fall 公開版】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜Fumiyasu Sumiya
 
プログラマ人生論
プログラマ人生論プログラマ人生論
プログラマ人生論ymmt
 
20100324 勉強会資料(ドメイン駆動)
20100324 勉強会資料(ドメイン駆動)20100324 勉強会資料(ドメイン駆動)
20100324 勉強会資料(ドメイン駆動)Masayuki Kanou
 
Janog31 bof-pattern-sasaki-01
Janog31 bof-pattern-sasaki-01Janog31 bof-pattern-sasaki-01
Janog31 bof-pattern-sasaki-01Ken SASAKI
 
Developer's Summit 夏 EnterpriseTED 資料
Developer's Summit 夏 EnterpriseTED 資料Developer's Summit 夏 EnterpriseTED 資料
Developer's Summit 夏 EnterpriseTED 資料Atsushi Takayasu
 
20141224 titech lecture_ishizaki_public
20141224 titech lecture_ishizaki_public20141224 titech lecture_ishizaki_public
20141224 titech lecture_ishizaki_publicKazuaki Ishizaki
 
パターンでわかる! .NET Coreの非同期処理
パターンでわかる! .NET Coreの非同期処理パターンでわかる! .NET Coreの非同期処理
パターンでわかる! .NET Coreの非同期処理Kouji Matsui
 
Asakusa Enterprise Batch Processing Framework for Hadoop
Asakusa Enterprise Batch Processing Framework for HadoopAsakusa Enterprise Batch Processing Framework for Hadoop
Asakusa Enterprise Batch Processing Framework for HadoopTakashi Kambayashi
 
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)NTT DATA Technology & Innovation
 
Mvc conf session_2_shibamura
Mvc conf session_2_shibamuraMvc conf session_2_shibamura
Mvc conf session_2_shibamuraHiroshi Okunushi
 

Similar to Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える (20)

Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)Phpではじめるオブジェクト指向(公開用)
Phpではじめるオブジェクト指向(公開用)
 
社内 DDD 勉強会 #2
社内 DDD 勉強会 #2社内 DDD 勉強会 #2
社内 DDD 勉強会 #2
 
ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計
 
Application Architecture for Enterprise Win Store Apps with DDD Pattern
Application Architecture for Enterprise Win Store Apps with DDD PatternApplication Architecture for Enterprise Win Store Apps with DDD Pattern
Application Architecture for Enterprise Win Store Apps with DDD Pattern
 
20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf
 
[visasQ] 2017-04-26 ビザスクを支えるアーキテクチャ
[visasQ] 2017-04-26 ビザスクを支えるアーキテクチャ[visasQ] 2017-04-26 ビザスクを支えるアーキテクチャ
[visasQ] 2017-04-26 ビザスクを支えるアーキテクチャ
 
アプリケーションコードにおける技術的負債について考える
アプリケーションコードにおける技術的負債について考えるアプリケーションコードにおける技術的負債について考える
アプリケーションコードにおける技術的負債について考える
 
fastTextの実装を見てみた
fastTextの実装を見てみたfastTextの実装を見てみた
fastTextの実装を見てみた
 
ソフトウェア設計原則【SOLID】を学ぶ #2 インターフェイス分離の原則.pdf
ソフトウェア設計原則【SOLID】を学ぶ #2 インターフェイス分離の原則.pdfソフトウェア設計原則【SOLID】を学ぶ #2 インターフェイス分離の原則.pdf
ソフトウェア設計原則【SOLID】を学ぶ #2 インターフェイス分離の原則.pdf
 
【JJUG CCC 2016 Fall 公開版】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
【JJUG CCC 2016 Fall 公開版】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜【JJUG CCC 2016 Fall 公開版】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
【JJUG CCC 2016 Fall 公開版】ドメイン駆動設計とscala 〜既存プロジェクトへの適用〜
 
プログラマ人生論
プログラマ人生論プログラマ人生論
プログラマ人生論
 
20100324 勉強会資料(ドメイン駆動)
20100324 勉強会資料(ドメイン駆動)20100324 勉強会資料(ドメイン駆動)
20100324 勉強会資料(ドメイン駆動)
 
Janog31 bof-pattern-sasaki-01
Janog31 bof-pattern-sasaki-01Janog31 bof-pattern-sasaki-01
Janog31 bof-pattern-sasaki-01
 
Developer's Summit 夏 EnterpriseTED 資料
Developer's Summit 夏 EnterpriseTED 資料Developer's Summit 夏 EnterpriseTED 資料
Developer's Summit 夏 EnterpriseTED 資料
 
20141224 titech lecture_ishizaki_public
20141224 titech lecture_ishizaki_public20141224 titech lecture_ishizaki_public
20141224 titech lecture_ishizaki_public
 
最速C# 7.x
最速C# 7.x最速C# 7.x
最速C# 7.x
 
パターンでわかる! .NET Coreの非同期処理
パターンでわかる! .NET Coreの非同期処理パターンでわかる! .NET Coreの非同期処理
パターンでわかる! .NET Coreの非同期処理
 
Asakusa Enterprise Batch Processing Framework for Hadoop
Asakusa Enterprise Batch Processing Framework for HadoopAsakusa Enterprise Batch Processing Framework for Hadoop
Asakusa Enterprise Batch Processing Framework for Hadoop
 
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
 
Mvc conf session_2_shibamura
Mvc conf session_2_shibamuraMvc conf session_2_shibamura
Mvc conf session_2_shibamura
 

More from pospome

トランザクションスクリプトのすすめ
トランザクションスクリプトのすすめトランザクションスクリプトのすすめ
トランザクションスクリプトのすすめpospome
 
MicroServices & APIs
MicroServices & APIsMicroServices & APIs
MicroServices & APIspospome
 
どこに何を書くのか?
どこに何を書くのか?どこに何を書くのか?
どこに何を書くのか?pospome
 
Datastore/Go のデータ設計と struct の振る舞いについて
Datastore/Go のデータ設計と struct の振る舞いについてDatastore/Go のデータ設計と struct の振る舞いについて
Datastore/Go のデータ設計と struct の振る舞いについてpospome
 
Goのシンプルさについて
GoのシンプルさについてGoのシンプルさについて
Goのシンプルさについてpospome
 
パッケージの循環参照
パッケージの循環参照パッケージの循環参照
パッケージの循環参照pospome
 
Controllerのbefore_actionにおける インスタンス変数セットについて
Controllerのbefore_actionにおける インスタンス変数セットについてControllerのbefore_actionにおける インスタンス変数セットについて
Controllerのbefore_actionにおける インスタンス変数セットについてpospome
 
REST API のコツ
REST API のコツREST API のコツ
REST API のコツpospome
 
サーバサイドNodeの使い道
サーバサイドNodeの使い道サーバサイドNodeの使い道
サーバサイドNodeの使い道pospome
 

More from pospome (9)

トランザクションスクリプトのすすめ
トランザクションスクリプトのすすめトランザクションスクリプトのすすめ
トランザクションスクリプトのすすめ
 
MicroServices & APIs
MicroServices & APIsMicroServices & APIs
MicroServices & APIs
 
どこに何を書くのか?
どこに何を書くのか?どこに何を書くのか?
どこに何を書くのか?
 
Datastore/Go のデータ設計と struct の振る舞いについて
Datastore/Go のデータ設計と struct の振る舞いについてDatastore/Go のデータ設計と struct の振る舞いについて
Datastore/Go のデータ設計と struct の振る舞いについて
 
Goのシンプルさについて
GoのシンプルさについてGoのシンプルさについて
Goのシンプルさについて
 
パッケージの循環参照
パッケージの循環参照パッケージの循環参照
パッケージの循環参照
 
Controllerのbefore_actionにおける インスタンス変数セットについて
Controllerのbefore_actionにおける インスタンス変数セットについてControllerのbefore_actionにおける インスタンス変数セットについて
Controllerのbefore_actionにおける インスタンス変数セットについて
 
REST API のコツ
REST API のコツREST API のコツ
REST API のコツ
 
サーバサイドNodeの使い道
サーバサイドNodeの使い道サーバサイドNodeの使い道
サーバサイドNodeの使い道
 

Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える

Editor's Notes

  1. 発表する前にということですが、 1つ目、DDD関係ない内容ですが、 僕がDDD好きなこともあって、 若干DDDの概念が出てきます 2つ目、僕はマサカリ歓迎するタイプでして、 批判ツイートいただけると勉強になるので 是非マサカリ投げて下さい 3つ目、スライドは文字多めにしているので、 後から見返せるようにしています
  2. 目次こんな感じです
  3. では、進めていきます
  4. レイヤを言葉で表現すると、コードを性質別に分けるモジュール戦略だと思っています 最近だとクリーンアーキテクチャとかネットでよく見るので、サーバサイドとか golang とか関係なく、アプリケーションエンジニアであれば、皆さんご存知かと思います
  5. 説明するまでもないと思うんですが、 依存関係を整理できる 凝集度を高めることができるからですね
  6. つまり、システムの保守性が低下しないようにするためのアプローチだと思っています 2:00 5分伸びても大丈夫
  7. では、次に行きます
  8. で、結局どうすればいいの? って最初に考えると思います レイヤってたくさんあるんですよね
  9. あとは オニオンアーキテクチャ IODAアーキテクチャ なんてのもあります
  10. ただ、個人的にどれも大体同じなので、悩まなくていーかなと思っています
  11. なぜかというと、 結局、以下の3つは担保されるからですね。 レイヤとして最低限必要なポイントは抑えることができます。
  12. 図にするとこんな感じです handler, usecase, domain, infra があります
  13. これがディレクトリ構造で レイヤアーキテクチャがベースです
  14. こんな感じで handler 上位に adapter を置くこともあるんですけど、 面倒なのであんまりやってないですね
  15. ということで、 僕のレイヤ構造を紹介したんですけど、 ネット上の記事を見ると、 大体同じ感じになるので、 レイヤ設計自体には、あまり悩まないと思います
  16. 今更「クリーンアーキテクチャとは?」みたいなことを僕が説明しても、他の記事見た方が早いので、 今日は説明しません 一歩実装に踏み込んだ話をしようと思います 5:00
  17. レイヤ内実装が今日のメインです
  18. 最初にレイヤのメリットを説明したと思うのですが、 実装によっては、このメリットを失ってしまう可能性があります
  19. なので、今日はこのメリットを失わないために意識している点を紹介します
  20. こんな感じで紹介していきます。 今回の発表では具体的なコードベースでレイヤ設計における実装を丸っと紹介しようと思ったんですけど、レイヤのルールを理解しなければ、「なぜそういうコードになるのか?」という意図が伝わらないと思いました。 なので、今回はレイヤ内実装といいつつも、若干抽象的なレイヤにおけるルールを紹介していこうと思います。
  21. では、1つ目 「レイヤ責務と改修起点とレイヤで扱う概念」についてです。
  22. これは先ほど紹介した僕が採用しているレイヤ構造です。 これをベースに説明していきます。
  23. 当然ながらレイヤには責務があります ということで、僕のレイヤにおける責務をザックリ説明します。
  24. handler は単純ですね。 基本的にHTTPを触って、usecase を呼んで、その戻り値を結果として出力します。 基本的に薄い実装になるんですけど、場合によっては複雑になったりします。
  25. 次は usecase レイヤ。 これはいわゆるアプリケーションレイヤで、 命名はクリーンアーキテクチャから取りました。 アプリケーションっていう表現よりも、ユースケースって表現の方がしっくりくるので、こういった命名にしています。 責務としては、ドメインを触って仕様上のユースケースを実行するだけです。 特徴は handler と1対1になることです。
  26. 次はドメインレイヤですね。 いわゆるモデルを置くところでしょうか。 このレイヤの実装が保守性に大きく左右されます。反面、説明するのが難しいので、今回の発表ではほぼ取り上げません。
  27. 最後がインフラレイヤです。 ここは技術的関心事と呼ばれるコード群を置くところで、DBアクセスだったり、メール送信の具体的なロジックを配置します。
  28. ということで、ザックリまとめるとこんな感じです。 handler, usecase, domain , infra それぞれのレイヤに責務があります。
  29. そして、レイヤの責務は改修起点でもあります
  30. 例えば、HTTP POST が PUT に変わった場合は handler が改修起点になります その他も同様に改修起点が決まっています
  31. この図の矢印は各レイヤの依存方向を簡単に表しているのですが、 例えば、handler が改修起点になった場合、他のレイヤに修正が及ぶことはないです。 domain が改修起点になった場合、それに依存している全てのレイヤに修正が及ぶ可能性があります。 こう見ると、「domain が改修起点になると、影響が大きすぎる」と思うかもしれないですが、domain はシステム仕様そのものなので、一番影響力があって当たり前です。 そもそもレイヤ同士の依存を完全になくすことは不可能なので、「どこに影響があると、どこが変化するのか?」を自然な形に保つことしかできません。 そう考えると、この依存関係自体は妥当であると思います。
  32. では、責務があいまいだとどうなるでしょう?
  33. そうすると、本来 handler で完結するはずの改修が全レイヤに影響することになるかもしれません せっかくレイヤを導入したのに、全レイヤが改修対象になりえるのであれば、fat controller と変わりません。 なんとなーく分けてみたって感じですかね。
  34. そして、さらに、domain が HTTP を触ってしまうと、どのレイヤでも HTTP を触ることができてしまいます。 「どのレイヤでもHTTPを触っていい」という状況になると「usecase にこれ以上ロジック書きたくないから domein に書こう」というコード量に依存した実装になってしまう可能性があり、レイヤの責務が破綻していきます。
  35. じゃあ、どうするかというと、 string に依存させます。 domain 内で定義されている struct があれば、それに依存させても大丈夫です。
  36. ということで、 レイヤの責務を明確化し、 レイヤ内で触る概念を見極める必要があります。 11:30
  37. 次が2つ目「レイヤ間の実装差し替え」です。
  38. なので、この図でいうと、handler が他の何かに変わっても、usecase は影響を受けないはずです。
  39. 図で言うとこんな感じですね
  40. で、どうなるかというと、 http 関係ないコマンドラインツール で http.Request を生成する必要があるんですよ これって不自然ですよね? これは暗黙的に handler に依存していることになります。
  41. じゃあ、どーするのかというと、 個人的には専用の input struct を定義して、 それを引数にしています
  42. input struct は可能な限り外部レイヤの影響を受けない値で構成させます。 今回の例でいうと string で構成されています。 そして、モデルを生成するメソッドも実装しています。 このメソッドを実装するかどうかは好みですね。直接 usecase で input struct の値を触って、モデルを生成してもいーと思います。 usecase の引数は struct ではなく、string, int というプリミティブ型でもいーんですけど、引数が増えると面倒なので、struct にしています
  43. 戻り値も同じように専用の struct を作ります。 ここは意見が別れるところでもあるのですが、フィールドにはモデルを持たせています。 例えば、ユーザー登録の usecase の戻り値であれば、登録された User モデルのみ structに含めますし、 ユーザー一覧の usecase であれば、User モデルの配列や表示上必要になるフラグも含めます。
  44. なので、ディレクトリ構造には input, output が存在します。 時間足りなくなるので、細かくは説明しません。
  45. ちなみにですが、 input, output の struct は interface として定義しません。 これは抽象化が不要認識だからです。 依存させても問題ない実装にすべきだと考えています。
  46. ということで、 レイヤ間の差し替えを意識して引数、戻り値を定義しましょう。 標準パッケージに依存してるからOKとかではないです。 15:00
  47. 次で3つ目ですね。 「infraを抽象として扱う」です。
  48. DBアクセスなどの infra を interface で抽象化するテクニックは有名だと思っています。 機械的に適用できるパターンなので、 今更説明するまでもないと思っています。
  49. ただ、いくつかポイントがあってですね、interface が表現する対象の抽象度がそれに当たります。
  50. 例えば、ユーザーの CRUD を表現する interface これでも動作はするのですが・・・
  51. interface 名に table って付いちゃってるんですよね。 table って RDB の概念じゃないですか。 RDB から NoSQL になったら interface 名って変更するんですかね? RDBを抽象化した interface の実装内で、memcache とかのキャッシュにアクセスしていーんですかね?
  52. この例だと、あくまで RDB の実装を抽象化しているだけなので、いまいち抽象として機能してない感がある。
  53. 仮に、MySQL から ポスグレへの実装差し替えのみ想定して定義したのであれば、正当性があるのですが、 それって抽象として表現する粒度が細かいと思いませんか?
  54. じゃあ、どーすればいいかというと、Repository という抽象的な単語に差し替えます。 で、単語は別に DAO とかでもいーです。好きなやつにしてください。
  55. あと、引数、戻り値にも注意した方がいーですね。 RDBのコネクションとか持つと、結局依存してしまいます。 ただ、ここらへんをちゃんとやるのはなかなか面倒だったりするので、適度な妥協は必要です。
  56. ということで、interface の命名粒度と引数、戻り値に気を付けましょう。
  57. ネット上の interface による抽象化の記事ではテストがフォーカスされているが、 抽象として扱う以上、より適切な抽象粒度を選択する必要があります。
  58. で、おまけがあってですね・・・
  59. モデルの CRUD 以外の抽象って、忘れられている感じがするんですよね。 例えば、メールとかですね。
  60. モデルの CRUD 以外もちゃんと抽象化してあげた方がいーです。 メッセージ・キューとかメールですね。
  61. で、僕はモデルの CRUD 以外の技術的な interface は service もしくは infra っていうパッケージに置いています。 今回は説明してる時間ないので、 Repository 以外も考えた方がいーよってことが伝わればいーかなと思います。
  62. さらにおまけがあってですね・・・
  63. 21:00
  64. そして、4つ目は「infraの抽象とレイヤ構造」です。
  65. 先ほどの発表で interface について話したと思うんですけど、 その interface と、実装のですね、 レイヤ構造における置き場所を考えないとレイヤの責務があいまいになります。
  66. interface は先ほどお話した Repository で、
  67. 実装は interface を実装したものですね。 で、これをどこに置くのかと・・・
  68. 結論からいうと、domain に interface を置いて、 infra に実装を置きます
  69. つまり、infra を domain に依存させています。
  70. 仮に、interface と実装の両方を domain に置く場合、 技術的な改修で domain レイヤが影響を受けます。 domain レイヤは業務領域に関するロジックを持つレイヤなので、 技術的な改修で影響を受けるのは不自然です。 実装を infra に置くことで、これを回避できます
  71. 仮に、interface と実装の両方を infra に置く場合ですが、 システム仕様上、モデルの CRUD に関する仕様変更があった時に、infra しか影響を受けないことになります。 モデルの CRUD というシステムのコアな仕様を infra が握っていることになるので、 これも不自然ですね。 interface を domain に置けば、回避できます。
  72. ということで、 interface は domain に置き、 実装は infra に置いています。
  73. 24:40
  74. 「infra 実装の DI 」についてです。
  75. 先ほど技術的関心事に関するコードは interface を domain に置き、実装は infra に置くと説明しましたが、それらを紐付ける仕組みが必要です。 そのために registry という仕組みを利用しています。
  76. このように registry というディレクトリありますね。
  77. registry は repository, service を生成する factory object で、戻り値が domain の interface になっており、実際に return するのは infra の実装です。
  78. 僕は repository, service の2つで分けています。
  79. registry の目的としては interface と実装の紐付けなんですが、 具体的にどのような repository, service を取得するのかをメソッド名で明示できる利点もあります。 例えば、ソーシャルゲームだと、ユーザーが持っている課金周りのデータはスレーブ遅延の影響を受けないようにマスターから取得したいというケースがあります。
  80. ということで、registry の使い方をコードベースで説明します。
  81. まず、handler は registry を持ちます。 そして、registry から usecase に必要となる repository, service を選択して、usecase にセットします。
  82. hanlder の registry は main でセットします。
  83. そして、usecase は domain レイヤで定義されている repository, service を持つので、任意のタイミングでそれらを叩くことができます。
  84. つまり、registry, handler, infra で依存関係が生まれます。
  85. usecase は直接 domain を触るので、 依存関係をはっきりさせておきたいからです。 サービスロケータとして registry を持つと、 依存関係が大きくなりすぎてしまう。
  86. 直接 repository, service を触るようなレイヤでもないし、usecase が必要とする repository, service に引っ張られて handler を修正するのが面倒だからですね。 本来であれば、repository, service にちゃんと依存させた方がいーですね。
  87. ということで、こんな感じです。
  88. usecase の struct に repositoy, service を持たせると、メソッドが増えていくたびに必要となる repository, seriviceが増えていくので、結局メソッドと struct のフィールドの依存関係が整理できていない感じがするんですよ。 なので、input 用の struct にそれらを持たせて、usecase は関数にした方がいーかなと思っています。 30:00
  89. 最後は「レイヤの必要性」についてです
  90. みなさんは今まで説明してきたポイントを考えながらレイヤ実装していますか?
  91. 意外かもしれないですが、レイヤ実装は難易度が高いです。 僕はこういったレイヤ構造に関する知識があるので、他人が考えたオレオレレイヤ構造のように自分が知らないレイヤでも、それに合わせてコードを書くことができると思っているのですが、みんながみんなそーではないという認識を持った方がいい。 そして、そういった人たちからすると、レイヤ構造は単なる複雑なアーキテクチャになる可能性があります。
  92. そして、システム仕様、規模によっては、同じように単なる複雑なアーキテクチャになってしまう可能性があります。
  93. あとから改修はなかなか上手くいかないと思いますね。 この中でいうと、密結合はあるあるですね。 あと、既存コードを把握できてなくて、触れない、触りたくないというのもありますね。 新規コードのみレイヤ構造を導入っていうのもアリなんですけど、人によっては統一感を重視したいという意見もあるので、これもなかなか難しいですね。
  94. そもそも、レイヤの必要性って色々な要素に左右されるので、適切な選択は無理なのかなーと最近思っています。
  95. 33:00 ここは少し戻って説明してもいーかも
  96. 今日は限られた時間のなかで可能な限りレイヤ設計におけるルールを紹介しました。 そして、時間が足りないので、 正確に説明できなかった箇所もあります。 とはいえ、参考にしても問題ない内容にできたかなーとは思います。