Skip to content

Base Project Directory Structure

This guide explains the standard directory structure of a Base framework project. Understanding this structure will help you navigate and organize your code effectively.

Overview

Base follows a modular architecture with clear separation of concerns. The standard project structure after generating a new project with base new <project_name> looks like this:

.
├── app/                  # Application code
│   ├── models/           # Domain models
│   ├── {modules}/        # Feature modules (posts, users, etc.)
│   └── init.go           # Module initialization
├── core/                 # Framework core components
├── docs/                 # API documentation (Swagger)
├── logs/                 # Application logs
├── static/               # Static files
├── storage/              # File storage
├── .air.toml             # Air configuration for hot reload
├── .env                  # Environment variables
├── .env.sample           # Sample environment variables
├── Dockerfile            # Docker configuration
├── go.mod                # Go module definition
├── go.sum                # Go module checksums
└── main.go              # Application entry point

Directory Details

/app Directory

This is where most of your application code lives. It follows the Hierarchical Model-View-Controller (HMVC) pattern.

/app/models

The models directory contains all domain models used across the application:

app/models/
├── user.go            # User model
├── post.go            # Post model
├── comment.go         # Comment model
└── ...

Example model:

go
// app/models/post.go
package models

import (
	"time"

	"gorm.io/gorm"
)

