|
| 1 | +# Go Hexagonal Project Coding Standards |
| 2 | + |
| 3 | +## Directory Structure |
| 4 | + |
| 5 | +The project follows the Hexagonal Architecture structure design: |
| 6 | + |
| 7 | +``` |
| 8 | +go-hexagonal/ |
| 9 | +├── adapter/ # Adapter Layer - Connecting domain with external infrastructure |
| 10 | +│ ├── repository/ # Repository implementations |
| 11 | +│ ├── dependency/ # Dependency injection |
| 12 | +│ ├── job/ # Background tasks |
| 13 | +│ └── amqp/ # Message queue |
| 14 | +├── api/ # API Layer - Handling HTTP, gRPC requests |
| 15 | +│ ├── http/ # HTTP API handlers |
| 16 | +│ ├── grpc/ # gRPC API handlers |
| 17 | +│ ├── error_code/ # Error code definitions |
| 18 | +│ └── dto/ # Data Transfer Objects |
| 19 | +├── application/ # Application Layer - Orchestrating business flows |
| 20 | +│ ├── example/ # Example application services |
| 21 | +│ └── core/ # Core interfaces and utilities |
| 22 | +├── domain/ # Domain Layer - Core business logic |
| 23 | +│ ├── service/ # Domain services |
| 24 | +│ ├── repo/ # Repository interfaces |
| 25 | +│ ├── event/ # Domain events |
| 26 | +│ ├── vo/ # Value objects |
| 27 | +│ ├── model/ # Domain models |
| 28 | +│ └── aggregate/ # Aggregate roots |
| 29 | +├── cmd/ # Application entry points |
| 30 | +├── config/ # Configuration |
| 31 | +├── tests/ # Tests |
| 32 | +└── util/ # Utilities |
| 33 | +``` |
| 34 | + |
| 35 | +## Naming Conventions |
| 36 | + |
| 37 | +### Package Naming Conventions |
| 38 | + |
| 39 | +- Use lowercase words, no underscores or mixed case |
| 40 | +- Package names should be short, meaningful nouns |
| 41 | +- Avoid using common variable names as package names |
| 42 | + |
| 43 | +```go |
| 44 | +// Correct |
| 45 | +package repository |
| 46 | +package service |
| 47 | + |
| 48 | +// Incorrect |
| 49 | +package Repository |
| 50 | +package service_impl |
| 51 | +``` |
| 52 | + |
| 53 | +### Variable Naming Conventions |
| 54 | + |
| 55 | +- Local variables: Use camelCase, e.g., `userID` instead of `userid` |
| 56 | +- Global variables: Use camelCase, capitalize first letter if exported |
| 57 | +- Constants: Use all uppercase with underscores, e.g., `MAX_CONNECTIONS` |
| 58 | + |
| 59 | +```go |
| 60 | +// Local variables |
| 61 | +func processUser() { |
| 62 | + userID := 123 |
| 63 | + firstName := "John" |
| 64 | +} |
| 65 | + |
| 66 | +// Global variables |
| 67 | +var ( |
| 68 | + GlobalConfig Configuration |
| 69 | + maxRetryCount = 3 |
| 70 | +) |
| 71 | + |
| 72 | +// Constants |
| 73 | +const ( |
| 74 | + MAX_CONNECTIONS = 100 |
| 75 | + DEFAULT_TIMEOUT = 30 |
| 76 | +) |
| 77 | +``` |
| 78 | + |
| 79 | +### Interface and Struct Naming Conventions |
| 80 | + |
| 81 | +- Interface naming: Usually end with "er", e.g., `Reader`, `Writer` |
| 82 | +- Structs implementing specific interfaces: Should be named after functionality rather than interface name |
| 83 | +- Avoid abbreviations unless they are very common (like HTTP, URL) |
| 84 | + |
| 85 | +```go |
| 86 | +// Interface |
| 87 | +type EventHandler interface { |
| 88 | + Handle(event Event) error |
| 89 | +} |
| 90 | + |
| 91 | +// Implementation |
| 92 | +type LoggingEventHandler struct { |
| 93 | + logger Logger |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +## Code Format and Style |
| 98 | + |
| 99 | +All code must pass `go fmt` and `golangci-lint` checks to ensure consistent style: |
| 100 | + |
| 101 | +```bash |
| 102 | +# Use make commands for checks |
| 103 | +make fmt |
| 104 | +make lint |
| 105 | +``` |
| 106 | + |
| 107 | +### Import Package Ordering |
| 108 | + |
| 109 | +Arrange imports in the following order: |
| 110 | + |
| 111 | +1. Standard library |
| 112 | +2. Third-party packages |
| 113 | +3. Internal project packages |
| 114 | + |
| 115 | +```go |
| 116 | +import ( |
| 117 | + // Standard library |
| 118 | + "context" |
| 119 | + "fmt" |
| 120 | + |
| 121 | + // Third-party packages |
| 122 | + "github.com/gin-gonic/gin" |
| 123 | + "go.uber.org/zap" |
| 124 | + |
| 125 | + // Internal project packages |
| 126 | + "go-hexagonal/domain/model" |
| 127 | + "go-hexagonal/util/log" |
| 128 | +) |
| 129 | +``` |
| 130 | + |
| 131 | +### File Internal Structure |
| 132 | + |
| 133 | +File content should be organized in the following order: |
| 134 | + |
| 135 | +1. Package documentation comments |
| 136 | +2. Package declaration |
| 137 | +3. Import packages |
| 138 | +4. Constants |
| 139 | +5. Variables |
| 140 | +6. Type definitions |
| 141 | +7. Function definitions |
| 142 | + |
| 143 | +## Comment Standards |
| 144 | + |
| 145 | +### Package Comments |
| 146 | + |
| 147 | +Each package should have package comments placed before the package statement: |
| 148 | + |
| 149 | +```go |
| 150 | +// Package repository provides data access implementations |
| 151 | +// for the domain repositories. |
| 152 | +package repository |
| 153 | +``` |
| 154 | + |
| 155 | +### Exported Functions and Types Comments |
| 156 | + |
| 157 | +All exported functions, types, constants, and variables should have comments: |
| 158 | + |
| 159 | +```go |
| 160 | +// ExampleService handles business logic for Example entities. |
| 161 | +// It provides CRUD operations and domain-specific validations. |
| 162 | +type ExampleService struct { |
| 163 | + // fields |
| 164 | +} |
| 165 | + |
| 166 | +// Create creates a new example entity with the given data. |
| 167 | +// It validates the input and publishes an event on successful creation. |
| 168 | +// Returns the created entity or an error if validation fails. |
| 169 | +func (s *ExampleService) Create(ctx context.Context, example *model.Example) (*model.Example, error) { |
| 170 | + // implementation |
| 171 | +} |
| 172 | +``` |
| 173 | + |
| 174 | +## Error Handling Standards |
| 175 | + |
| 176 | +### Using Unified Error Handling Library |
| 177 | + |
| 178 | +The project uses the `util/errors` package for unified error handling: |
| 179 | + |
| 180 | +```go |
| 181 | +import "go-hexagonal/util/errors" |
| 182 | + |
| 183 | +// Creating errors |
| 184 | +if input.Name == "" { |
| 185 | + return nil, errors.NewValidationError("Name cannot be empty", nil) |
| 186 | +} |
| 187 | + |
| 188 | +// Wrapping errors |
| 189 | +result, err := repository.Find(id) |
| 190 | +if err != nil { |
| 191 | + return nil, errors.Wrap(err, errors.ErrorTypePersistence, "Failed to query record") |
| 192 | +} |
| 193 | + |
| 194 | +// Error type checking |
| 195 | +if errors.IsNotFoundError(err) { |
| 196 | + // Handle resource not found case |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +### HTTP Layer Error Handling |
| 201 | + |
| 202 | +The API layer uses a unified error handling middleware: |
| 203 | + |
| 204 | +```go |
| 205 | +// Middleware is configured in router.go |
| 206 | +router.Use(middleware.ErrorHandlerMiddleware()) |
| 207 | +``` |
| 208 | + |
| 209 | +## Testing Standards |
| 210 | + |
| 211 | +### Test Naming Conventions |
| 212 | + |
| 213 | +- Test functions should be named `TestXxx`, where `Xxx` is the name of the function being tested |
| 214 | +- Table-driven test variables should be named `tests` or `testCases` |
| 215 | + |
| 216 | +```go |
| 217 | +func TestExampleService_Create(t *testing.T) { |
| 218 | + tests := []struct { |
| 219 | + name string |
| 220 | + input *model.Example |
| 221 | + mockSetup func(repo *mocks.MockExampleRepo) |
| 222 | + wantErr bool |
| 223 | + expectedErr string |
| 224 | + }{ |
| 225 | + // Test cases |
| 226 | + } |
| 227 | + |
| 228 | + for _, tt := range tests { |
| 229 | + t.Run(tt.name, func(t *testing.T) { |
| 230 | + // Test implementation |
| 231 | + }) |
| 232 | + } |
| 233 | +} |
| 234 | +``` |
| 235 | + |
| 236 | +## CI/CD Standards |
| 237 | + |
| 238 | +The project uses GitHub Actions for continuous integration to ensure code quality: |
| 239 | + |
| 240 | +- Every commit and PR will run code checks and tests |
| 241 | +- Code must pass all checks and tests to be merged |
| 242 | +- It's recommended to use pre-commit hooks to check code quality locally |
| 243 | + |
| 244 | +```bash |
| 245 | +# Install pre-commit hooks |
| 246 | +make pre-commit.install |
| 247 | +``` |
| 248 | + |
| 249 | +## Best Practices |
| 250 | + |
| 251 | +1. **Dependency Injection**: Always use dependency injection, avoid global variables and singletons |
| 252 | +2. **Context Passing**: Always pass context through function calls for cancellation and timeout control |
| 253 | +3. **Error Handling**: Use unified error handling, don't discard errors, wrap errors appropriately |
| 254 | +4. **Test Coverage**: Ensure critical code has sufficient test coverage, use table-driven tests |
| 255 | +5. **Concurrency Safety**: Ensure data structures accessed concurrently are thread-safe |
0 commit comments