Skip to content

Feature/improve #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ root = true

[*]
charset = utf-8
indent_size = 4
end_of_line = lf
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true

[Makefile]
indent_style = tab
trim_trailing_whitespace = true

[*.go]
indent_style = tab
indent_style = space
indent_size = 4

[*.{json,yml,yaml}]
indent_size = 2
9 changes: 4 additions & 5 deletions .github/workflows/github-actions-demo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ name: CI/CD with Docker for Golang

on:
push:
branches:
- master
branches: [ main, master ]
pull_request:
branches:
- master
branches: [ main, master ]

jobs:
build-and-test:
Expand Down Expand Up @@ -43,7 +41,8 @@ jobs:
go-version: 1.23
- name: Install dependencies
run: go mod tidy

- name: Build
run: go build -v -o hexagonal-app ./cmd/main.go
- name: Run Tests
run: go test -v ./...

Expand Down
34 changes: 30 additions & 4 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ linters:
- wrapcheck
- testpackage
- tagliatelle
- nosnakecase
- nlreturn
- funlen
- gofumpt
- gochecknoglobals
- gocognit
- godox
- gomnd
- lll
- wsl
- forbidigo
Expand All @@ -21,8 +19,36 @@ linters:
- gci
- dogsled
- gochecknoinits
- scopelint
- tagalign
- depguard
- cyclop
- nosprintfhostport
- mnd
- tagalign

linters-settings:
goimports:
local-prefixes: go-hexagonal
revive:
rules:
- name: var-naming
- name: exported
arguments:
- "disableStutteringCheck"
- name: package-comments
- name: dot-imports
- name: blank-imports
- name: context-keys-type
- name: context-as-argument
- name: error-return
- name: error-strings
- name: error-naming
- name: increment-decrement
- name: var-declaration
- name: range
- name: receiver-naming
- name: time-naming
- name: indent-error-flow
- name: empty-block
- name: superfluous-else
- name: modifies-parameter
- name: unreachable-code
10 changes: 8 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ repos:
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.1
hooks:
- id: no-go-testing
- id: golangci-lint
- id: go-fmt
- id: go-imports
- id: go-unit-tests
- id: go-build
- id: go-mod-tidy
- repo: https://github.com/detailyang/pre-commit-shell
rev: 1.0.5
hooks:
Expand All @@ -25,3 +27,7 @@ repos:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ['@commitlint/config-conventional']
- repo: https://github.com/golangci/golangci-lint
rev: v1.64.8
hooks:
- id: golangci-lint
255 changes: 255 additions & 0 deletions CODING_STYLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
# Go Hexagonal Project Coding Standards

## Directory Structure

The project follows the Hexagonal Architecture structure design:

```
go-hexagonal/
├── adapter/ # Adapter Layer - Connecting domain with external infrastructure
│ ├── repository/ # Repository implementations
│ ├── dependency/ # Dependency injection
│ ├── job/ # Background tasks
│ └── amqp/ # Message queue
├── api/ # API Layer - Handling HTTP, gRPC requests
│ ├── http/ # HTTP API handlers
│ ├── grpc/ # gRPC API handlers
│ ├── error_code/ # Error code definitions
│ └── dto/ # Data Transfer Objects
├── application/ # Application Layer - Orchestrating business flows
│ ├── example/ # Example application services
│ └── core/ # Core interfaces and utilities
├── domain/ # Domain Layer - Core business logic
│ ├── service/ # Domain services
│ ├── repo/ # Repository interfaces
│ ├── event/ # Domain events
│ ├── vo/ # Value objects
│ ├── model/ # Domain models
│ └── aggregate/ # Aggregate roots
├── cmd/ # Application entry points
├── config/ # Configuration
├── tests/ # Tests
└── util/ # Utilities
```

## Naming Conventions

### Package Naming Conventions

- Use lowercase words, no underscores or mixed case
- Package names should be short, meaningful nouns
- Avoid using common variable names as package names

```go
// Correct
package repository
package service

// Incorrect
package Repository
package service_impl
```

### Variable Naming Conventions

