Simple projects
For small projects or prototypes, everything can live in a single file:Standard layout
For larger applications, separate concerns into packages:main.go
The entry point sets up the app and routes:handlers/users.go
Handlers focus on HTTP concerns:services/user_service.go
Services contain business logic:models/user.go
Models define data structures:Clean architecture
For complex applications, use a layered architecture:Benefits
| Layer | Responsibility | Dependencies |
|---|---|---|
| cmd | Start the app | internal, pkg |
| handler | HTTP request/response | service |
| service | Business rules | repository, domain |
| repository | Database operations | domain |
| domain | Core types | none |
Dependency injection
Pass dependencies explicitly:Static files and templates
Include static files and templates:embed.go
main.go
Configuration
Use environment variables with sensible defaults:Testing
Keep tests next to the code they test:Best practices
- Keep main.go small - Only setup and wiring
- Handlers are thin - Extract business logic to services
- One package per concern - Donβt mix handlers with models
- Use interfaces at boundaries - Makes testing easier
- Prefer explicit over implicit - Pass dependencies, donβt use globals
- Group related routes - Use
app.Group()for organization
Anti-patterns to avoid
- God packages -
utils,helpers,common - Circular imports - Usually means wrong package boundaries
- Business logic in handlers - Keep handlers focused on HTTP
- Global state - Makes testing difficult