Project Structure Best Practices in Go
A well-organized project structure is crucial for maintainability, scalability, and collaboration. This guide covers best practices for structuring Go projects.
Core Principles
1. Package Organization
Go projects should follow these package organization principles:
Package Names:
- Use simple, clear, lowercase names
- Avoid underscores or mixedCaps
- Name should reflect purpose
- Keep names short but meaningful
Package Structure:
- Group related functionality
- Avoid circular dependencies
- Maintain clear boundaries
- Design for reusability
Import Organization:
- Standard library imports first
- Third-party imports second
- Local package imports last
- Use blank lines between groups
Standard Project Layout
A typical Go project should follow this structure:
project-root/
├── cmd/ # Main applications
│ └── myapp/ # Application-specific code
│ └── main.go # Application entry point
├── internal/ # Private code
│ ├── auth/ # Authentication package
│ ├── database/ # Database package
│ └── server/ # Server package
├── pkg/ # Public libraries
│ ├── models/ # Data models
│ └── utils/ # Utility functions
├── api/ # API definitions
│ └── openapi/ # OpenAPI/Swagger specs
├── web/ # Web assets
│ ├── static/ # Static files
│ └── templates/ # HTML templates
├── configs/ # Configuration files
├── scripts/ # Build and automation scripts
├── test/ # Additional test files
├── docs/ # Documentation
├── examples/ # Example code
├── third_party/ # Third-party tools
├── vendor/ # Vendored dependencies
├── .gitignore # Git ignore file
├── go.mod # Go modules file
├── go.sum # Go modules checksum
├── Makefile # Build automation
└── README.md # Project documentation
Directory Purposes
cmd/
- Contains main applications
- Each subdirectory is a separate executable
- Keep main.go simple and focused
- Move logic to internal/ or pkg/
internal/
- Private application code
- Not importable by other projects
- Protected by Go compiler
- Perfect for business logic
pkg/
- Public libraries
- Importable by other projects
- Well-documented APIs
- Stable interfaces
api/
- API definitions
- Protocol documentation
- OpenAPI/Swagger specs
- gRPC protocol definitions
Package Design Principles
1. Package Cohesion
Packages should have a single, well-defined purpose:
// Good: focused package
package validation
type Validator interface {
Validate() error
}
// Bad: mixed concerns
package utils // Avoid catch-all packages
2. Package Coupling
Minimize dependencies between packages:
// Good: clear dependencies
package orders
import (
"myapp/internal/customers"
"myapp/internal/products"
)
// Bad: circular dependencies
package a
import "myapp/internal/b"
package b
import "myapp/internal/a"
3. Package Interfaces
Define interfaces at package boundaries:
// Good: interface in consuming package
package service
type Repository interface {
Find(id string) (*Entity, error)
Save(entity *Entity) error
}
// Bad: concrete dependencies
type Service struct {
repo *postgres.Repository // Avoid concrete types
}
Application Structure Patterns
1. Layer Pattern
Organize code by technical responsibility:
internal/
├── handlers/ # HTTP handlers
├── services/ # Business logic
├── repositories/ # Data access
└── models/ # Data structures
2. Domain-Driven Design
Organize code by business domain:
internal/
├── ordering/ # Order management
├── catalog/ # Product catalog
├── shipping/ # Shipping logic
└── billing/ # Payment processing
3. Clean Architecture
Organize code by dependency direction:
internal/
├── domain/ # Business entities
├── usecase/ # Application logic
├── interface/ # External interfaces
└── infrastructure/ # Implementation details
Best Practices
1. Dependency Management
- Use Go modules for dependency management
- Vendor dependencies when needed
- Keep dependencies up to date
- Regularly audit dependencies
2. Configuration Management
- Use environment variables for configuration
- Keep configs in a dedicated package
- Support multiple environments
- Use strong typing for config
3. Testing Organization
- Keep tests alongside code
- Use table-driven tests
- Organize integration tests separately
- Maintain test helpers
Common Anti-patterns
1. Avoid Deep Package Hierarchies
// Bad
import "myapp/pkg/services/handlers/api/v1/users"
// Good
import "myapp/pkg/users"
2. Avoid Utility Packages
// Bad
package utils
// Good
package stringutil
package timeutil
3. Avoid Package Name Stuttering
// Bad
package stringutil
func stringutil.StringConvert()
// Good
package stringutil
func Convert()
Project Examples
1. Basic Service
myservice/
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── api/
│ ├── service/
│ └── storage/
├── pkg/
│ └── models/
└── configs/
2. Microservice
microservice/
├── cmd/
│ └── api/
├── internal/
│ ├── domain/
│ ├── ports/
│ └── adapters/
├── pkg/
│ └── shared/
└── api/
└── proto/
Next Steps
- Learn about Error Handling
- Explore Configuration Management
- Study Code Style