nunu

repository·main·Indexed 25 days ago

https://github.com/go-nunu/nunu

A scaffolding CLI tool for Go applications that provides a structured, layered architecture. It includes commands to initialize projects with various layouts (Basic, Advanced, Chat, Admin, MCP Server, and Monorepo) and scaffold architectural components such as Handlers, Services, Repositories, and Models. The tool integrates Google Wire for dependency injection, Viper for configuration, Zap for logging, and supports hot-reloading via the `nunu run` command.

Tokens
7.8K
Snippets
29
Records
46
Agent score
78%

What's inside nunu

  1. How dependency injection works with Wire in Nunu

    main

    Nunu uses the Wire framework to manage dependency injection, ensuring modularity and decoupling.

    Instead of manually wiring dependencies, you define them in a wire.go file specific to a subcommand. Wire then precompiles these definitions to generate a wire_gen.go file.

    Key Files:

    • cmd/[subcommand]/wire.go: The configuration file where you define dependencies for that specific subcommand (e.g., server or migration).
    • cmd/[subcommand]/wire/wire_gen.go: The automatically generated file containing the injection logic. Do not modify this file manually.

    For more details, refer to the official Wire documentation.

  2. Understand the Nunu directory structure

    main

    Nunu follows a layered architecture. Key directories include:

    • cmd/: Application entry points (e.g., server, migration, task). Contains main.go and wire.go files.
    • config/: Application configuration files for different environments.
    • deploy/: Deployment scripts and configuration.
    • internal/: The core business logic, subdivided into:
      • handler/: HTTP request handlers.
      • job/: Background task logic.
      • model/: Data model definitions.
      • repository/: Data access layer (database interactions).
      • server/: HTTP server implementation.
      • service/: Business logic implementation.
    • pkg/: Common utilities and functions.
    • scripts/: Scripts for compilation, testing, and deployment.
    • test/: Unit tests organized by module.
    • web/: Frontend assets (HTML, CSS, JS).
  3. Understand the Nunu layered architecture

    main

    Nunu uses a classic layered architecture designed for modularity and decoupling, utilizing Wire for dependency injection.

    Core Directory Structure & Modules:

    • cmd/: Application entry points (e.g., server, migration, task). Contains main.go and wire.go files.
    • internal/: The core business logic of the application.
      • handler/: HTTP request processors that call services.
      • service/: Implementation of business logic.
      • repository/: Data access layer for database interactions.
      • model/: Data model definitions.
      • middleware/: HTTP middleware.
      • server/: HTTP server implementation.
      • job/: Background task logic.
    • config/: Environment-specific configuration files.
    • pkg/: General-purpose utilities and tools.
    • test/: Unit tests organized by module.
    • api/: API definitions (e.g., v1).
    • deploy/: Deployment scripts and configurations.
    • docs/: Project documentation.
    • web/: Frontend assets (HTML, CSS, JS).
  4. Read configuration items using Viper

    main

    Configuration is typically stored in .yaml files within the config directory. You can inject *viper.Viper via dependency injection to read values in your code.

    Example configuration (config/local.yaml):

    data:
      mysql:
        user: root:123456@tcp(127.0.0.1:3380)/user?charset=utf8mb4&parseTime=True&loc=Local
      redis:
        addr: 127.0.0.1:6350
        password: ""
        db: 0
        read_timeout: 0.2s
        write_timeout: 0.2s

    Example code to read config:

    func NewDB(conf *viper.Viper) *gorm.DB {
    	db, err := gorm.Open(mysql.Open(conf.GetString("data.mysql.user")), &gorm.Config{})
    	if err != nil {
    		panic(err)
    	}
    	return db
    }

    Note: After adding new dependency injections for configuration, run nunu wire to update the dependency graph.

  5. Implement Repository using Interface-Oriented Programming

    main

    Nunu follows interface-oriented programming. Components like xxxRepository, xxxService, and xxxHandler are implemented based on interfaces rather than concrete structs. This improves flexibility, scalability, and testability (allowing for easy mocking).

    Pattern Example:

    type UserRepository interface {
    	FirstById(id int64) (*model.User, error)
    }
    
    type userRepository struct {
    	*Repository
    }
    
    func NewUserRepository(repository *Repository) *UserRepository {
    	return &UserRepository{
    		Repository: repository,
    	}
    }
  6. How mocking enables dependency isolation in unit tests

    main

    In Nunu projects, unit testing follows the Single Responsibility Principle. When a component (like a handler) depends on another (like a service), which in turn depends on a repository, initializing the entire chain for a single test is complex and violates isolation.

    Mocking solves this by simulating or replacing external modules. This allows you to:

    1. Isolate dependencies: Test logic without needing real databases or network requests.
    2. Control environments: Simulate specific states like system time or error conditions.
    3. Improve efficiency: Avoid time-consuming real-world operations (I/O, network).

    Nunu utilizes the following libraries for mocking:

    • github.com/golang/mock: For generating mocks from Go interfaces.
    • github.com/go-redis/redismock/v9: For mocking Redis queries.
    • github.com/DATA-DOG/go-sqlmock: For mocking SQL drivers.
  7. Use interface-oriented programming for testability

    main

    To use golang/mock, you must follow interface-oriented programming. Instead of having a struct directly represent a service or repository, define an interface first, then implement that interface with a concrete struct. This allows the testing framework to generate a mock implementation of the interface that can be injected into your components.

    Pattern:

    1. Define type MyInterface interface { ... }.
    2. Implement type myStruct struct { ... }.
    3. Provide a constructor that returns the interface type.
    type UserRepository interface {
    	FirstById(id int64) (*model.User, error)
    }
    
    type userRepository struct {
    	*Repository
    }
    
    func NewUserRepository(repository *Repository) *UserRepository {
    	return &UserRepository{
    		Repository: repository,
    	}
    }
  8. Configure dependency injection with Wire

    main

    Nunu uses Google Wire for dependency injection. When adding new components, you must update the providerSet variables in cmd/server/wire.go to include the new factory functions for the HandlerSet, ServiceSet, and RepositorySet.

    // In cmd/server/wire.go
    
    var HandlerSet = wire.NewSet(
    	handler.NewHandler,
    	handler.NewUserHandler,
    	handler.NewOrderHandler, // Add new handler factory
    )
    
    var ServiceSet = wire.NewSet(
    	service.NewService,
    	service.NewUserService,
    	service.NewOrderService, // Add new service factory
    )
    
    var RepositorySet = wire.NewSet(
    	repository.NewDB,
    	repository.NewRedis,
    	repository.NewRepository,
    	repository.NewUserRepository,
    	repository.NewOrderRepository, // Add new repository factory
    )
  9. Create a new Go project with Nunu

    main

    Use the nunu new command to scaffold a new project. By default, it uses the standard GitHub repository template. You can specify alternative templates using the -r flag.

    Available Templates:

    • Basic layout: https://gitee.com/go-nunu/nunu-layout-basic.git
    • Advanced layout: https://gitee.com/go-nunu/nunu-layout-advanced.git
    • Monorepo layout: https://github.com/go-nunu/nunu-layout-monorepo.git
    nunu new projectName
    
    # Using specific templates
    nunu new nomeDoProjeto -r https://gitee.com/go-nunu/nunu-layout-basic.git
    nunu new nomeDoProjeto -r https://gitee.com/go-nunu/nunu-layout-advanced.git
    nunu new nomeDoProjeto -r https://github.com/go-nunu/nunu-layout-monorepo.git
  10. Create component layers in batch

    main
    Nunu allows you to generate a full stack of components (Handler, Service, Repository, and Model) for a specific entity using a single command. This automates the creation of files in the corresponding internal/ directories and provides boilerplate structures.
  11. Generate Swagger documentation

    main

    Nunu supports automatic OpenAPI documentation generation using swag.

    1. Install the tool: go install github.com/swaggo/swag/cmd/swag@latest.
    2. Add standard Swag comments to your handler functions.
    3. Run swag init to generate the documentation files.
    4. Access the UI at http://127.0.0.1:8000/swagger/index.html.