// Post represents a post entity
type Post struct {
	Id        uint           `json:"id" gorm:"primarykey"`
	CreatedAt time.Time      `json:"created_at"`
	UpdatedAt time.Time      `json:"updated_at"`
	DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"index"`
	Title     string         `json:"title"`
	Content   string         `json:"content"`
}

// TableName returns the table name for the Post model
func (item *Post) TableName() string {
	return "posts"
}

// GetId returns the Id of the model
func (item *Post) GetId() uint {
	return item.Id
}

// GetModelName returns the model name
func (item *Post) GetModelName() string {
	return "post"
}

/app/{module}

Each feature is organized into its own module directory (e.g., posts, users, etc.):

app/posts/
├── controller.go      # HTTP handlers & routing
├── service.go         # Business logic
└── module.go          # Module registration

Each module typically contains:

  • controller.go: Handles HTTP requests, validation, Swagger documentation, and RESTful endpoints
  • service.go: Contains business logic, database operations, and emitter events
  • module.go: Registers the module, sets up dependency injection, and wires components together

/app/init.go

This file initializes and registers all modules:

go
// app/init.go
package app

import (
	// Import your modules here
	"base/app/posts"
	
	"base/core/emitter"
	"base/core/logger"
	"base/core/middleware"
	"base/core/module"
	"base/core/storage"
	
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// Initialize modules
func Initialize(db *gorm.DB, router *gin.Engine, log logger.Logger, em *emitter.Emitter, storage *storage.ActiveStorage) {
	// Create API router group
	apiRouter := router.Group("/api")
	
	// Apply middlewares
	middleware.ApplyMiddlewares(apiRouter)
	
	// Register modules
	modules := []module.Module{
		// Register your modules here
		posts.NewPostModule(db, apiRouter, log, em, storage),
		// Add more modules as needed
	}
	
	// Initialize each module
	for _, m := range modules {
		m.Initialize()
	}
}

/core Directory

Contains the framework's core components and utilities:

core/
├── storage/           # File storage system (local, S3, etc.)
├── logger/            # Structured logging utilities
├── emitter/           # Event system for pub/sub functionality
├── middleware/        # HTTP middleware components
├── module/            # Module interfaces and abstractions
├── auth/              # Authentication services
├── types/             # Common type definitions
├── utils/             # Utility functions
└── config/            # Configuration utilities

The core directory contains reusable components that can be imported by your application modules. These components provide the foundation for the Base framework's functionality.

/docs Directory

Contains API documentation generated by Swagger:

docs/
├── docs.go            # Generated Swagger documentation code
├── swagger.json       # Swagger API specification in JSON format
└── swagger.yaml       # Swagger API specification in YAML format

Base uses Swagger for API documentation. The controller files contain Swagger annotations that are used to generate comprehensive API documentation. This makes it easy to document your API as you build it.

/storage Directory

Stores uploaded files and generated content:

storage/
├── uploads/           # User uploads
├── cache/             # Cached data
└── logs/              # Application logs

/.env File

Contains environment-specific configuration:

bash
SERVER_ADDRESS=:8080
JWT_SECRET=your_jwt_secret
API_KEY=your_api_key

# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=postgres
DB_PASSWORD=postgres

# Storage
STORAGE_DRIVER=local
STORAGE_PATH=storage

# Email
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=username
MAIL_PASSWORD=password

/main.go File

The application entry point with Swagger annotations:

go
// main.go
package main

import (
	"base/core"
	_ "base/docs" // Import the Swagger docs
	"fmt"
	"net"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/joho/godotenv"
	log "github.com/sirupsen/logrus"
)

// @title Base API
// @version 1.0
// @description This is the API documentation for Base
// @host localhost:8001
// @BasePath /api
// @schemes http https
// @produces json
// @consumes json

// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-Api-Key
// @description API Key for authentication

// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
// @description Enter your token with the prefix "Bearer "

func getLocalIP() string {
	// Implementation details...
	return "127.0.0.1"
}

func main() {
	// Load the .env file
	if err := godotenv.Load(); err != nil {
		log.Warn("Error loading .env file")
	}

	// Disable Gin's default logger
	gin.SetMode(gin.ReleaseMode)

	// Bootstrap the application
	app, err := core.StartApplication()
	if err != nil {
		log.Fatalf("Failed to bootstrap application: %v", err)
	}

	// Get local IP and format server address
	localIP := getLocalIP()
	addr := app.Config.ServerAddress
	if strings.HasPrefix(addr, ":") {
		addr = "0.0.0.0" + addr
	}

	fmt.Printf("\nServer is running at:\n")
	fmt.Printf("- Local:   http://localhost%s\n", strings.TrimPrefix(addr, "0.0.0.0"))
	fmt.Printf("- Network: http://%s%s\n\n", localIP, strings.TrimPrefix(addr, "0.0.0.0"))

	// Start the server
	if err := app.Router.Run(app.Config.ServerAddress); err != nil {
		log.Fatalf("Failed to start server: %v", err)
	}
}

The main file sets up the application and contains Swagger annotations that define API metadata. The core.StartApplication() function bootstraps the entire application, initializing the database, router, logger, and other core components.

Additional Directories

As your project grows, you might add more directories:

/tests Directory

Contains automated tests:

tests/
├── unit/              # Unit tests
├── integration/       # Integration tests
└── e2e/               # End-to-end tests

/docs Directory

Contains project documentation:

docs/
├── api/               # API documentation
├── guides/            # User guides
└── examples/          # Code examples

/scripts Directory

Contains utility scripts:

scripts/
├── migrations/        # Database migration scripts
├── setup.sh           # Setup script
└── deploy.sh          # Deployment script

Best Practices

  1. Keep Models Centralized: All models should be in the /app/models directory to prevent circular dependencies. This allows modules to reference each other's models without creating import cycles.

  2. Module Independence: Each module should be self-contained with minimal dependencies on other modules. This promotes a clean HMVC (Hierarchical Model-View-Controller) architecture.

  3. Separation of Concerns: Keep controllers thin and move business logic to services. Controllers should only handle HTTP requests, validation, and response formatting, while services handle the actual business logic.

  4. Use Swagger Annotations: Document your API using Swagger annotations in your controller files. This generates comprehensive API documentation automatically.

  5. Leverage the Event System: Use the emitter system for loose coupling between components. This allows modules to communicate without direct dependencies.

  6. Follow REST Conventions: Organize your API endpoints following RESTful conventions (GET for retrieval, POST for creation, PUT for updates, DELETE for removal).

  7. Configuration via Environment: Use environment variables for configuration and load them through the .env file. This makes your application deployable in different environments without code changes.

Conclusion

The Base framework's directory structure is designed to promote clean architecture, modularity, and maintainability. By following this structure, you'll create applications that are easier to understand, test, and extend.

Released under the MIT License.