- Local variables: Use camelCase, e.g., `userID` instead of `userid`
- Global variables: Use camelCase, capitalize first letter if exported
- Constants: Use all uppercase with underscores, e.g., `MAX_CONNECTIONS`

```go
// Local variables
func processUser() {
userID := 123
firstName := "John"
}

// Global variables
var (
GlobalConfig Configuration
maxRetryCount = 3
)

// Constants
const (
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30
)
```

### Interface and Struct Naming Conventions

- Interface naming: Usually end with "er", e.g., `Reader`, `Writer`
- Structs implementing specific interfaces: Should be named after functionality rather than interface name
- Avoid abbreviations unless they are very common (like HTTP, URL)

```go
// Interface
type EventHandler interface {
Handle(event Event) error
}

// Implementation
type LoggingEventHandler struct {
logger Logger
}
```

## Code Format and Style

All code must pass `go fmt` and `golangci-lint` checks to ensure consistent style:

```bash
# Use make commands for checks
make fmt
make lint
```

### Import Package Ordering

Arrange imports in the following order:

1. Standard library
2. Third-party packages
3. Internal project packages

```go
import (
// Standard library
"context"
"fmt"

// Third-party packages
"github.com/gin-gonic/gin"
"go.uber.org/zap"

// Internal project packages
"go-hexagonal/domain/model"
"go-hexagonal/util/log"
)
```

### File Internal Structure

File content should be organized in the following order:

1. Package documentation comments
2. Package declaration
3. Import packages
4. Constants
5. Variables
6. Type definitions
7. Function definitions

## Comment Standards

### Package Comments

Each package should have package comments placed before the package statement:

```go
// Package repository provides data access implementations
// for the domain repositories.
package repository
```

### Exported Functions and Types Comments

All exported functions, types, constants, and variables should have comments:

```go
// ExampleService handles business logic for Example entities.
// It provides CRUD operations and domain-specific validations.
type ExampleService struct {
// fields
}

// Create creates a new example entity with the given data.
// It validates the input and publishes an event on successful creation.
// Returns the created entity or an error if validation fails.
func (s *ExampleService) Create(ctx context.Context, example *model.Example) (*model.Example, error) {
// implementation
}
```

## Error Handling Standards

### Using Unified Error Handling Library

The project uses the `util/errors` package for unified error handling:

```go
import "go-hexagonal/util/errors"

// Creating errors
if input.Name == "" {
return nil, errors.NewValidationError("Name cannot be empty", nil)
}

// Wrapping errors
result, err := repository.Find(id)
if err != nil {
return nil, errors.Wrap(err, errors.ErrorTypePersistence, "Failed to query record")
}

// Error type checking
if errors.IsNotFoundError(err) {
// Handle resource not found case
}
```

### HTTP Layer Error Handling

The API layer uses a unified error handling middleware:

```go
// Middleware is configured in router.go
router.Use(middleware.ErrorHandlerMiddleware())
```

## Testing Standards

### Test Naming Conventions

- Test functions should be named `TestXxx`, where `Xxx` is the name of the function being tested
- Table-driven test variables should be named `tests` or `testCases`

```go
func TestExampleService_Create(t *testing.T) {
tests := []struct {
name string
input *model.Example
mockSetup func(repo *mocks.MockExampleRepo)
wantErr bool
expectedErr string
}{
// Test cases
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test implementation
})
}
}
```

## CI/CD Standards

The project uses GitHub Actions for continuous integration to ensure code quality:

- Every commit and PR will run code checks and tests
- Code must pass all checks and tests to be merged
- It's recommended to use pre-commit hooks to check code quality locally

```bash
# Install pre-commit hooks
make pre-commit.install
```

## Best Practices

1. **Dependency Injection**: Always use dependency injection, avoid global variables and singletons
2. **Context Passing**: Always pass context through function calls for cancellation and timeout control
3. **Error Handling**: Use unified error handling, don't discard errors, wrap errors appropriately
4. **Test Coverage**: Ensure critical code has sufficient test coverage, use table-driven tests
5. **Concurrency Safety**: Ensure data structures accessed concurrently are thread-safe
Loading