呼び出し順に沿ったDI/インターフェースの流れ(暗記)
code:go
⏺ 正しい理解(層・ファイル・メソッド明記)
この行で何が起きているか
【層】: Injector層
【ファイル】: /core/injector/injector.go
【メソッド】: InjectCore関数内
masterBookUseCase := usecase.NewMasterBook(masterBookQuery, masterBookRepo)
1. 実際に呼び出しているのは
【呼び出し元】: Injector層 /core/injector/injector.go の InjectCore関数
【呼び出し先】: UseCase層 /core/usecase/master_book_impl.go の NewMasterBook関数
func NewMasterBook(...) MasterBook { // ← この関数を呼び出している
return &masterBook{...} // 実装構造体を作成して返す
}
2. 戻り値の型がインターフェース
【層】: UseCase層
【ファイル】: /core/usecase/master_book_impl.go
【メソッド】: NewMasterBook関数
// 関数の戻り値の型がインターフェース
func NewMasterBook(...) MasterBook { // ← MasterBookは /core/usecase/master_book.go で定義されたインターフェース型
impl := &masterBook{...} // 実装構造体を作成
return impl // 実装を返すが、型はインターフェース
}
詳しい流れ
Step 1: 関数呼び出し
【層】: Injector層
【ファイル】: /core/injector/injector.go
【メソッド】: InjectCore関数
// 1. 関数呼び出し(実装の関数を呼ぶ)
masterBookUseCase := usecase.NewMasterBook(masterBookQuery, masterBookRepo)
Step 2: NewMasterBook関数の実行
【層】: UseCase層
【ファイル】: /core/usecase/master_book_impl.go
【メソッド】: NewMasterBook関数
// 2. NewMasterBook関数の中身
func NewMasterBook(...) MasterBook {
// 実装構造体を作成
impl := &masterBook{
masterBookQuery: masterBookQuery,
masterBookRepo: masterBookRepo,
}
// 実装を返すが、戻り値の型はインターフェース
return impl // 型: MasterBook, 実体: *masterBook
}
Step 3: 結果を受け取り
【層】: Injector層
【ファイル】: /core/injector/injector.go
【メソッド】: InjectCore関数
// 3. 結果
// masterBookUseCaseには実装が入っているが、型はインターフェース
// masterBookUseCase の型: usecase.MasterBook(インターフェース)
// masterBookUseCase の実体: *masterBook(実装構造体)
インターフェース定義の場所
【層】: UseCase層
【ファイル】: /core/usecase/master_book.go
【内容】: インターフェース定義
type MasterBook interface {
Search(ctx context.Context, in input.SearchMasterBook) ([]*output.SearchMasterBook, error)
}
まとめ
- 呼び出し元: Injector層 /core/injector/injector.go の InjectCore関数
- 呼び出し先: UseCase層 /core/usecase/master_book_impl.go の NewMasterBook関数
- 戻り値の型: UseCase層 /core/usecase/master_book.go で定義された MasterBookインターフェース
- 戻り値の実体: UseCase層 /core/usecase/master_book_impl.go の *masterBook実装構造体
答え: インターフェースを呼び出しているのではなく、実装の関数を呼び出して、インターフェース型で受け取っているです。
code:go
UseCase層のインターフェースと実装の関係
1. ファイル構成と役割
インターフェース定義
【ファイル】: /core/usecase/master_book.go
【役割】: インターフェース定義(契約書)
type MasterBook interface {
Search(ctx context.Context, in input.SearchMasterBook) ([]*output.SearchMasterBook, error)
}
実装
【ファイル】: /core/usecase/master_book_impl.go
【役割】: インターフェースの実装
// 実装構造体(private)
type masterBook struct {
masterBookQuery query.MasterBook
masterBookRepository repository.MasterBook
}
// コンストラクタ(インターフェースを返す)
func NewMasterBook(...) MasterBook { // ← MasterBookインターフェースを返す
return &masterBook{...}
}
---
2. 関係性
usecase/master_book.go (インターフェース定義)
↑ 実装する
usecase/master_book_impl.go (実装)
↑ 注入される
Handler層
---
3. DI(依存性注入)の流れ
起動時の組み立て
【場所】: /core/injector/injector.go の InjectCore関数内
func InjectCore(dbClient *db.Client) *handler.Core {
// Query層作成
masterBookQuery := query.NewMasterBook(dbClient)
// Repository層作成
masterBookRepo := repository.NewMasterBook(dbClient)
// UseCase層作成(ここに書かれる)
masterBookUseCase := usecase.NewMasterBook(masterBookQuery, masterBookRepo)
// 型: usecase.MasterBook(インターフェース)
// 実体: *masterBook(実装)
// Handler層に注入
core := handler.NewCore(..., masterBookUseCase)
return core
}
呼び出しの流れ
main.go
↓ InjectCore(dbClient)
injector.go の InjectCore関数
↓ usecase.NewMasterBook(query, repo)
usecase/master_book_impl.go の NewMasterBook関数
↓ usecase.MasterBook インターフェースを返す
injector.go に戻る
↓ handler.NewCore(..., masterBookUseCase)
handler/handler.go の NewCore関数
↓ *handler.Core を返す
main.go に戻る
---
4. 実行時の動作
// Handler が呼び出すのはインターフェース
result, err := h.masterBookUsecase.Search(...)
// ↑ master_book.go で定義されたインターフェースのメソッド
// ↑ 実際に実行されるのは master_book_impl.go の実装
---
【重要ポイント】
- usecase/master_book.goはusecase/master_book_impl.goの戻り値の型
- Handler層の戻り値ではない
- DIは/core/injector/injector.goで実行される
- 起動時に全依存性を組み立て、実行時はインターフェース経由で呼び出す
注入すると呼び出すは同じですか
⏺ 注入と呼び出しは全く違います。
注入(Injection)
【タイミング】: 起動時(1回だけ)
【目的】: 依存性を外から渡して組み立てる
【場所】: /core/injector/injector.go
// 注入の例
func NewMasterBook(
masterBookQuery query.MasterBook, // ← 外から注入される
masterBookRepository repository.MasterBook, // ← 外から注入される
) MasterBook {
return &masterBook{
masterBookQuery: masterBookQuery, // 注入されたものを保存
masterBookRepository: masterBookRepository, // 注入されたものを保存
}
}
// Handler層への注入
core := handler.NewCore(..., masterBookUseCase) // ← UseCaseを注入
呼び出し(Call)
【タイミング】: 実行時(リクエストの度)
【目的】: メソッドを実行する
【場所】: 各層のメソッド内
// 呼び出しの例
func (h Core) SearchMasterBooks(...) {
result, err := h.masterBookUsecase.Search(ctx, input) // ← メソッド呼び出し
}
func (u *masterBook) Search(...) {
books, err := u.masterBookQuery.Search(ctx, query) // ← メソッド呼び出し
}
具体的な違い
注入のタイミング
アプリ起動
↓
injector.go で全部組み立て(注入)
↓
サーバー待機状態
呼び出しのタイミング
HTTPリクエスト受信
↓
Handler.Search() 呼び出し
↓
UseCase.Search() 呼び出し
↓
Query.Search() 呼び出し
↓
レスポンス返却
まとめ
- 注入: 起動時に依存性を外から渡して保存(構築フェーズ)
- 呼び出し: 実行時にメソッドを実行(実行フェーズ)
答え: 全く違います。注入は組み立て、呼び出しは実行です。
code:go
⏺ Handler層の依存性注入(層・ファイル・メソッド明記)
Handler層への注入
【層】: Injector層
【ファイル】: /core/injector/injector.go
【メソッド】: InjectCore関数内
core := handler.NewCore(
logger,
firebaseAuth,
userUseCase,
publisherUseCase,
authorUseCase,
masterBookUseCase, // ← UseCase層のインターフェースを注入
)
1. 実際に呼び出しているのは
【呼び出し元】: Injector層 /core/injector/injector.go の InjectCore関数
【呼び出し先】: Handler層 /core/handler/handler.go の NewCore関数
func NewCore(
logger *slog.Logger,
firebaseAuthGlue firebase.FirebaseAuthGlue,
userUsecase usecase.User,
publisherUsecase usecase.Publisher,
authorUsecase usecase.Author,
masterBookUsecase usecase.MasterBook, // ← この引数を受け取る
) *Core { // ← 戻り値は構造体のポインタ
return &Core{
Logger: logger,
firebaseAuthGlue: firebaseAuthGlue,
userUsecase: userUsecase,
publisherUsecase: publisherUsecase,
authorUsecase: authorUsecase,
masterBookUsecase: masterBookUsecase, // ← フィールドに保存
}
}
2. Core構造体の定義
【層】: Handler層
【ファイル】: /core/handler/handler.go
【構造体】: Core
type Core struct {
Unimplemented // 未実装メソッド用の埋め込み
Logger *slog.Logger // ログ用
firebaseAuthGlue firebase.FirebaseAuthGlue
userUsecase usecase.User // インターフェース
publisherUsecase usecase.Publisher // インターフェース
authorUsecase usecase.Author // インターフェース
masterBookUsecase usecase.MasterBook // ← インターフェースとして保存
}
詳しい流れ
Step 1: Handler構造体作成の呼び出し
【層】: Injector層
【ファイル】: /core/injector/injector.go
【メソッド】: InjectCore関数
// 1. Handler構造体作成の呼び出し
core := handler.NewCore(
logger,
firebaseAuth,
userUseCase, // usecase.User インターフェース
publisherUseCase, // usecase.Publisher インターフェース
authorUseCase, // usecase.Author インターフェース
masterBookUseCase, // usecase.MasterBook インターフェース
)
Step 2: NewCore関数の実行
【層】: Handler層
【ファイル】: /core/handler/handler.go
【メソッド】: NewCore関数
// 2. NewCore関数の中身
func NewCore(..., masterBookUsecase usecase.MasterBook) *Core {
// 構造体を作成
return &Core{
Logger: logger,
firebaseAuthGlue: firebaseAuthGlue,
userUsecase: userUsecase,
publisherUsecase: publisherUsecase,
authorUsecase: authorUsecase,
masterBookUsecase: masterBookUsecase, // インターフェースをフィールドに保存
}
}
Step 3: 結果を受け取り
【層】: Injector層
【ファイル】: /core/injector/injector.go
【メソッド】: InjectCore関数
// 3. 結果
// core には Handler構造体が入っている
// core の型: *handler.Core(構造体のポインタ)
// core.masterBookUsecase の型: usecase.MasterBook(インターフェース)
// core.masterBookUsecase の実体: *masterBook(UseCase層の実装)
実行時の使用
【層】: Handler層
【ファイル】: /core/handler/master_book.go
【メソッド】: SearchMasterBooks関数
func (h Core) SearchMasterBooks(ctx context.Context, request oapi.SearchMasterBooksRequestObject) {
// 注入されたインターフェースを使用
result, err := h.masterBookUsecase.Search(ctx, searchInput)
// ↑ h.masterBookUsecase: usecase.MasterBook インターフェース
// ↑ 実際に実行される: /core/usecase/master_book_impl.go の Search メソッド
}
まとめ
- 呼び出し元: Injector層 /core/injector/injector.go の InjectCore関数
- 呼び出し先: Handler層 /core/handler/handler.go の NewCore関数
- 戻り値の型: Handler層 /core/handler/handler.go の *Core構造体
- 注入されるもの: UseCase層の usecase.MasterBookインターフェース(実体は実装構造体)
- 保存場所: Core構造体のmasterBookUsecaseフィールド
答え: Handler層でも同様に、実装の関数(NewCore)を呼び出して、構造体を作成し、その中にインターフェースを保存しているです。
code:go
⏺ 呼び出し順に沿ったDI/インターフェースの流れ
【起動時】1. アプリケーション起動
【層】: Main層
【ファイル】: /cmd/core/main.go
【処理】: アプリケーション開始
func main() {
// データベース接続作成
sqlDB, err := sql.Open("postgres", "postgres://...")
dbClient := db.NewClient(sqlDB)
// Injectorを呼び出す
core := injector.InjectCore(dbClient)
---
【起動時】2. Injectorが依存性を組み立て
【層】: Injector層
【ファイル】: /core/injector/injector.go
【処理】: 全ての依存性を組み立てる
func InjectCore(dbClient *db.Client) *handler.Core {
// ① Query層の実装を作成
masterBookQuery := query.NewMasterBook(dbClient)
// ↑ 呼び出し: /core/infra/query/master_book_impl.go
// ↑ 戻り値: query.MasterBook インターフェース(実体は private masterBook構造体)
// ② Repository層の実装を作成
masterBookRepo := repository.NewMasterBook(dbClient)
// ↑ 呼び出し: /core/infra/repository/master_book_impl.go
// ↑ 戻り値: repository.MasterBook インターフェース(実体は private masterBook構造体)
// ③ UseCase層の実装を作成
masterBookUseCase := usecase.NewMasterBook(masterBookQuery, masterBookRepo)
// ↑ 呼び出し: /core/usecase/master_book_impl.go
// ↑ 戻り値: usecase.MasterBook インターフェース(実体は private masterBook構造体)
// ④ Handler層の実装を作成
core := handler.NewCore(..., masterBookUseCase)
// ↑ 呼び出し: /core/handler/handler.go
// ↑ 戻り値: *handler.Core 構造体
return core
}
---
【起動時】3. Query層コンストラクタの中身
【層】: Infrastructure/Query層
【ファイル】: /core/infra/query/master_book_impl.go
【処理】: Query層の実装を作成
func NewMasterBook(dbClient *db.Client) query.MasterBook {
// private構造体を作成
impl := &masterBook{dbClient: dbClient}
// インターフェースとして返す
return impl // 型: query.MasterBook, 実体: *masterBook
}
// インターフェース定義はここ:
【ファイル】: /core/domain/query/master_book.go
type MasterBook interface {
Search(ctx context.Context, query MasterBookSearchQuery) ([]*model.MasterBook, error)
}
---
【起動時】4. UseCase層コンストラクタの中身
【層】: UseCase層
【ファイル】: /core/usecase/master_book_impl.go
【処理】: UseCase層の実装を作成
func NewMasterBook(
masterBookQuery query.MasterBook, // ② で作られたインターフェース
masterBookRepo repository.MasterBook, // ② で作られたインターフェース
) MasterBook { // 戻り値もインターフェース
// private構造体を作成
impl := &masterBook{
masterBookQuery: masterBookQuery, // インターフェースを保存
masterBookRepo: masterBookRepo, // インターフェースを保存
}
// インターフェースとして返す
return impl // 型: usecase.MasterBook, 実体: *masterBook
}
// インターフェース定義はここ:
【ファイル】: /core/usecase/master_book.go
type MasterBook interface {
Search(ctx context.Context, in input.SearchMasterBook) ([]*output.SearchMasterBook, error)
}
---
【起動時】5. Handler層コンストラクタの中身
【層】: Handler層
【ファイル】: /core/handler/handler.go
【処理】: Handler層の実装を作成
func NewCore(
// ...他の引数
masterBookUseCase usecase.MasterBook, // ③ で作られたインターフェース
) *Core {
// 構造体を作成
return &Core{
masterBookUsecase: masterBookUseCase, // インターフェースを保存
}
}
---
【実行時】6. HTTPリクエスト処理開始
【層】: Handler層
【ファイル】: /core/handler/master_book.go
【処理】: 検索リクエストを処理
func (h Core) SearchMasterBooks(ctx context.Context, request oapi.SearchMasterBooksRequestObject) {
// input作成
searchInput := input.NewSearchMasterBook(title, authorName, publisherName)
// UseCaseインターフェースを呼び出し
result, err := h.masterBookUsecase.Search(ctx, searchInput)
// ↑ 呼び出し: インターフェースのSearchメソッド
// ↑ 実際に実行されるのは: /core/usecase/master_book_impl.go の Search メソッド
// ↑ 戻り値: []*output.SearchMasterBook
---
【実行時】7. UseCase層での処理
【層】: UseCase層
【ファイル】: /core/usecase/master_book_impl.go
【処理】: ビジネスロジック実行
func (u *masterBook) Search(ctx context.Context, in input.SearchMasterBook) ([]*output.SearchMasterBook, error) {
// 検索条件チェック
if !in.HasAnySearchCriteria() {
return nil, nil
}
// Queryインターフェースを呼び出し
books, err := u.masterBookQuery.Search(ctx, query.MasterBookSearchQuery{...})
// ↑ 呼び出し: インターフェースのSearchメソッド
// ↑ 実際に実行されるのは: /core/infra/query/master_book_impl.go の Search メソッド
// ↑ 戻り値: []*model.MasterBook
// outputに変換
result := output.NewMasterBookSearch(books)
// ↑ 呼び出し: /core/usecase/output/master_book.go
// ↑ 戻り値: []*output.SearchMasterBook
return result, nil
}
---
【実行時】8. Query層での処理
【層】: Infrastructure/Query層
【ファイル】: /core/infra/query/master_book_impl.go
【処理】: データベースアクセス
func (m *masterBook) Search(ctx context.Context, query query.MasterBookSearchQuery) ([]*model.MasterBook, error) {
// SQL組み立て
if query.Title.Valid {
pattern := "%" + db.EscapeLikePattern(query.Title.String) + "%"
mods = append(mods, dbmodels.SelectWhere.MasterBooks.Title.ILike(pattern))
}
// データベース実行
dbBooks, err := dbmodels.MasterBooks.Query(mods...).All(ctx, m.dbClient.Get(ctx))
// ↑ 呼び出し: Bob ORM → PostgreSQL
// ↑ 戻り値: []*dbmodels.MasterBook
// モデル変換
books := make([]*model.MasterBook, len(dbBooks))
for i, dbBook := range dbBooks {
booksi = (*model.MasterBook)(dbBook) // 型変換 }
return books, nil // 型: []*model.MasterBook
}
---
【実行時】9. 結果の返却(戻りの流れ)
【戻り値の流れ】:
PostgreSQL → []*dbmodels.MasterBook
↓ 型変換 (Query層)
[]*model.MasterBook → Query層から UseCase層へ
↓ output変換 (UseCase層)
[]*output.SearchMasterBook → UseCase層から Handler層へ
↓ API変換 (Handler層)
[]oapi.MasterBook → HTTPレスポンス
↓
JSON → クライアント
---
【重要】インターフェースと実装の対応表
| 層 | インターフェース定義場所 | 実装場所 | 注入される場所 |
|-------------|----------------------------------------|--------------------------------------------|-------------|
| Query層 | /core/domain/query/master_book.go | /core/infra/query/master_book_impl.go | UseCase層に注入 |
| Repository層 | /core/domain/repository/master_book.go | /core/infra/repository/master_book_impl.go | UseCase層に注入 |
| UseCase層 | /core/usecase/master_book.go | /core/usecase/master_book_impl.go | Handler層に注入 |
| Handler層 | なし(構造体) | /core/handler/handler.go | main関数で使用 |
ポイント:
- 起動時にすべての依存性が組み立てられる
- 実行時はインターフェース経由で実装を呼び出す
- 各層は上位のインターフェースしか知らない(下位の実装詳細を知らない)