Elixir模块名中点号的使用时机及命名规范咨询
Great question! Let's unpack how Elixir's module naming with dots works, its connection to directory structure, and when you should use it.
First: A Quick Correction
First off, note that valid Elixir module names require uppercase first letters. So the syntax you're seeing should actually be defmodule Foo.Bar.Baz do end (not lowercase foo.bar.baz) — lowercase dots would refer to atoms or variables, which would throw a compilation error.
What Do Dots in Module Names Mean?
Dots in Elixir module names represent nested modules, not just a direct mapping to file paths. Think of it as a way to create a namespace or logical grouping for related code. For example:
Foo.BarmeansBaris a module nested inside theFoomoduleFoo.Bar.Bazis a module nested insideFoo.Bar, which itself is nested insideFoo
This is purely a logical organization tool at the language level — Elixir doesn't enforce any link between module nesting and your project's directory structure... but Mix (Elixir's build tool) does have a strong convention around this.
The Mix Convention: Modules ↔ Directory Structure
Mix follows the "module name maps to path" convention to keep projects organized. Here's how it works:
- A module named
MyApp.Foo.Barshould live atlib/my_app/foo/bar.ex - A module named
MyApp.Utils.StringHelpersshould live atlib/my_app/utils/string_helpers.ex
But as you noticed, this is a convention, not a rule. You can put a module named Bar in lib/foo/bar.ex and it will compile and run. However, this is not recommended for a few reasons:
- Other developers will struggle to find the module (they'll look for
Barinlib/bar.ex, notlib/foo/bar.ex) - Mix's automated tools (like
mix testfinding test files, ormix compileoptimizing builds) may not work as expected - You risk module name collisions if another module named
Baris added elsewhere in the project
When to Use Nested Module Names
Nested modules are useful in several scenarios:
- Logical Grouping: Cluster related functionality under a parent namespace. For example, all user-related modules (profile management, authentication, settings) can live under
MyApp.UserasMyApp.User.Profile,MyApp.User.Auth, etc. - Namespace Isolation: Avoid naming conflicts, especially in libraries. If you're building a HTTP client library, you might have
MyHTTPClient.Adapter.Curl,MyHTTPClient.Middleware.JSON— clearly signaling these modules belong to your library's ecosystem. - Internal Implementation Marking: Use nested modules to indicate private, internal code. For example,
MyApp.Private.ValidationHelperstells other developers this module isn't intended for external use. - Domain Boundaries: Split large domains into smaller, focused modules. A e-commerce app might have
MyApp.ShoppingCart.Cart,MyApp.ShoppingCart.Item,MyApp.ShoppingCart.Checkoutto keep the shopping cart logic organized.
Key Takeaways
- Dots in module names are for nested logical grouping, not just directory paths
- Mix's module-to-directory convention is strongly recommended for maintainability, even though it's not enforced
- Nested modules help keep code organized, avoid conflicts, and communicate intent to other developers
内容的提问来源于stack exchange,提问作者adamkgray

