How to access nested modules (submodules) in Go?

Hubert picture Hubert · Sep 5, 2019 · Viewed 7.5k times · Source

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.

Ok 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.

Question:

How to configure modules to import domain with all submodules ???

Thanks in advance,
Hubert

Answer

Volker picture Volker · Sep 5, 2019

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.