Is this the right way to modularize something?

By and large, the only things that need to change between a module of one type and the other are:

  • what other modules are allowed to import this one, and
  • the set of options the module can define.

(If you’ve gone messing around with special module args, those will also vary. I’m not a fan of leaning on that mechanism for ordinary data-sharing purposes but I have no idea if I’m in the minority.)

Yup, exactly.

I recommend keeping each of the different varieties of modules separate. I’d suggest one top-level folder for each different variety of thing:

  • home ← contains all, and only, Home Manager modules
  • darwin ← contains all, and only, nix-darwin modules
    • darwin/hosts ← these are modules too, so they go in here
  • nixos ← contains all, and only, NixOS modules
    • nixos/hosts ← these are modules too, so they go in here
  • lib ← contains utility Nix functions or constants that can be vanilla imported by modules, but contains no modules (I don’t know if you have any of these)
  • pkgs ← contains Nix package definitions that can be callPackage’d by modules or overlays
  • overlays (if you have any)
  • files

Keep module imports (the imports = [ ... ]; style) to within a single top-level directory. Permit vanilla Nix imports (the import ... style) from {home, darwin, nixos, pkgs, overlays} to utils, and from modules to a root module of another variety when needed (for example, when a common darwin module that defines users imports the root HM module for a user).

Resist the urge to define polyglot modules that will work in multiple top-level module flavor directories. Factor any intolerable duplication out of the module system and into plain-Nix files in lib. This is not necessarily the opinion shared by many people but I think it’s good advice.

This last bit is mostly aesthetic, but I try to keep my folder structures uniformly two-deep. I always make nixos/foo/default.nix instead of nixos/foo.nix, and nixos/foo_bar/baz.nix instead of nixos/foo/bar/baz.nix. That way, relative import paths (of whatever variety) don’t need a lot of thought: ../../foo always points to a different top-level folder, and ../foo always points to another grouping in the same top-level category.

Hope that helps!

1 Like