できない.dev

Go で「import cycle not allowed」が解決できない

Go はパッケージ同士が直接・間接に循環参照することを禁止している。
共通部分を第3のパッケージへ切り出すか、依存の向きをインターフェースで逆転させて循環を断ち切る。

#go#import#package#cycle

公開:

要約

import cycle not allowed は、Go のパッケージが互いを直接または間接に import し合う「循環参照」になっているときのコンパイルエラーです。
Go 言語仕様はパッケージの循環 import を認めていません。
これは設計上の制約で、回避ではなく依存関係そのものを一方向に整理して解消します。
典型的には、共通部分を別パッケージに切り出すか、インターフェースで依存の向きを逆転させます。

package myapp/service
	imports myapp/repo
	imports myapp/service: import cycle not allowed

よくある原因

  1. 相互参照: servicerepo を、reposervice を import している。
  2. 共通型の置き場所: 両方が使う型を一方のパッケージに置いたため、もう一方がそれを取りに行って循環する。
  3. ユーティリティの逆流: 「便利関数」パッケージが、本来上位であるはずのパッケージを import している。
  4. 場所ベースの分割: 責務ではなくファイルの置き場所でパッケージを切ったため、依存が双方向になっている。

解決策

1. 共通部分を第3のパッケージへ切り出す

A と B が共通で必要とする型・定数・インターフェースを model のような独立パッケージに移し、A も B も model を import する形にします。
依存の矢印が model に向かう一方向になり、循環が消えます。

変更前: service ⇄ repo
変更後: service → model ← repo

2. インターフェースで依存を逆転させる

下位(repo)が上位(service)の型を直接使いたい場合は、repo 側に必要な振る舞いをインターフェースとして定義し、service がそれを実装します。
下位は上位を import しなくて済みます(依存性逆転)。

// repo パッケージ側でインターフェースを定義し、service を import しない
type Notifier interface {
	Notify(msg string) error
}

3. 統合・可視化で見直す

分ける必然性が薄ければ 1 パッケージにまとめるのも有効です。
複雑な依存は go list -deps ./... などで可視化し、どの辺が循環しているかを特定してから断ち切ります。

この記事は役立ちましたか?