@el-j/magic-helix-plugins 4.0.0-beta.2 → 4.0.0-beta.3

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 (120) hide show
  1. package/dist/architecture/codeowners.md +123 -0
  2. package/dist/architecture/monorepo.md +146 -0
  3. package/dist/architecture/nx.md +122 -0
  4. package/dist/architecture/turborepo.md +114 -0
  5. package/dist/ci/github-actions.md +268 -0
  6. package/dist/ci/gitlab-ci.md +330 -0
  7. package/dist/containers/docker-multistage.md +120 -0
  8. package/dist/containers/kubernetes-deploy.md +210 -0
  9. package/dist/cpp/index.cjs +79 -0
  10. package/dist/cpp/index.mjs +209 -0
  11. package/dist/csharp/index.cjs +2 -2
  12. package/dist/csharp/{index.js → index.mjs} +17 -11
  13. package/dist/csharp/templates/framework-aspnetcore.md +205 -0
  14. package/dist/csharp/templates/framework-blazor.md +271 -0
  15. package/dist/csharp/templates/lang-csharp.md +162 -0
  16. package/dist/devops/docker-compose.md +111 -0
  17. package/dist/devops/docker-dockerfile.md +94 -0
  18. package/dist/devops/github-actions.md +160 -0
  19. package/dist/devops/gitlab-ci.md +210 -0
  20. package/dist/generic/lang-typescript.md +57 -0
  21. package/dist/generic/state-redux.md +21 -0
  22. package/dist/generic/state-rxjs.md +6 -0
  23. package/dist/generic/style-mui.md +23 -0
  24. package/dist/generic/style-tailwind.md +76 -0
  25. package/dist/generic/test-cypress.md +21 -0
  26. package/dist/generic/test-jest.md +20 -0
  27. package/dist/generic/test-playwright.md +21 -0
  28. package/dist/generic/test-vitest.md +131 -0
  29. package/dist/go/index.cjs +3 -3
  30. package/dist/go/{index.js → index.mjs} +18 -15
  31. package/dist/go/templates/lang-go.md +571 -0
  32. package/dist/index.cjs +1 -1
  33. package/dist/index.mjs +24 -0
  34. package/dist/java/index.cjs +2 -2
  35. package/dist/java/{index.js → index.mjs} +25 -19
  36. package/dist/java/templates/build-gradle.md +102 -0
  37. package/dist/java/templates/build-maven.md +86 -0
  38. package/dist/java/templates/framework-spring-boot.md +179 -0
  39. package/dist/java/templates/lang-java.md +78 -0
  40. package/dist/java/templates/lang-kotlin.md +88 -0
  41. package/dist/meta/magic-helix-meta.md +213 -0
  42. package/dist/meta/meta-debug.md +459 -0
  43. package/dist/meta/meta-implement.md +450 -0
  44. package/dist/meta/meta-roadmap.md +265 -0
  45. package/dist/nodejs/templates/angular-core.md +19 -0
  46. package/dist/nodejs/templates/lang-typescript.md +57 -0
  47. package/dist/nodejs/templates/nestjs-core.md +7 -0
  48. package/dist/nodejs/templates/react-core.md +677 -0
  49. package/dist/nodejs/templates/react-zustand.md +7 -0
  50. package/dist/nodejs/templates/state-redux.md +21 -0
  51. package/dist/nodejs/templates/state-rxjs.md +6 -0
  52. package/dist/nodejs/templates/style-primevue.md +6 -0
  53. package/dist/nodejs/templates/style-quasar.md +22 -0
  54. package/dist/nodejs/templates/style-tailwind.md +76 -0
  55. package/dist/nodejs/templates/test-cypress.md +21 -0
  56. package/dist/nodejs/templates/test-jest.md +20 -0
  57. package/dist/nodejs/templates/test-playwright.md +21 -0
  58. package/dist/nodejs/templates/test-vitest.md +131 -0
  59. package/dist/nodejs/templates/vue-core.md +108 -0
  60. package/dist/nodejs/templates/vue-pinia.md +5 -0
  61. package/dist/patterns/architecture/clean-architecture.md +469 -0
  62. package/dist/patterns/architecture/dependency-injection.md +517 -0
  63. package/dist/patterns/architecture/domain-driven-design.md +621 -0
  64. package/dist/patterns/architecture/layered-architecture.md +382 -0
  65. package/dist/patterns/architecture/repository-pattern.md +408 -0
  66. package/dist/patterns/domain-expertise/nextjs-rules.md +115 -0
  67. package/dist/patterns/domain-expertise/react-patterns.md +181 -0
  68. package/dist/patterns/domain-expertise/server-components.md +212 -0
  69. package/dist/patterns/domain-expertise/shadcn-ui.md +52 -0
  70. package/dist/patterns/domain-expertise/tailwind-patterns.md +52 -0
  71. package/dist/patterns/environment/container-awareness.md +17 -0
  72. package/dist/patterns/environment/ide-features.md +17 -0
  73. package/dist/patterns/environment/os-commands.md +17 -0
  74. package/dist/patterns/organization/heading-hierarchy.md +103 -0
  75. package/dist/patterns/organization/sequential-workflows.md +102 -0
  76. package/dist/patterns/organization/xml-rule-groups.md +64 -0
  77. package/dist/patterns/reasoning/agent-loop.md +151 -0
  78. package/dist/patterns/reasoning/confirmation-gates.md +141 -0
  79. package/dist/patterns/reasoning/dependency-analysis.md +132 -0
  80. package/dist/patterns/reasoning/one-tool-per-iteration.md +152 -0
  81. package/dist/patterns/reasoning/preview-before-action.md +194 -0
  82. package/dist/patterns/reasoning/reflection-checkpoints.md +166 -0
  83. package/dist/patterns/reasoning/result-verification.md +157 -0
  84. package/dist/patterns/reasoning/subtask-breakdown.md +131 -0
  85. package/dist/patterns/reasoning/thinking-tags.md +100 -0
  86. package/dist/patterns/role-definition/capability-declarations.md +72 -0
  87. package/dist/patterns/role-definition/expert-identity.md +45 -0
  88. package/dist/patterns/role-definition/scope-boundaries.md +61 -0
  89. package/dist/patterns/safety/code-safety-rules.md +17 -0
  90. package/dist/patterns/safety/credential-handling.md +17 -0
  91. package/dist/patterns/safety/destructive-warnings.md +17 -0
  92. package/dist/patterns/safety/refusal-messages.md +17 -0
  93. package/dist/patterns/tone/adaptive-tone.md +17 -0
  94. package/dist/patterns/tone/concise-communication.md +17 -0
  95. package/dist/patterns/tone/forbidden-phrases.md +17 -0
  96. package/dist/patterns/tool-guidelines/function-schemas.md +143 -0
  97. package/dist/patterns/tool-guidelines/parameter-examples.md +137 -0
  98. package/dist/patterns/tool-guidelines/usage-policies.md +105 -0
  99. package/dist/php/index.cjs +2 -2
  100. package/dist/php/{index.js → index.mjs} +12 -6
  101. package/dist/php/templates/framework-laravel.md +112 -0
  102. package/dist/php/templates/lang-php.md +94 -0
  103. package/dist/python/index.cjs +4 -4
  104. package/dist/python/{index.js → index.mjs} +10 -7
  105. package/dist/python/templates/lang-python.md +508 -0
  106. package/dist/ruby/index.cjs +2 -2
  107. package/dist/ruby/{index.js → index.mjs} +16 -10
  108. package/dist/ruby/templates/framework-rails.md +309 -0
  109. package/dist/ruby/templates/framework-sinatra.md +227 -0
  110. package/dist/ruby/templates/lang-ruby.md +216 -0
  111. package/dist/rust/index.cjs +3 -3
  112. package/dist/rust/{index.js → index.mjs} +24 -18
  113. package/dist/rust/templates/lang-rust.md +89 -0
  114. package/dist/swift/index.cjs +32 -0
  115. package/dist/swift/index.mjs +112 -0
  116. package/dist/swift/templates/framework-vapor.md +352 -0
  117. package/dist/swift/templates/lang-swift.md +291 -0
  118. package/package.json +31 -21
  119. package/dist/index.js +0 -20
  120. /package/dist/nodejs/{index.js → index.mjs} +0 -0
