未経験からエンジニア 奮闘記

未経験からエンジニアに自由に生きる途中

MENU

Goにおけるパッケージの利用可能範囲と共通化の自分ベストプラクティス

Goにおけるパッケージの利用可能範囲と共通化の自分ベストプラクティス

なぜ調べたのか?

Goを使ったプロジェクトで以下のような問題に直面し、調査を行いました。

  1. ディレクトリ間でのパッケージ利用時のエラー
    • ディレクトリAで定義した機能をディレクトリBから利用しようとしたところエラーが発生。
    • パッケージのスコープやアクセス可能範囲に疑問を抱きました。
  2. 既存コードの意図を汲み取る必要性
    • 他者が書いた既存コードを理解する過程で、特定のパッケージがどこまで利用可能なのかを知りたい場面がありました。

これらの背景から、Goにおけるパッケージの適用範囲や構造に関する知識を整理する必要があると感じました。


結論

調査の結果、以下の知見を得ました。

  1. 機能上のパッケージのスコープ
    • go.mod以下に存在するすべてのディレクトリにパッケージはアクセス可能。
    • ただし、同じパッケージ名が複数存在すると名前衝突が発生するため、別名をつけてインポートする必要がある。
  2. 実務での運用ルール
    • Goではディレクトリ単位でパッケージを分離して利用するのが一般的。
    • 名前衝突を避けるため、共通機能を別ディレクトリ(例: common)に移しておくことが推奨される。
  3. 共通機能の整理方法
    • 以下のようなディレクトリ構造を採用すると、コードの再利用性が向上しやすい。

        RootDir
          ├─ common/
          │   ├─ utility.go  // 共通機能
          │   ├─ package common
          ├─ A/
          │   ├─ middleware/
          │   │   ├─ xx.go
          │   │   ├─ package middleware
          │   ├─ main.go
          ├─ B/
          │   ├─ middleware/
          │   │   ├─ xx.go
          │   │   ├─ package middleware
          │   ├─ main.go
          ├─ C/
          │   ├─ hoge/
          │   │   ├─ main.go
          ├─ go.mod
      

パッケージの適応範囲の検証内容

ケース1: go.mod配下でのパッケージ利用

以下のディレクトリ構成では、CディレクトリからAmiddlewareパッケージの関数を呼び出せます。

RootDir/
  ├─ A/
  │   ├─ middleware/
  │   │   ├─ xx.go (package middleware)
  │   │   ├─ AMiddlewareFunc
  ├─ B/
  │   ├─ middleware/
  │   │   ├─ xx.go (package middleware)
  │   │   ├─ BMiddlewareFunc
  ├─ C/
  │   ├─ hoge/
  │   │   ├─ main.go
  ├─ go.mod

検証結果:

  • C/hoge/main.go内でimport "RootDir/A/middleware"とすることで、AMiddlewareFuncを呼び出せました。
  • すべてのディレクトリが同じgo.modの管理下にある場合、パッケージの利用に制約は少ない。

ケース2: 各ディレクトリに独自のgo.modが存在する場合

以下のようにディレクトリごとに独自のgo.modを持たせた場合、CディレクトリからABのパッケージを利用できません。

RootDir/
  ├─ A/
  │   ├─ middleware/
  │   │   ├─ xx.go (package middleware)
  │   │   ├─ AMiddlewareFunc
  │   ├─ go.mod
  │   ├─ main.go
  ├─ B/
  │   ├─ middleware/
  │   │   ├─ xx.go (package middleware)
  │   │   ├─ BMiddlewareFunc
  │   ├─ go.mod
  │   ├─ main.go
  ├─ C/
  │   ├─ hoge/
  │   │   ├─ main.go

検証結果:

  • C/hoge/main.goからA/middlewareB/middlewareを呼び出せません。
  • ディレクトリが独立したgo.modを持つ場合、外部パッケージとして明示的にインポートする必要があります。

通化のためのディレクトリ構造の提案

以下の構造を採用することで、共通機能の管理が容易になります。

RootDir/
  ├─ common/
  │   ├─ utility.go (package common)
  ├─ A/
  │   ├─ middleware/
  │   │   ├─ xx.go (package middleware)
  │   ├─ main.go
  ├─ B/
  │   ├─ middleware/
  │   │   ├─ xx.go (package middleware)
  │   ├─ main.go
  ├─ C/
  │   ├─ hoge/
  │   │   ├─ main.go
  ├─ go.mod

このように、commonディレクトリを作成して共通機能を格納すると、以下のメリットがあります。

  1. コードの再利用性向上
    • 共通処理を一元化できるため、修正が容易。
  2. 名前衝突の回避
    • 各パッケージが独立しているため、衝突の心配がない。
  3. 保守性の向上
    • 共通機能が明確に分離され、開発者全員がアクセスしやすくなる。

まとめ

Goでは、go.mod以下のディレクトリでパッケージを利用できますが、名前衝突の可能性を考慮する必要があります。実務ではディレクトリ単位でパッケージを分離し、共通機能をcommonディレクトリなどに集約することが推奨されます。このような構造を採用することで、コードの保守性や再利用性を高めることができます。

Goプロジェクトを設計する際の参考になれば幸いです!