How to access nested modules (submodules) in Go?
Asked Answered
L

3

19

Go version: 1.12.9

Here is the structure of a simple demo project:

enter image description here

So we have a module domain, which contains two modules: activity and person.

I would like to use domain with all nested modules in the main file => modules.go.

I know how to import domain in the main go.mod:

module modules

go 1.12

replace modules/domain v0.0.0 => ./domain

require modules/domain v0.0.0

So after that can use code from domain/domain.go, but I cannot access code from activity and person modules.

Yes, I could manually import nested modules, for example:
(main go.mod):

module modules

go 1.12

replace modules/domain v0.0.0 => ./domain

replace modules/domain/activity v0.0.0 => ./domain/activity

require (
    modules/domain v0.0.0
    modules/domain/activity v0.0.0
)

but I don't want to manually import all submodules.

How to configure modules to import domain with all submodules?

Leukas answered 5/9, 2019 at 13:0 Comment(1)
You can't. That's not how modules work. But in your case these probably shouldn't be separate modules in the first place, just separate packages in the same module.Tupelo
D
31

Yes, I could manually import nested modules [...] but I don't want to manually import all submodules. How to configure modules to import domain with all submodules ?

You simply cannot do this.

Some background:

  • There is no real notion of "sub"-module, all modules are equal.
  • Same for packages: all(*) packages are equal. If you want to use a package you must import it.
  • The layout in the filesystem does not imply any kind of technical relation between packages (e.g. net/http/cookiejar is as much related to net/http as it is related crypto/md5: not at all).
  • Being part of a module doesn't mean much: Modules are just sets of packages versioned together and doesn't add any additional relation between these packages.

The rule is dead simple: If you want to import a package you have to import it.

The fact that there are no magical wildcard imports might seem annoying (see below) but prevents unintended imports: Adding a package doesn't magically import it (and execute its init functions!).

In real live having to import all packages is not that annoying because "normal" Go code doesn't use tiny packages. This is a common mistake for someone indoctrinated by Java/C#/PHP/Node "project architectures" with lots of folders and tiny classes: Don't do that in Go. It is not helping. It often even leads to circular import and hurts.

Same for modules. A Go module is probably not what you think it is. A module is a set of packages versioned together. I doubt there is a reason to provide different versions for package person and package domain at the same time. (It is allowed to have several modules in one repository/project, but having more than one is a very rare case, yours clearly isn't).

Best advice (in order of relevance):

  • Stop making every package its own module.
  • Stop making tiny packages.
  • Stop trying to mimick a source code layout ("architecture") you might be used from other languages.

(*) internal and vendored packages are an exception which does not apply to your problem.

Doomsday answered 5/9, 2019 at 13:11 Comment(6)
"How to Write Go Code" only documents the flawed approach of writing Go code in GOPATH, it has not been updated to document Go Modules.Merchant
Actually as of today (1/19/20) they FINALLY updated "How To Write Go Code" actually using the module system. golang.org/doc/code.htmlHoracehoracio
@slezica You are right, I was annoyed and a bit insulted. This is a clear XY problem with the wanted solution being utterly dangerous. I tried to explain a bit more.Doomsday
@Doomsday Fantastic rewrite :) thank you! It will be appreciated by future readers, I'm sure.Eidson
A reasonable use-case for nested modules is to isolate the dependencies of a package that is imported by other modules. For example, Kubernetes operators are routinely written in go, and consist of a set of type definitions in pkg/apis, and controller logic elsewhere. Applications or tools that use these types needn't be bothered with the controller dependencies, so isolating the dependencies of pkg/apis makes sense.Dwelling
Here is another official documents about proper code organization in a repository: go.dev/doc/modules/managing-sourceAloysia
E
3

go 1.18 has the support for the new multi-workspace feature by go.work files. for more info, check golang workspace proposal

Earmuff answered 3/3, 2022 at 18:22 Comment(0)
N
0

I faced a similar situation with Go 1.20. Here is How I solved it.

Package

I have used a repository as package in my Go project. This repository is uses a sub-module.

Here is the .gitmodules file inside my repository my-repository/services:

enter image description here

Change

What I was missing:
After the introduction of Workspace, submodules were treated differently. So now I need to create a go.work file. Needed to be placed at same level as go.mod.

go.work

go 1.20

use ./validator/validate

Usage

Now I want to use this package in my main repo. Imported it as,

enter image description here

This solved my issue. You can find the documentation at: Setting up your workspace

Nub answered 2/2, 2023 at 11:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.