@@ -0,0 +1,571 @@
1
+ # Language: Go
2
+
3
+ ## Architecture: Clean Architecture with Go Idioms
4
+
5
+ **Organize Go applications with clear boundaries: Handlers → Use Cases → Domain → Infrastructure.**
6
+
7
+ ### Project Structure
8
+
9
+ ```
10
+ project/
11
+ ├── cmd/
12
+ │ └── server/
13
+ │ └── main.go # Application entry point
14
+
15
+ ├── internal/
16
+ │ ├── handlers/ # HTTP handlers (presentation layer)
17
+ │ │ ├── user_handler.go
18
+ │ │ └── order_handler.go
19
+ │ │
20
+ │ ├── usecases/ # Business logic (application layer)
21
+ │ │ ├── user_usecase.go
22
+ │ │ └── order_usecase.go
23
+ │ │
24
+ │ ├── domain/ # Domain models and interfaces
25
+ │ │ ├── user.go # Domain entities
26
+ │ │ ├── order.go
27
+ │ │ └── repository.go # Repository interfaces
28
+ │ │
29
+ │ └── infrastructure/ # External concerns
30
+ │ ├── postgres/
31
+ │ │ └── user_repository.go
32
+ │ └── redis/
33
+ │ └── cache.go
34
+
35
+ └── pkg/ # Public, reusable packages
36
+ ├── validator/
37
+ └── logger/
38
+ ```
39
+
40
+ ### Rule: Handlers Handle HTTP Only
41
+
42
+ **ALWAYS** keep HTTP handlers thin. They parse requests, call use cases, and serialize responses.
43
+
44
+ ```go
45
+ // ✅ Good: Thin handler
46
+ package handlers
47
+
48
+ import (
49
+ "encoding/json"
50
+ "net/http"
51
+ "github.com/yourorg/project/internal/domain"
52
+ "github.com/yourorg/project/internal/usecases"
53
+ )
54
+
55
+ type UserHandler struct {
56
+ userUseCase usecases.UserUseCase // Interface, not concrete type
57
+ }
58
+
59
+ func NewUserHandler(uc usecases.UserUseCase) *UserHandler {
60
+ return &UserHandler{userUseCase: uc}
61
+ }
62
+
63
+ func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
64
+ // Parse request
65
+ var req CreateUserRequest
66
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
67
+ http.Error(w, "Invalid request", http.StatusBadRequest)
68
+ return
69
+ }
70
+
71
+ // Call use case (business logic)
72
+ user, err := h.userUseCase.CreateUser(r.Context(), req.Email, req.Name)
73
+ if err != nil {
74
+ if err == domain.ErrEmailExists {
75
+ http.Error(w, err.Error(), http.StatusConflict)
76
+ return
77
+ }
78
+ http.Error(w, "Internal error", http.StatusInternalServerError)
79
+ return
80
+ }
81
+
82
+ // Serialize response
83
+ w.Header().Set("Content-Type", "application/json")
84
+ w.WriteStatus(http.StatusCreated)
85
+ json.NewEncoder(w).Encode(toUserResponse(user))
86
+ }
87
+
88
+ // ❌ Bad: Business logic in handler
89
+ func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
90
+ var req CreateUserRequest
91
+ json.NewDecoder(r.Body).Decode(&req)
92
+
93
+ // ❌ Validation in handler
94
+ if !strings.Contains(req.Email, "@") {
95
+ http.Error(w, "Invalid email", http.StatusBadRequest)
96
+ return
97
+ }
98
+
99
+ // ❌ Database access in handler
100
+ var existing User
101
+ h.db.QueryRow("SELECT * FROM users WHERE email = $1", req.Email).Scan(&existing)
102
+ if existing.ID != "" {
103
+ http.Error(w, "Email exists", http.StatusConflict)
104
+ return
105
+ }
106
+
107
+ // ❌ Domain logic in handler
108
+ user := User{
109
+ ID: uuid.New().String(),
110
+ Email: req.Email,
111
+ Name: req.Name,
112
+ }
113
+ h.db.Exec("INSERT INTO users (id, email, name) VALUES ($1, $2, $3)",
114
+ user.ID, user.Email, user.Name)
115
+
116
+ json.NewEncoder(w).Encode(user)
117
+ }
118
+ ```
119
+
120
+ ### Rule: Use Cases Orchestrate Business Logic
121
+
122
+ **ALWAYS** put business logic in use cases. Use cases coordinate between repositories and domain models.
123
+
124
+ ```go
125
+ // ✅ internal/usecases/user_usecase.go
126
+ package usecases
127
+
128
+ import (
129
+ "context"
130
+ "github.com/yourorg/project/internal/domain"
131
+ )
132
+
133
+ // UserUseCase interface (for dependency inversion)
134
+ type UserUseCase interface {
135
+ CreateUser(ctx context.Context, email, name string) (*domain.User, error)
136
+ UpdateUserEmail(ctx context.Context, userID, newEmail string) (*domain.User, error)
137
+ }
138
+
139
+ // userUseCase implementation
140
+ type userUseCase struct {
141
+ userRepo domain.UserRepository // Interface from domain layer
142
+ }
143
+
144
+ func NewUserUseCase(userRepo domain.UserRepository) UserUseCase {
145
+ return &userUseCase{userRepo: userRepo}
146
+ }
147
+
148
+ func (uc *userUseCase) CreateUser(ctx context.Context, email, name string) (*domain.User, error) {
149
+ // Validate using domain methods
150
+ if err := domain.ValidateEmail(email); err != nil {
151
+ return nil, err
152
+ }
153
+
154
+ // Business rule: Check uniqueness
155
+ existing, err := uc.userRepo.FindByEmail(ctx, email)
156
+ if err != nil {
157
+ return nil, err
158
+ }
159
+ if existing != nil {
160
+ return nil, domain.ErrEmailExists
161
+ }
162
+
163
+ // Create domain entity
164
+ user := domain.NewUser(email, name)
165
+
166
+ // Persist
167
+ if err := uc.userRepo.Save(ctx, user); err != nil {
168
+ return nil, err
169
+ }
170
+
171
+ return user, nil
172
+ }
173
+
174
+ func (uc *userUseCase) UpdateUserEmail(ctx context.Context, userID, newEmail string) (*domain.User, error) {
175
+ user, err := uc.userRepo.FindByID(ctx, userID)
176
+ if err != nil {
177
+ return nil, err
178
+ }
179
+ if user == nil {
180
+ return nil, domain.ErrUserNotFound
181
+ }
182
+
183
+ // Use domain method (business logic)
184
+ if err := user.ChangeEmail(newEmail); err != nil {
185
+ return nil, err
186
+ }
187
+
188
+ if err := uc.userRepo.Save(ctx, user); err != nil {
189
+ return nil, err
190
+ }
191
+
192
+ return user, nil
193
+ }
194
+ ```
195
+
196
+ ### Rule: Domain Layer Contains Business Rules
197
+
198
+ **ALWAYS** put business rules and invariants in domain entities. Keep them infrastructure-agnostic.
199
+
200
+ ```go
201
+ // ✅ internal/domain/user.go - Domain entity
202
+ package domain
203
+
204
+ import (
205
+ "errors"
206
+ "regexp"
207
+ "strings"
208
+ "time"
209
+ "github.com/google/uuid"
210
+ )
211
+
212
+ // Domain errors (business errors)
213
+ var (
214
+ ErrInvalidEmail = errors.New("invalid email format")
215
+ ErrEmailExists = errors.New("email already exists")
216
+ ErrUserNotFound = errors.New("user not found")
217
+ ErrInvalidName = errors.New("name must be at least 2 characters")
218
+ )
219
+
220
+ // User is a domain entity
221
+ type User struct {
222
+ ID string
223
+ Email string
224
+ Name string
225
+ CreatedAt time.Time
226
+ IsActive bool
227
+ }
228
+
229
+ // NewUser creates a new user with validation
230
+ func NewUser(email, name string) *User {
231
+ return &User{
232
+ ID: uuid.New().String(),
233
+ Email: email,
234
+ Name: name,
235
+ CreatedAt: time.Now().UTC(),
236
+ IsActive: true,
237
+ }
238
+ }
239
+
240
+ // ChangeEmail changes user email with validation
241
+ func (u *User) ChangeEmail(newEmail string) error {
242
+ if err := ValidateEmail(newEmail); err != nil {
243
+ return err
244
+ }
245
+
246
+ if strings.EqualFold(u.Email, newEmail) {
247
+ return errors.New("new email must be different")
248
+ }
249
+
250
+ u.Email = newEmail
251
+ return nil
252
+ }
253
+
254
+ // Deactivate deactivates the user account
255
+ func (u *User) Deactivate() error {
256
+ if !u.IsActive {
257
+ return errors.New("user is already inactive")
258
+ }
259
+
260
+ u.IsActive = false
261
+ return nil
262
+ }
263
+
264
+ // IsAdmin checks if user is admin (business rule)
265
+ func (u *User) IsAdmin() bool {
266
+ return strings.HasSuffix(u.Email, "@company.com")
267
+ }
268
+
269
+ // ValidateEmail validates email format
270
+ func ValidateEmail(email string) error {
271
+ emailRegex := regexp.MustCompile(`^[^\s@]+@[^\s@]+\.[^\s@]+$`)
272
+ if !emailRegex.MatchString(email) {
273
+ return ErrInvalidEmail
274
+ }
275
+ if len(email) > 255 {
276
+ return errors.New("email too long")
277
+ }
278
+ return nil
279
+ }
280
+ ```
281
+
282
+ ### Rule: Repository Interfaces in Domain, Implementation in Infrastructure
283
+
284
+ **ALWAYS** define repository interfaces in domain layer. Implement them in infrastructure layer.
285
+
286
+ ```go
287
+ // ✅ internal/domain/repository.go - Interface in domain
288
+ package domain
289
+
290
+ import "context"
291
+
292
+ // UserRepository defines the contract for user data access
293
+ type UserRepository interface {
294
+ Save(ctx context.Context, user *User) error
295
+ FindByID(ctx context.Context, id string) (*User, error)
296
+ FindByEmail(ctx context.Context, email string) (*User, error)
297
+ Delete(ctx context.Context, id string) error
298
+ }
299
+
300
+ // ✅ internal/infrastructure/postgres/user_repository.go - Implementation
301
+ package postgres
302
+
303
+ import (
304
+ "context"
305
+ "database/sql"
306
+ "errors"
307
+ "github.com/yourorg/project/internal/domain"
308
+ )
309
+
310
+ type userRepository struct {
311
+ db *sql.DB
312
+ }
313
+
314
+ // NewUserRepository creates a new PostgreSQL user repository
315
+ func NewUserRepository(db *sql.DB) domain.UserRepository {
316
+ return &userRepository{db: db}
317
+ }
318
+
319
+ func (r *userRepository) Save(ctx context.Context, user *domain.User) error {
320
+ query := `
321
+ INSERT INTO users (id, email, name, created_at, is_active)
322
+ VALUES ($1, $2, $3, $4, $5)
323
+ ON CONFLICT (id) DO UPDATE
324
+ SET email = $2, name = $3, is_active = $5
325
+ `
326
+
327
+ _, err := r.db.ExecContext(ctx, query,
328
+ user.ID,
329
+ user.Email,
330
+ user.Name,
331
+ user.CreatedAt,
332
+ user.IsActive,
333
+ )
334
+
335
+ return err
336
+ }
337
+
338
+ func (r *userRepository) FindByID(ctx context.Context, id string) (*domain.User, error) {
339
+ query := `SELECT id, email, name, created_at, is_active FROM users WHERE id = $1`
340
+
341
+ var user domain.User
342
+ err := r.db.QueryRowContext(ctx, query, id).Scan(
343
+ &user.ID,
344
+ &user.Email,
345
+ &user.Name,
346
+ &user.CreatedAt,
347
+ &user.IsActive,
348
+ )
349
+
350
+ if errors.Is(err, sql.ErrNoRows) {
351
+ return nil, nil // Not found
352
+ }
353
+ if err != nil {
354
+ return nil, err
355
+ }
356
+
357
+ return &user, nil
358
+ }
359
+
360
+ func (r *userRepository) FindByEmail(ctx context.Context, email string) (*domain.User, error) {
361
+ query := `SELECT id, email, name, created_at, is_active FROM users WHERE email = $1`
362
+
363
+ var user domain.User
364
+ err := r.db.QueryRowContext(ctx, query, email).Scan(
365
+ &user.ID,
366
+ &user.Email,
367
+ &user.Name,
368
+ &user.CreatedAt,
369
+ &user.IsActive,
370
+ )
371
+
372
+ if errors.Is(err, sql.ErrNoRows) {
373
+ return nil, nil
374
+ }
375
+ if err != nil {
376
+ return nil, err
377
+ }
378
+
379
+ return &user, nil
380
+ }
381
+
382
+ func (r *userRepository) Delete(ctx context.Context, id string) error {
383
+ query := `DELETE FROM users WHERE id = $1`
384
+ _, err := r.db.ExecContext(ctx, query, id)
385
+ return err
386
+ }
387
+ ```
388
+
389
+ ### Complete Example: Main Setup (Dependency Injection)
390
+
391
+ ```go
392
+ // cmd/server/main.go - Wire dependencies
393
+ package main
394
+
395
+ import (
396
+ "database/sql"
397
+ "log"
398
+ "net/http"
399
+
400
+ _ "github.com/lib/pq"
401
+ "github.com/yourorg/project/internal/handlers"
402
+ "github.com/yourorg/project/internal/infrastructure/postgres"
403
+ "github.com/yourorg/project/internal/usecases"
404
+ )
405
+
406
+ func main() {
407
+ // Infrastructure: Database
408
+ db, err := sql.Open("postgres", "postgres://user:pass@localhost/db?sslmode=disable")
409
+ if err != nil {
410
+ log.Fatal(err)
411
+ }
412
+ defer db.Close()
413
+
414
+ // Infrastructure: Repositories (concrete implementations)
415
+ userRepo := postgres.NewUserRepository(db)
416
+ orderRepo := postgres.NewOrderRepository(db)
417
+
418
+ // Use Cases (business logic)
419
+ userUseCase := usecases.NewUserUseCase(userRepo)
420
+ orderUseCase := usecases.NewOrderUseCase(orderRepo, userRepo)
421
+
422
+ // Handlers (HTTP layer)
423
+ userHandler := handlers.NewUserHandler(userUseCase)
424
+ orderHandler := handlers.NewOrderHandler(orderUseCase)
425
+
426
+ // Routes
427
+ http.HandleFunc("/users", userHandler.CreateUser)
428
+ http.HandleFunc("/orders", orderHandler.CreateOrder)
429
+
430
+ log.Println("Server starting on :8080")
431
+ log.Fatal(http.ListenAndServe(":8080", nil))
432
+ }
433
+ ```
434
+
435
+ ## Go Best Practices
436
+
437
+ - **ALWAYS** use `gofmt` to format code consistently.
438
+ - **ALWAYS** handle errors explicitly - never ignore them.
439
+ - **ALWAYS** use meaningful variable names (camelCase).
440
+ - **ALWAYS** write comprehensive godoc comments for exported functions/types.
441
+ - **ALWAYS** use `defer` for resource cleanup.
442
+ - **ALWAYS** prefer `struct` embedding over inheritance.
443
+ - **ALWAYS** use interfaces for abstraction and testing.
444
+ - **ALWAYS** use `context.Context` for cancellation and timeouts.
445
+ - **ALWAYS** use `go vet` and `golint` for code quality checks.
446
+ - **ALWAYS** write table-driven tests.
447
+ - **ALWAYS** use `t.Parallel()` for independent test functions.
448
+ - **ALWAYS** use `sync.WaitGroup` or channels for goroutine synchronization.
449
+ - **ALWAYS** avoid global variables - pass dependencies explicitly.
450
+ - **ALWAYS** use `panic` only for unrecoverable errors.
451
+ - **ALWAYS** use `log` package or structured logging for production.
452
+ - **ALWAYS** use `bufio` for efficient I/O operations.
453
+ - **ALWAYS** prefer `strings.Builder` for string concatenation in loops.
454
+ - **ALWAYS** use `time.Time` for time handling, not `int64`.
455
+ - **ALWAYS** use `json` tags for struct field serialization.
456
+ - **ALWAYS** use `iota` for enumerated constants.
457
+ - **ALWAYS** keep functions short and focused on single responsibilities.
458
+
459
+ ## Testing Strategy
460
+
461
+ **Handlers:** Test HTTP contracts (mock use cases)
462
+ ```go
463
+ // internal/handlers/user_handler_test.go
464
+ func TestCreateUser(t *testing.T) {
465
+ mockUseCase := &MockUserUseCase{
466
+ CreateUserFunc: func(ctx context.Context, email, name string) (*domain.User, error) {
467
+ return &domain.User{ID: "123", Email: email, Name: name}, nil
468
+ },
469
+ }
470
+
471
+ handler := NewUserHandler(mockUseCase)
472
+
473
+ req := httptest.NewRequest("POST", "/users", strings.NewReader(`{"email":"test@example.com","name":"Test"}`))
474
+ w := httptest.NewRecorder()
475
+
476
+ handler.CreateUser(w, req)
477
+
478
+ assert.Equal(t, http.StatusCreated, w.Code)
479
+ }
480
+ ```
481
+
482
+ **Use Cases:** Test business logic (mock repositories)
483
+ ```go
484
+ // internal/usecases/user_usecase_test.go
485
+ func TestCreateUser_EmailExists(t *testing.T) {
486
+ mockRepo := &MockUserRepository{
487
+ FindByEmailFunc: func(ctx context.Context, email string) (*domain.User, error) {
488
+ return &domain.User{ID: "existing"}, nil // Email exists
489
+ },
490
+ }
491
+
492
+ uc := NewUserUseCase(mockRepo)
493
+
494
+ _, err := uc.CreateUser(context.Background(), "test@example.com", "Test")
495
+
496
+ assert.Equal(t, domain.ErrEmailExists, err)
497
+ }
498
+ ```
499
+
500
+ **Domain Models:** Test business rules (no mocking)
501
+ ```go
502
+ // internal/domain/user_test.go
503
+ func TestUser_ChangeEmail(t *testing.T) {
504
+ tests := []struct {
505
+ name string
506
+ newEmail string
507
+ expectErr bool
508
+ }{
509
+ {"valid email", "new@example.com", false},
510
+ {"invalid email", "not-an-email", true},
511
+ {"same email", "test@example.com", true},
512
+ }
513
+
514
+ for _, tt := range tests {
515
+ t.Run(tt.name, func(t *testing.T) {
516
+ user := NewUser("test@example.com", "Test")
517
+ err := user.ChangeEmail(tt.newEmail)
518
+
519
+ if tt.expectErr {
520
+ assert.Error(t, err)
521
+ } else {
522
+ assert.NoError(t, err)
523
+ assert.Equal(t, tt.newEmail, user.Email)
524
+ }
525
+ })
526
+ }
527
+ }
528
+ ```
529
+
530
+ **Repositories:** Integration tests with real database
531
+ ```go
532
+ // internal/infrastructure/postgres/user_repository_test.go
533
+ func TestUserRepository_Save(t *testing.T) {
534
+ db := setupTestDB(t) // Helper to create test DB
535
+ defer db.Close()
536
+
537
+ repo := NewUserRepository(db)
538
+ user := domain.NewUser("test@example.com", "Test User")
539
+
540
+ err := repo.Save(context.Background(), user)
541
+ assert.NoError(t, err)
542
+
543
+ found, err := repo.FindByID(context.Background(), user.ID)
544
+ assert.NoError(t, err)
545
+ assert.Equal(t, user.Email, found.Email)
546
+ }
547
+ ```
548
+
549
+ ## Architecture Summary
550
+
551
+ 1. **Handlers** (`internal/handlers/`) = HTTP layer
552
+ - Parse requests, serialize responses
553
+ - Call use cases
554
+ - No business logic
555
+
556
+ 2. **Use Cases** (`internal/usecases/`) = Business logic
557
+ - Orchestrate domain and repositories
558
+ - Application workflows
559
+ - Transaction boundaries
560
+
561
+ 3. **Domain** (`internal/domain/`) = Business rules
562
+ - Entities with behavior
563
+ - Repository interfaces
564
+ - Framework-agnostic
565
+
566
+ 4. **Infrastructure** (`internal/infrastructure/`) = External concerns
567
+ - Repository implementations
568
+ - Database access
569
+ - External APIs
570
+
571
+ **Reference:** See `patterns/architecture/clean-architecture.md`, `patterns/architecture/repository-pattern.md`, and `patterns/architecture/dependency-injection.md` for detailed architectural guidance.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("./BasePlugin-odQJAKA-.cjs"),e=require("./nodejs/index.cjs"),i=require("./go/index.cjs"),u=require("./python/index.cjs"),r=require("./rust/index.cjs"),P=require("./java/index.cjs"),g=require("./ruby/index.cjs"),l=require("./php/index.cjs"),o=require("./csharp/index.cjs");exports.BasePlugin=n.BasePlugin;exports.NodeJSPlugin=e.NodeJSPlugin;exports.GoPlugin=i.GoPlugin;exports.PythonPlugin=u.PythonPlugin;exports.RustPlugin=r.RustPlugin;exports.JavaPlugin=P.JavaPlugin;exports.RubyPlugin=g.RubyPlugin;exports.PHPPlugin=l.PHPPlugin;exports.CSharpPlugin=o.CSharpPlugin;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("./BasePlugin-odQJAKA-.cjs"),n=require("./nodejs/index.cjs"),e=require("./go/index.cjs"),u=require("./python/index.cjs"),r=require("./rust/index.cjs"),P=require("./java/index.cjs"),g=require("./ruby/index.cjs"),l=require("./php/index.cjs"),t=require("./csharp/index.cjs"),o=require("./cpp/index.cjs"),s=require("./swift/index.cjs");exports.BasePlugin=i.BasePlugin;exports.NodeJSPlugin=n.NodeJSPlugin;exports.GoPlugin=e.GoPlugin;exports.PythonPlugin=u.PythonPlugin;exports.RustPlugin=r.RustPlugin;exports.JavaPlugin=P.JavaPlugin;exports.RubyPlugin=g.RubyPlugin;exports.PHPPlugin=l.PHPPlugin;exports.CSharpPlugin=t.CSharpPlugin;exports.CppPlugin=o.CppPlugin;exports.SwiftPlugin=s.SwiftPlugin;
package/dist/index.mjs ADDED
@@ -0,0 +1,24 @@
1
+ import { B as p } from "./BasePlugin-6wv0hYJ9.js";
2
+ import { NodeJSPlugin as P } from "./nodejs/index.mjs";
3
+ import { GoPlugin as u } from "./go/index.mjs";
4
+ import { PythonPlugin as i } from "./python/index.mjs";
5
+ import { RustPlugin as g } from "./rust/index.mjs";
6
+ import { JavaPlugin as m } from "./java/index.mjs";
7
+ import { RubyPlugin as a } from "./ruby/index.mjs";
8
+ import { PHPPlugin as S } from "./php/index.mjs";
9
+ import { CSharpPlugin as y } from "./csharp/index.mjs";
10
+ import { CppPlugin as C } from "./cpp/index.mjs";
11
+ import { SwiftPlugin as R } from "./swift/index.mjs";
12
+ export {
13
+ p as BasePlugin,
14
+ y as CSharpPlugin,
15
+ C as CppPlugin,
16
+ u as GoPlugin,
17
+ m as JavaPlugin,
18
+ P as NodeJSPlugin,
19
+ S as PHPPlugin,
20
+ i as PythonPlugin,
21
+ a as RubyPlugin,
22
+ g as RustPlugin,
23
+ R as SwiftPlugin
24
+ };
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("../BasePlugin-odQJAKA-.cjs");class l extends o.BasePlugin{constructor(){super(...arguments),this.name="java",this.displayName="Java",this.version="3.0.0",this.priority=75}async detect(e){return this.fileExists(e,"pom.xml")?this.detectMaven(e):this.fileExists(e,"build.gradle")||this.fileExists(e,"build.gradle.kts")?this.detectGradle(e):null}getTemplates(){return[{name:"java-core",tags:["java"],content:`# Java Development Guidelines
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("node:path"),c=require("../BasePlugin-odQJAKA-.cjs");function d(n){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const t in n)if(t!=="default"){const a=Object.getOwnPropertyDescriptor(n,t);Object.defineProperty(e,t,a.get?a:{enumerable:!0,get:()=>n[t]})}}return e.default=n,Object.freeze(e)}const g=d(l);class m extends c.BasePlugin{constructor(){super(...arguments),this.name="java",this.displayName="Java",this.version="3.0.0",this.priority=75}async detect(e){return this.fileExists(e,"pom.xml")?this.detectMaven(e):this.fileExists(e,"build.gradle")||this.fileExists(e,"build.gradle.kts")?this.detectGradle(e):null}getTemplates(){return[{name:"java-core",tags:["java"],content:()=>this.loadTemplateFromFile(g.join(__dirname,"templates/lang-java.md")).then(e=>e||this.getJavaFallbackTemplate())}]}getJavaFallbackTemplate(){return`# Java Development Guidelines
2
2
 
3
3
  This project uses Java.
4
4
 
@@ -20,4 +20,4 @@ This project uses Java.
20
20
  ## Testing
21
21
  - Write JUnit tests
22
22
  - Use Mockito for mocking
23
- - Aim for good test coverage`}]}getDependencyTagMap(){return{"org.springframework.boot:spring-boot":"spring-boot","spring-boot-starter":"spring-boot",junit:"junit"}}detectMaven(e){const t=this.readFile(e,"pom.xml");if(!t)return{language:"Java",name:this.getProjectName(e),dependencies:{},manifestFile:"pom.xml",projectPath:e};const n=t.match(/<artifactId>([^<]+)<\/artifactId>/),s=t.match(/<description>([^<]+)<\/description>/),a={},i=t.matchAll(/<dependency>[\s\S]*?<groupId>([^<]+)<\/groupId>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?(?:<version>([^<]+)<\/version>)?/g);for(const r of i)a[`${r[1]}:${r[2]}`]=r[3]||"*";return{language:"Java",name:n?.[1]||this.getProjectName(e),description:s?.[1],dependencies:a,manifestFile:"pom.xml",projectPath:e}}detectGradle(e){const t=this.fileExists(e,"build.gradle")?"build.gradle":"build.gradle.kts",n=this.readFile(e,t),s={};if(n){const a=n.matchAll(/(?:implementation|api|testImplementation)\s*['"]([^:'"]+):([^:'"]+):?([^'"]*)['"]/g);for(const i of a)s[`${i[1]}:${i[2]}`]=i[3]||"*"}return{language:"Java/Kotlin",name:this.getProjectName(e),dependencies:s,manifestFile:t,projectPath:e}}}exports.JavaPlugin=l;
23
+ - Aim for good test coverage`}getDependencyTagMap(){return{"org.springframework.boot:spring-boot":"spring-boot","spring-boot-starter":"spring-boot",junit:"junit"}}detectMaven(e){const t=this.readFile(e,"pom.xml");if(!t)return{language:"Java",name:this.getProjectName(e),dependencies:{},manifestFile:"pom.xml",projectPath:e};const a=t.match(/<artifactId>([^<]+)<\/artifactId>/),s=t.match(/<description>([^<]+)<\/description>/),r={},i=t.matchAll(/<dependency>[\s\S]*?<groupId>([^<]+)<\/groupId>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?(?:<version>([^<]+)<\/version>)?/g);for(const o of i)r[`${o[1]}:${o[2]}`]=o[3]||"*";return{language:"Java",name:a?.[1]||this.getProjectName(e),description:s?.[1],dependencies:r,manifestFile:"pom.xml",projectPath:e}}detectGradle(e){const t=this.fileExists(e,"build.gradle")?"build.gradle":"build.gradle.kts",a=this.readFile(e,t),s={};if(a){const r=a.matchAll(/(?:implementation|api|testImplementation)\s*['"]([^:'"]+):([^:'"]+):?([^'"]*)['"]/g);for(const i of r)s[`${i[1]}:${i[2]}`]=i[3]||"*"}return{language:"Java/Kotlin",name:this.getProjectName(e),dependencies:s,manifestFile:t,projectPath:e}}}exports.JavaPlugin=m;