Skip to content

Modules

A Module is a class annotated with a Module decorator. Modules encapsulate a closely related set of capabilities: controllers, providers, and imported modules.

Module Interface

In Nika, a Module is any struct that implements the nika.Module interface:

type Module interface {
    Controllers() []interface{}
    Providers()   []interface{}
    Imports()     []Module
    Exports()     []interface{}
}
  • Controllers() — Returns the controllers (or their constructors) for this module
  • Providers() — Returns the providers (or their constructors) for this module
  • Imports() — Returns sub-modules to load recursively
  • Exports() — Returns providers that modules importing this module may inject

Creating a Module

package src

import "github.com/nika-framework/nika"

type UsersModule struct{}

func NewUsersModule() *UsersModule {
    return &UsersModule{}
}

func (m *UsersModule) Controllers() []interface{} {
    return []interface{}{
        NewUserController,
    }
}

func (m *UsersModule) Providers() []interface{} {
    return []interface{}{
        NewUserService,
        NewUserRepository,
    }
}

func (m *UsersModule) Imports() []nika.Module {
    return []nika.Module{}
}

func (m *UsersModule) Exports() []interface{} {
    return []interface{}{}
}

Root Module

The root module is the entry point of your application. It imports all feature modules:

package src

import "github.com/nika-framework/nika"

type AppModule struct{}

func NewAppModule() *AppModule {
    return &AppModule{}
}

func (m *AppModule) Controllers() []interface{} {
    return []interface{}{
        NewHealthController,
    }
}

func (m *AppModule) Providers() []interface{} {
    return []interface{}{}
}

func (m *AppModule) Imports() []nika.Module {
    return []nika.Module{
        NewUsersModule(),
        NewAuthModule(),
        NewConfigModule(),
    }
}

func (m *AppModule) Exports() []interface{} {
    return []interface{}{}
}

Feature Modules

Organize your application into feature modules. Each module is self-contained:

// Auth module
type AuthModule struct{}

func NewAuthModule() *AuthModule {
    return &AuthModule{}
}

func (m *AuthModule) Controllers() []interface{} {
    return []interface{}{
        NewAuthController,
    }
}

func (m *AuthModule) Providers() []interface{} {
    return []interface{}{
        NewAuthService,
        NewTokenService,
    }
}

func (m *AuthModule) Imports() []nika.Module {
    return []nika.Module{}
}

func (m *AuthModule) Exports() []interface{} {
    return []interface{}{}
}

Module Loading Order

When app.LoadModule(rootModule) is called, Nika processes modules in the following order:

1. Recursively load all Imports (sub-modules)
2. Register all Providers into the DI container
3. Resolve and register all Controllers

This means exported providers from imported modules are available before the parent module's providers and controllers are resolved.

Directory Structure

A recommended project structure:

my-app/
├── main.go
├── src/
│   ├── app_module.go          # Root module
│   ├── users/
│   │   ├── users_module.go
│   │   ├── user_controller.go
│   │   ├── user_service.go
│   │   └── user_repository.go
│   ├── auth/
│   │   ├── auth_module.go
│   │   ├── auth_controller.go
│   └── └── auth_service.go
├── go.mod
└── go.sum

Shared Providers

A module can expose selected providers to modules that import it. Private providers remain available only to their own module:

// RoleModule
func (m *RoleModule) Providers() []interface{} {
    return []interface{}{
        NewRoleRepository,
        NewRoleService,
    }
}

func (m *RoleModule) Exports() []interface{} {
    return []interface{}{NewRoleService}
}

// UserModule imports RoleModule and can inject *RoleService.
// *RoleRepository remains private to RoleModule.

App-level singletons registered with app.RegisterSingleton() remain available to every module.