ga-plugins-cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/config-patcher.d.ts +20 -50
  2. package/dist/config-patcher.d.ts.map +1 -1
  3. package/dist/config-patcher.js +138 -102
  4. package/dist/config-patcher.js.map +1 -1
  5. package/dist/index.js +75 -22
  6. package/dist/index.js.map +1 -1
  7. package/dist/installer.d.ts +0 -18
  8. package/dist/installer.d.ts.map +1 -1
  9. package/dist/installer.js +19 -39
  10. package/dist/installer.js.map +1 -1
  11. package/dist/types.d.ts +10 -6
  12. package/dist/types.d.ts.map +1 -1
  13. package/dist/uninstaller.d.ts +0 -23
  14. package/dist/uninstaller.d.ts.map +1 -1
  15. package/dist/uninstaller.js +22 -68
  16. package/dist/uninstaller.js.map +1 -1
  17. package/package.json +3 -2
  18. package/plugins/go-reviewer/.claude-plugin/plugin.json +12 -0
  19. package/plugins/go-reviewer/commands/go-review.md +424 -0
  20. package/plugins/go-reviewer/mcp-servers/go-reviewer-mcp/README.md +236 -0
  21. package/plugins/go-reviewer/mcp-servers/go-reviewer-mcp/main.go +678 -0
  22. package/plugins/go-scaffolder/.claude-plugin/plugin.json +12 -0
  23. package/plugins/go-scaffolder/commands/scaffold-service.md +802 -0
  24. package/plugins/go-scaffolder/reference-service/.env.example +27 -0
  25. package/plugins/go-scaffolder/reference-service/Dockerfile +55 -0
  26. package/plugins/go-scaffolder/reference-service/REFERENCE-SERVICE-NOTICE.md +104 -0
  27. package/plugins/go-scaffolder/reference-service/cmd/server/main.go +266 -0
  28. package/plugins/go-scaffolder/reference-service/config/config.go +67 -0
  29. package/plugins/go-scaffolder/reference-service/go.mod +17 -0
  30. package/plugins/go-scaffolder/reference-service/internal/domain/booking.go +118 -0
  31. package/plugins/go-scaffolder/reference-service/internal/handler/booking.go +242 -0
  32. package/plugins/go-scaffolder/reference-service/internal/handler/booking_test.go +451 -0
  33. package/plugins/go-scaffolder/reference-service/internal/repository/booking_postgres.go +124 -0
  34. package/plugins/go-scaffolder/reference-service/internal/usecase/booking.go +181 -0
  35. package/plugins/go-standards/.claude-plugin/plugin.json +22 -0
  36. package/plugins/go-standards/commands/go-standards-check.md +232 -0
  37. package/plugins/go-standards/skills/concurrency.md +336 -0
  38. package/plugins/go-standards/skills/config.md +267 -0
  39. package/plugins/go-standards/skills/error-handling.md +286 -0
  40. package/plugins/go-standards/skills/http-chi.md +390 -0
  41. package/plugins/go-standards/skills/logging-observability.md +340 -0
  42. package/plugins/go-standards/skills/naming-and-style.md +315 -0
  43. package/plugins/go-standards/skills/project-layout.md +313 -0
  44. package/plugins/go-standards/skills/testing.md +366 -0
  45. package/plugins/java2go-porter/.claude-plugin/plugin.json +21 -0
  46. package/plugins/java2go-porter/agents/analyzer.md +232 -0
  47. package/plugins/java2go-porter/agents/reviewer.md +241 -0
  48. package/plugins/java2go-porter/agents/test-pairer.md +365 -0
  49. package/plugins/java2go-porter/agents/translator.md +419 -0
  50. package/plugins/java2go-porter/commands/port-java-service.md +149 -0
  51. package/plugins/java2go-porter/skills/idiom-mapping.md +75 -0
  52. package/plugins/migration-safety/.claude-plugin/plugin.json +20 -0
  53. package/plugins/migration-safety/commands/gen-characterization-test.md +452 -0
  54. package/plugins/migration-safety/commands/strangler-plan.md +356 -0
  55. package/plugins/migration-safety/skills/strangler-fig.md +382 -0
