Module System
IOTA SDK’s module system enables modular, composable architecture. Each module is self-contained and can be added or removed without affecting others.
Module Lifecycle
1. Registration Phase
During application startup, each module’s Register() method is called:
Schema/migration files are typically embedded in modules; the application loads migrations from its configured migrations directory (e.g. MigrationsDir), not by modules calling a migration manager during Register().
2. Initialization Phase
Services are wired together via dependency injection:
3. Running Phase
The application serves requests with all modules active:
Module Structure
Required Files
modules/{module}/
├── module.go # Module interface implementation
└── links.go # Navigation definitionStandard Structure
modules/{module}/
├── domain/ # Business logic
│ ├── aggregates/ # Aggregate roots
│ ├── entities/ # Domain entities
│ └── value_objects/ # Value objects
├── infrastructure/
│ └── persistence/ # Data access
│ ├── models/ # Database models
│ ├── schema/ # SQL migrations
│ └── *.go # Repository implementations
├── services/ # Business services
├── presentation/
│ ├── controllers/ # HTTP handlers
│ └── templates/ # Templ files
├── module.go # Module registration
└── links.go # NavigationModule Interface
All modules implement the application.Module interface:
Registration Methods
| Method | Purpose | Called In |
|---|---|---|
RegisterServices() | Add business services | Services layer |
RegisterControllers() | Add HTTP handlers | Presentation layer |
RegisterMiddleware() | Add request middleware | Infrastructure |
RegisterLocaleFiles() | Translations | Presentation |
RegisterHashFsAssets() | Static assets | Presentation |
QuickLinks().Add(spotlight.NewQuickLink(trKey, link)...) | Spotlight quick links | Navigation / actions |
Service Registration
Service Pattern
Services are registered as interfaces and retrieved by type:
Dependency Injection
Services declare dependencies explicitly:
Controller Registration
Routing
Controllers define their base path during registration:
Permission Integration
Controllers check permissions via middleware:
Cross-Module Communication
Service Access
Modules access other modules via the service registry:
Event-Driven Communication
Loose coupling via domain events:
Module Configuration
Module Options
Modules accept configuration via options pattern:
Environment-Based Config
Configuration can vary by environment:
| Environment | Configuration | Purpose |
|---|---|---|
| Development | Debug logging, test endpoints | Development |
| Staging | Production-like, limited data | Testing |
| Production | Optimized, no debug info | Production |
Best Practices
- Single Responsibility - Each module handles one business domain
- Explicit Dependencies - Declare all required services
- No Circular Dependencies - Module A shouldn’t depend on B if B depends on A
- Event-Based Coupling - Use events for loose coupling
- Interface Segregation - Small, focused service interfaces
Built-in Modules
Modules are loaded from modules.BuiltInModules (see modules/load.go): core, hrm, finance, projects, logging, warehouse, crm, website, billing, oidc, testkit. BiChat and Superadmin are not in BuiltInModules (BiChat is conditionally loaded when configured; Superadmin is a separate cmd/superadmin application).
Next Steps
- Multi-Tenancy - How modules handle tenant isolation
- Domain-Driven Design - DDD patterns in modules
- Core Module - See a complete module implementation