ディレクトリ構成
package 分け方
独立性の高いコードをまとめる
package名は「何をするか?」がよくわかるもの
ある2つのファイルを分けて、さらに共通部分を切り分けた場合、共通ファイルとセットでimportしなければならず、独立性が保てない。
code: directory struct
common
\\セットでcommonをimport
sample1
\\セットでcommonをimport
sample2
packageは細かく分けすぎない
package名と識別子は重複してはいけない
中規模以下のAPIならルートディレクトリに直接展開してもよい
循環参照などをする必要がなくなり、参照が簡単になる。
中規模以上になるとごちゃごちゃして分かりにくくなる。
Railsのような構造にしてはいけない
controllers.UserControllerというように重複する可能性がある
循環参照を強いられる可能性がある
名前が明白ではない
より良い名前をつけるために
ルートパッケージはドメインタイプのために存在する
ドメインは、背後にある技術要素に依存しない物事のこと
ルートパッケージは外部のどのパッケージにも依存してはいけない
ユーザのデータを保持するUser構造体や、ユーザのデータをフェッチしたり保存したりするためのUserServiceインターフェイスなどの単純なデータ型をルートパッケージに配置する
code:go
package myapp
type User struct {
ID int
Name string
Address Address
}
type UserService interface {
User(id int) (*User, error)
Users() ([]*User, error)
CreateUser(u *User) error
DeleteUser(id int) error
}
依存関係ごとにサブパッケージをグルーピングする
ルートパッケージの外部への依存はサブパッケージに押し込める
サブパッケージはドメインと実装をつなぐアダプターとして存在する
例えば、UserServiceはPostgreSQLを背後に持つかもしれない。postgres.UserServiceの実装を提供するpostgresのサブパッケージをアプリケーションに導入することができる
code:go
package postgres
import (
"database/sql"
"github.com/myname/myapp"
_ "github.com/lib/pq"
)
// UserService represents a PostgreSQL implementation of myapp.UserService.
type UserService struct {
DB *sql.DB
}
// User returns a user for a given id.
func (s *UserService) User(id int) (*myapp.User, error) {
var u myapp.User
row := db.QueryRow(SELECT id, name FROM users WHERE id = $1, id)
if row.Scan(&u.ID, &u.Name); err != nil {
return nil, err
}
return &u, nil
}
サードパーティーの依存関係を分散させる
net/httpパッケージもただの1つの依存にすぎない。httpサブパッケージをアプリケーションに含めることで、同様に依存関係を分離することができる
ラップしている依存関係と同じ名前のパッケージを持つことは奇妙に見えるかもしれないが、意図的である。net/httpをアプリケーションの他の場所で使うことを許さないかぎり、パッケージ名の衝突は起こりえない
すべてのHTTPのコードをhttpパッケージへ分離することが要求されることが、名前を重複させることの恩恵である。
code:go
package http
import (
"net/http"
"github.com/benbjohnson/myapp"
)
type Handler struct {
UserService myapp.UserService
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// handle request
}
共有された「モック」サブパッケージを用いる
メインパッケージは依存関係を結びつける
code:go
package main
import (
"log"
"os"
"github.com/benbjohnson/myapp"
"github.com/benbjohnson/myapp/postgres"
"github.com/benbjohnson/myapp/http"
)
func main() {
// Connect to database.
db, err := postgres.Open(os.Getenv("DB"))
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Create services.
us := &postgres.UserService{DB: db}
// Attach to HTTP handler.
var h http.Handler
h.UserService = us
// start http server...
}
参照