@@ -0,0 +1,340 @@
1
+ # Logging and Observability Standard
2
+
3
+ The observability stack is Grafana + Prometheus + Loki + Tempo. Every
4
+ service must emit structured logs (JSON → Loki), Prometheus metrics, and
5
+ OpenTelemetry traces (→ Tempo) with trace IDs correlated across all three.
6
+
7
+ ---
8
+
9
+ ## Structured Logging with zap
10
+
11
+ Use `go.uber.org/zap` in production mode. JSON output is required for Loki
12
+ ingestion. Never use `fmt.Println`, `log.Println`, or `log.Printf` in
13
+ production code paths.
14
+
15
+ ### Logger initialization
16
+
17
+ ```go
18
+ // cmd/server/main.go
19
+ import "go.uber.org/zap"
20
+
21
+ func buildLogger(level, serviceName, serviceVersion string) (*zap.Logger, error) {
22
+ cfg := zap.NewProductionConfig()
23
+
24
+ // Parse level
25
+ var zapLevel zap.AtomicLevel
26
+ if err := zapLevel.UnmarshalText([]byte(level)); err != nil {
27
+ return nil, fmt.Errorf("invalid log level %q: %w", level, err)
28
+ }
29
+ cfg.Level = zapLevel
30
+
31
+ // Always add service-level fields for Loki label extraction
32
+ cfg.InitialFields = map[string]interface{}{
33
+ "service": serviceName,
34
+ "version": serviceVersion,
35
+ }
36
+
37
+ logger, err := cfg.Build()
38
+ if err != nil {
39
+ return nil, fmt.Errorf("building logger: %w", err)
40
+ }
41
+ return logger, nil
42
+ }
43
+ ```
44
+
45
+ ### Passing the logger
46
+
47
+ Pass `*zap.Logger` via constructor — never use a global logger variable.
48
+
49
+ ```go
50
+ repo := repository.NewUserRepository(db, logger)
51
+ uc := usecase.NewUserUsecase(repo, logger)
52
+ h := handler.NewUserHandler(uc, logger)
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Log Levels
58
+
59
+ | Level | When to use |
60
+ |-------|-------------|
61
+ | `Error` | Something went wrong that requires operator attention. Always include `zap.Error(err)`. |
62
+ | `Warn` | Degraded state — the service is working but something is off (retries, fallback used). |
63
+ | `Info` | Business-significant events (user created, order placed, payment processed). |
64
+ | `Debug` | Developer-oriented detail (SQL queries, cache hits). Disabled in production. |
65
+
66
+ ### DO
67
+
68
+ ```go
69
+ // Error — actionable failure with structured context
70
+ logger.Error("create user failed",
71
+ zap.Error(err),
72
+ zap.String("email", input.Email),
73
+ zap.String("request_id", requestID),
74
+ zap.String("trace_id", traceID),
75
+ )
76
+
77
+ // Info — business event, structured fields
78
+ logger.Info("user created",
79
+ zap.String("user_id", user.ID),
80
+ zap.String("email", user.Email),
81
+ zap.Duration("duration", time.Since(start)),
82
+ )
83
+
84
+ // Warn — degraded but continuing
85
+ logger.Warn("cache miss, falling back to db",
86
+ zap.String("key", cacheKey),
87
+ zap.Duration("cache_latency", latency),
88
+ )
89
+
90
+ // Debug — dev only, never in prod
91
+ logger.Debug("executing sql",
92
+ zap.String("query", query),
93
+ zap.Any("args", args),
94
+ )
95
+ ```
96
+
97
+ ### DO NOT
98
+
99
+ ```go
100
+ fmt.Println("user created: " + user.ID) // WRONG: unstructured, lost in Loki
101
+ log.Printf("error: %v", err) // WRONG: unstructured stdlib log
102
+ logger.Info(fmt.Sprintf("user %s created", id)) // WRONG: interpolated string not queryable
103
+ logger.Error("something went wrong") // WRONG: no zap.Error(err), not actionable
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Trace ID Correlation
109
+
110
+ Every log entry for a request must include the trace ID so Grafana can link
111
+ Loki logs to a Tempo trace. Extract the trace ID from the span and inject it
112
+ into a child logger at the handler level.
113
+
114
+ ```go
115
+ // internal/handler/user_handler.go
116
+ import (
117
+ "go.opentelemetry.io/otel/trace"
118
+ "go.uber.org/zap"
119
+ )
120
+
121
+ func (h *UserHandler) Create(w http.ResponseWriter, r *http.Request) {
122
+ ctx := r.Context()
123
+ span := trace.SpanFromContext(ctx)
124
+ traceID := span.SpanContext().TraceID().String()
125
+
126
+ // Create a request-scoped logger with trace_id baked in
127
+ log := h.logger.With(
128
+ zap.String("trace_id", traceID),
129
+ zap.String("request_id", middleware.GetReqID(ctx)),
130
+ )
131
+
132
+ user, err := h.uc.Create(ctx, input)
133
+ if err != nil {
134
+ log.Error("create user", zap.Error(err))
135
+ respondError(w, err)
136
+ return
137
+ }
138
+
139
+ log.Info("user created", zap.String("user_id", user.ID))
140
+ respondJSON(w, http.StatusCreated, user)
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Prometheus Metrics
147
+
148
+ Register metrics at package init. Use the naming convention
149
+ `<service>_<subsystem>_<name>_<unit>`.
150
+
151
+ ```go
152
+ // internal/metrics/metrics.go
153
+ package metrics
154
+
155
+ import (
156
+ "github.com/prometheus/client_golang/prometheus"
157
+ "github.com/prometheus/client_golang/prometheus/promauto"
158
+ )
159
+
160
+ var (
161
+ HTTPRequestsTotal = promauto.NewCounterVec(
162
+ prometheus.CounterOpts{
163
+ Name: "user_service_http_requests_total",
164
+ Help: "Total number of HTTP requests by method, path, and status.",
165
+ },
166
+ []string{"method", "path", "status"},
167
+ )
168
+
169
+ HTTPRequestDuration = promauto.NewHistogramVec(
170
+ prometheus.HistogramOpts{
171
+ Name: "user_service_http_duration_seconds",
172
+ Help: "HTTP request duration in seconds.",
173
+ Buckets: prometheus.DefBuckets, // .005 .01 .025 .05 .1 .25 .5 1 2.5 5 10
174
+ },
175
+ []string{"method", "path"},
176
+ )
177
+
178
+ ActiveConnections = promauto.NewGauge(prometheus.GaugeOpts{
179
+ Name: "user_service_active_connections",
180
+ Help: "Number of active HTTP connections.",
181
+ })
182
+
183
+ DBQueryDuration = promauto.NewHistogramVec(
184
+ prometheus.HistogramOpts{
185
+ Name: "user_service_db_query_duration_seconds",
186
+ Help: "Database query duration in seconds.",
187
+ Buckets: []float64{.001, .005, .01, .025, .05, .1, .5, 1},
188
+ },
189
+ []string{"operation", "table"},
190
+ )
191
+ )
192
+ ```
193
+
194
+ ### Metrics middleware
195
+
196
+ ```go
197
+ // internal/handler/middleware/metrics.go
198
+ package middleware
199
+
200
+ import (
201
+ "net/http"
202
+ "strconv"
203
+ "time"
204
+
205
+ "github.com/go-chi/chi/v5"
206
+ chimiddleware "github.com/go-chi/chi/v5/middleware"
207
+
208
+ "github.com/zokypesch/go-ga-lib/internal/metrics"
209
+ )
210
+
211
+ func PrometheusMetrics(next http.Handler) http.Handler {
212
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
213
+ start := time.Now()
214
+ ww := chimiddleware.NewWrapResponseWriter(w, r.ProtoMajor)
215
+
216
+ next.ServeHTTP(ww, r)
217
+
218
+ routePattern := chi.RouteContext(r.Context()).RoutePattern()
219
+ duration := time.Since(start).Seconds()
220
+ status := strconv.Itoa(ww.Status())
221
+
222
+ metrics.HTTPRequestsTotal.WithLabelValues(r.Method, routePattern, status).Inc()
223
+ metrics.HTTPRequestDuration.WithLabelValues(r.Method, routePattern).Observe(duration)
224
+ })
225
+ }
226
+ ```
227
+
228
+ ---
229
+
230
+ ## OpenTelemetry Tracing (→ Tempo)
231
+
232
+ ```go
233
+ // config/otel.go
234
+ package config
235
+
236
+ import (
237
+ "context"
238
+ "fmt"
239
+
240
+ "go.opentelemetry.io/otel"
241
+ "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
242
+ "go.opentelemetry.io/otel/propagation"
243
+ "go.opentelemetry.io/otel/sdk/resource"
244
+ sdktrace "go.opentelemetry.io/otel/sdk/trace"
245
+ semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
246
+ )
247
+
248
+ func InitTracer(ctx context.Context, endpoint, serviceName, serviceVersion string) (func(context.Context) error, error) {
249
+ exporter, err := otlptracehttp.New(ctx,
250
+ otlptracehttp.WithEndpoint(endpoint),
251
+ otlptracehttp.WithInsecure(),
252
+ )
253
+ if err != nil {
254
+ return nil, fmt.Errorf("creating OTLP exporter: %w", err)
255
+ }
256
+
257
+ res, err := resource.New(ctx,
258
+ resource.WithAttributes(
259
+ semconv.ServiceName(serviceName),
260
+ semconv.ServiceVersion(serviceVersion),
261
+ ),
262
+ )
263
+ if err != nil {
264
+ return nil, fmt.Errorf("creating resource: %w", err)
265
+ }
266
+
267
+ tp := sdktrace.NewTracerProvider(
268
+ sdktrace.WithBatcher(exporter),
269
+ sdktrace.WithResource(res),
270
+ )
271
+
272
+ otel.SetTracerProvider(tp)
273
+ otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
274
+ propagation.TraceContext{},
275
+ propagation.Baggage{},
276
+ ))
277
+
278
+ return tp.Shutdown, nil
279
+ }
280
+ ```
281
+
282
+ ### Using spans in usecases
283
+
284
+ ```go
285
+ // internal/usecase/user_usecase.go
286
+ import "go.opentelemetry.io/otel"
287
+
288
+ var tracer = otel.Tracer("user-service/usecase")
289
+
290
+ func (uc *UserUsecase) Create(ctx context.Context, input domain.CreateUserInput) (*domain.User, error) {
291
+ ctx, span := tracer.Start(ctx, "UserUsecase.Create")
292
+ defer span.End()
293
+
294
+ span.SetAttributes(
295
+ attribute.String("user.email", input.Email),
296
+ )
297
+
298
+ user, err := uc.repo.Create(ctx, &domain.User{Email: input.Email})
299
+ if err != nil {
300
+ span.RecordError(err)
301
+ span.SetStatus(codes.Error, err.Error())
302
+ return nil, fmt.Errorf("create user: %w", err)
303
+ }
304
+
305
+ span.SetAttributes(attribute.String("user.id", user.ID))
306
+ return user, nil
307
+ }
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Standard Metric Naming
313
+
314
+ ```
315
+ <service_name>_http_requests_total counter {method, path, status}
316
+ <service_name>_http_duration_seconds histogram {method, path}
317
+ <service_name>_active_connections gauge
318
+ <service_name>_db_query_duration_seconds histogram {operation, table}
319
+ <service_name>_cache_hits_total counter {cache}
320
+ <service_name>_cache_misses_total counter {cache}
321
+ <service_name>_external_calls_total counter {service, operation, status}
322
+ <service_name>_external_call_duration_seconds histogram {service, operation}
323
+ ```
324
+
325
+ ---
326
+
327
+ ## DO NOT
328
+
329
+ ```go
330
+ fmt.Println("user created") // WRONG: unstructured
331
+ log.Println("error:", err) // WRONG: stdlib log
332
+ logger.Info("user " + id + " created") // WRONG: interpolated, not queryable
333
+ logger.Error("failed") // WRONG: no zap.Error(err)
334
+ prometheus.MustRegister(counter) // WRONG in tests — use promauto in prod
335
+ // WRONG: registering metrics inside a function called per request
336
+ func (h *Handler) Create(...) {
337
+ counter := prometheus.NewCounter(...) // WRONG: re-registers every call, panics
338
+ prometheus.MustRegister(counter)
339
+ }
340
+ ```
@@ -0,0 +1,315 @@
1
+ # Naming and Style Standard
2
+
3
+ Go naming conventions differ fundamentally from Java. This skill maps the
4
+ Java patterns you know to their idiomatic Go equivalents, and calls out the
5
+ anti-patterns that must not appear in migrated code.
6
+
7
+ ---
8
+
9
+ ## Package Names
10
+
11
+ Packages are lowercase, single words, no underscores, no plurals.
12
+ The package name IS the namespace — it will prefix every exported name at
13
+ the call site, so make the combination read naturally.
14
+
15
+ ### DO
16
+
17
+ ```
18
+ handler not handlerService, not handlers
19
+ usecase not useCaseService, not use_case
20
+ repository not repositoryLayer, not repo_layer (repo is acceptable as abbreviation)
21
+ domain not models, not entities, not dto
22
+ config not configuration, not conf_loader
23
+ ```
24
+
25
+ ```go
26
+ // Reads naturally: handler.NewUserHandler, usecase.NewUserUsecase
27
+ package handler
28
+ package usecase
29
+ package repository
30
+ package domain
31
+ package config
32
+ package metrics
33
+ ```
34
+
35
+ ### DO NOT
36
+
37
+ ```go
38
+ package handlerService // WRONG: redundant suffix
39
+ package userHandlers // WRONG: plural and redundant
40
+ package util // WRONG: meaningless catch-all
41
+ package common // WRONG: a dumping ground
42
+ package helpers // WRONG: Java holdover
43
+ package base // WRONG: no abstract base classes in Go
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Exported Type Names
49
+
50
+ Types are nouns. Service types include their role only when it disambiguates.
51
+
52
+ ### DO
53
+
54
+ ```go
55
+ type User struct { ... }
56
+ type Order struct { ... }
57
+ type UserHandler struct { ... } // Handler suffix is the role — ok
58
+ type UserUsecase struct { ... } // Usecase suffix distinguishes from domain.User — ok
59
+ type UserRepository struct { ... } // Repository suffix distinguishes from domain.User — ok
60
+ ```
61
+
62
+ ### DO NOT
63
+
64
+ ```go
65
+ type UserManager struct { ... } // WRONG: Manager = vague, Java-ism
66
+ type UserHelper struct { ... } // WRONG: Helper = vague
67
+ type UserUtil struct { ... } // WRONG: Util = vague
68
+ type AbstractUser struct { ... } // WRONG: no abstract classes
69
+ type BaseHandler struct { ... } // WRONG: use composition, not inheritance
70
+ type IUserRepository interface { } // WRONG: I prefix is C#/Java; Go uses plain names
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Interface Naming
76
+
77
+ Single-method interfaces: method name + `er`.
78
+ Multi-method interfaces: describe the capability as a noun phrase.
79
+
80
+ ### DO
81
+
82
+ ```go
83
+ type Storer interface {
84
+ Store(ctx context.Context, key string, value []byte) error
85
+ }
86
+
87
+ type Reader interface {
88
+ Read(ctx context.Context, key string) ([]byte, error)
89
+ }
90
+
91
+ type ReadWriter interface {
92
+ Reader
93
+ Writer
94
+ }
95
+
96
+ // Domain repository — multi-method, describes the contract
97
+ type UserRepository interface {
98
+ FindByID(ctx context.Context, id string) (*User, error)
99
+ Create(ctx context.Context, u *User) (*User, error)
100
+ Update(ctx context.Context, u *User) error
101
+ Delete(ctx context.Context, id string) error
102
+ }
103
+
104
+ // Domain usecase — multi-method
105
+ type UserUsecase interface {
106
+ Get(ctx context.Context, id string) (*User, error)
107
+ Create(ctx context.Context, input CreateUserInput) (*User, error)
108
+ }
109
+ ```
110
+
111
+ ### DO NOT
112
+
113
+ ```go
114
+ type IUserRepository interface { ... } // WRONG: I prefix (Java/C#)
115
+ type UserRepositoryInterface interface { ... } // WRONG: Interface suffix
116
+ type AbstractUserService interface { ... } // WRONG: Abstract prefix
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Receiver Names
122
+
123
+ Short, consistent, based on the type name. Never use `self` or `this`.
124
+
125
+ ### DO
126
+
127
+ ```go
128
+ type UserHandler struct { ... }
129
+ func (h *UserHandler) Create(...) { } // h for Handler
130
+ func (h *UserHandler) Get(...) { } // consistent across all methods
131
+
132
+ type UserUsecase struct { ... }
133
+ func (uc *UserUsecase) Create(...) { } // uc for Usecase
134
+
135
+ type userRepository struct { ... }
136
+ func (r *userRepository) Create(...) { } // r for Repository
137
+ ```
138
+
139
+ ### DO NOT
140
+
141
+ ```go
142
+ func (self *UserHandler) Create(...) { } // WRONG: self is Python
143
+ func (this *UserHandler) Create(...) { } // WRONG: this is Java/C++
144
+ func (userHandler *UserHandler) Create(...) { } // WRONG: too verbose
145
+ func (u *UserHandler) Create(...) { } // WRONG: u is also used for *User — ambiguous
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Constructor Naming
151
+
152
+ Always `NewX`. Returns `*X` and optionally an error if initialization can fail.
153
+
154
+ ### DO
155
+
156
+ ```go
157
+ // Simple — no error possible
158
+ func NewUserHandler(uc domain.UserUsecase, logger *zap.Logger) *UserHandler {
159
+ return &UserHandler{uc: uc, logger: logger}
160
+ }
161
+
162
+ // Complex — returns error if init can fail
163
+ func NewUserRepository(db *sql.DB) (*userRepository, error) {
164
+ if db == nil {
165
+ return nil, errors.New("db must not be nil")
166
+ }
167
+ if err := db.Ping(); err != nil {
168
+ return nil, fmt.Errorf("pinging db: %w", err)
169
+ }
170
+ return &userRepository{db: db}, nil
171
+ }
172
+ ```
173
+
174
+ ### DO NOT
175
+
176
+ ```go
177
+ func CreateUserHandler(...) *UserHandler { } // WRONG: Create prefix
178
+ func BuildUserHandler(...) *UserHandler { } // WRONG: Build prefix (use Builder pattern only when warranted)
179
+ func GetUserHandler(...) *UserHandler { } // WRONG: Get prefix
180
+ func MakeUserHandler(...) *UserHandler { } // WRONG: Make prefix (reserved for make() built-in)
181
+ func UserHandlerFactory(...) *UserHandler { } // WRONG: Factory suffix
182
+ ```
183
+
184
+ ---
185
+
186
+ ## No Getters and Setters
187
+
188
+ Go structs expose fields directly unless access control or lazy
189
+ initialization requires otherwise. Getters named `GetFoo()` are a Java
190
+ habit; write `Foo()` if an accessor is needed at all.
191
+
192
+ ### DO
193
+
194
+ ```go
195
+ type User struct {
196
+ ID string
197
+ Email string
198
+ Name string
199
+ }
200
+
201
+ // Direct field access — idiomatic
202
+ user.Email = "new@example.com"
203
+ fmt.Println(user.Name)
204
+
205
+ // Method with meaning — not a getter
206
+ func (u *User) DisplayName() string {
207
+ if u.Name != "" {
208
+ return u.Name
209
+ }
210
+ return u.Email
211
+ }
212
+ ```
213
+
214
+ ### DO NOT
215
+
216
+ ```go
217
+ // WRONG: Java-style getters/setters
218
+ func (u *User) GetID() string { return u.ID } // WRONG
219
+ func (u *User) SetID(id string) { u.ID = id } // WRONG
220
+ func (u *User) GetEmail() string { return u.Email } // WRONG
221
+ func (u *User) SetEmail(e string) { u.Email = e } // WRONG
222
+ ```
223
+
224
+ Exception: implement the `fmt.Stringer` interface as `String()`, not
225
+ `ToString()`. Implement `json.Marshaler` as `MarshalJSON()`, etc. Standard
226
+ library interface methods are exempt from the no-getter rule.
227
+
228
+ ---
229
+
230
+ ## No Abstract Base Classes — Use Composition
231
+
232
+ Go has no inheritance. Share behavior via interfaces and embedding, not
233
+ abstract classes.
234
+
235
+ ### DO
236
+
237
+ ```go
238
+ // Shared logging behavior via embedding
239
+ type baseHandler struct {
240
+ logger *zap.Logger
241
+ }
242
+
243
+ func (b *baseHandler) logError(ctx context.Context, op string, err error) {
244
+ b.logger.Error(op, zap.Error(err), zap.String("request_id", middleware.GetReqID(ctx)))
245
+ }
246
+
247
+ type UserHandler struct {
248
+ baseHandler // embedded — inherits logError
249
+ uc domain.UserUsecase
250
+ }
251
+
252
+ type OrderHandler struct {
253
+ baseHandler
254
+ oc domain.OrderUsecase
255
+ }
256
+ ```
257
+
258
+ ### DO NOT
259
+
260
+ ```go
261
+ // WRONG: Java-style abstract base
262
+ type AbstractHandler struct {
263
+ logger *zap.Logger
264
+ }
265
+ // Extending via struct embedding is fine — naming it Abstract is not
266
+ ```
267
+
268
+ ---
269
+
270
+ ## File Organization
271
+
272
+ One file per major type. Split by responsibility, not by pattern.
273
+
274
+ ### DO
275
+
276
+ ```
277
+ internal/handler/
278
+ user_handler.go // UserHandler and its methods
279
+ order_handler.go // OrderHandler and its methods
280
+ health_handler.go // HealthHandler
281
+ respond.go // shared response helpers
282
+ decode.go // shared decoding helpers
283
+ middleware/
284
+ auth.go
285
+ metrics.go
286
+ ```
287
+
288
+ ### DO NOT
289
+
290
+ ```
291
+ internal/handler/
292
+ handlers.go // WRONG: all handlers in one file
293
+ interfaces.go // WRONG: don't separate interfaces into their own file
294
+ models.go // WRONG: models belong in domain/
295
+ utils.go // WRONG: no utils files
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Quick Reference: Java Anti-Pattern → Go Idiomatic
301
+
302
+ | Java anti-pattern | Go idiom |
303
+ |-------------------|----------|
304
+ | `UserManager` class | `UserUsecase` or inline in handler |
305
+ | `UserHelper` / `UserUtil` | function in the package that uses it |
306
+ | `AbstractBaseHandler` | embedded struct (unnamed or descriptively named) |
307
+ | `IUserRepository` interface | `UserRepository` interface (no I prefix) |
308
+ | `getUserById()` method | `Get` method or `FindByID` method |
309
+ | `setEmail(String email)` | `user.Email = email` |
310
+ | `UserDTO` / `UserVO` | input/output structs inline: `CreateUserInput`, `UserResponse` |
311
+ | `UserServiceImpl` | `userUsecase` (unexported concrete, exported interface) |
312
+ | `@Override` method | satisfies interface implicitly — no annotation |
313
+ | `final` field | unexported field or struct value semantics |
314
+ | `Optional<User>` | `(*User, error)` return pair |
315
+ | `@Builder` (Lombok) | functional options (`WithTimeout`) or plain struct literal |