算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
golang-architecture【设计与创意】Use this skill when the user says 'Go project structure', 'Golang architecture', 'Go package layout', 'Go clean arch', 'Go folder structure', 'Go module design', 'Go interface design', or when structuring a Go application. This skill enforces: standard Go project layout (cmd/internal/pkg), Clean Architecture with interfaces defined at the consumer si…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Structure Go applications with standard project layout. Interfaces defined by consumers. Dependencies flow inward. Internal packages enforced by compiler.
Exact user phrases: "Go project structure", "Golang architecture", "Go package layout", "Go clean arch", "Go folder structure", "Go module design", "Go interface design", "Go project layout".
Before activating, verify:
No file output. Produces folder structure and code examples as text.
Folder structure:
{project}/
cmd/{service}/main.go
internal/
domain/
application/
infrastructure/
config/
pkg/
api/
Code: show relevant package and types only. No imports lines.
No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.
Folder structure: unlimited. Code: 15 lines per example.
| Criterion | Standard (cmd/internal/pkg) | Flat | Hexagonal (ports/adapters) |
|---|---|---|---|
| Package count | 10+ packages | 1-5 packages | 15+ packages |
| Dependency clarity | Internal enforces boundaries | Manual discipline | Explicit port/adapter |
| Team size | 3+ developers | 1-2 developers | 5+ developers |
| Testing | Unit per package, integration per infra | Package-level | Port-mockable |
| Build time | Slightly slower (many packages) | Fastest | Slower |
Decision: Multi-developer project with clear boundaries → Standard. Small tool/CLI → Flat. Large system with multiple adapters → Hexagonal.
| Approach | Consumer | Implementer | Best For |
|---|---|---|---|
| Consumer-side | Domain defines repo interface | Infrastructure implements | Clean Architecture |
| Implementer-side | Infrastructure exposes interface | Inferface next to impl | Simple CRUD, libraries |
| Shared | Separate domain package | Separate impl package | Large microservices |
Decision: Business logic depends on interface → Consumer-side. Library/external-facade → Implementer-side.
cmd/
server/
main.go -- Entry point. Parse flags, build deps, start server. No logic.
internal/
domain/
entity.go -- Domain entities
repository.go -- Repository interfaces (ports)
service.go -- Domain services
application/
usecase.go -- Use case interfaces + implementations
dto.go -- Data transfer objects
infrastructure/
postgres/
repository.go -- Repository implementations
http/
handler.go -- HTTP handlers
config/
config.go -- Configuration
pkg/ -- Shared libraries (importable by external modules)
api/ -- API definitions (OpenAPI, protobuf)
migrations/
// internal/domain/repository.go -- Interface defined by domain
type UserRepository interface {
FindByID(ctx context.Context, id uuid.UUID) (*User, error)
Save(ctx context.Context, user *User) error
FindByEmail(ctx context.Context, email string) (*User, error)
List(ctx context.Context, offset, limit int) ([]User, int, error)
}
// internal/domain/service.go -- Domain service
type UserService struct {
repo UserRepository
}
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}
func (s *UserService) Register(ctx context.Context, email, name string) (*User, error) {
existing, _ := s.repo.FindByEmail(ctx, email)
if existing != nil {
return nil, fmt.Errorf("register user: %w", ErrEmailAlreadyExists)
}
user := User{ID: uuid.New(), Email: email, Name: name, Active: true}
if err := s.repo.Save(ctx, &user); err != nil {
return nil, fmt.Errorf("register user: %w", err)
}
return &user, nil
}
// internal/infrastructure/postgres/repository.go -- Implements domain interface
type PostgresUserRepository struct {
db *sql.DB
}
func NewPostgresUserRepository(db *sql.DB) *PostgresUserRepository {
return &PostgresUserRepository{db: db}
}
func (r *PostgresUserRepository) FindByID(ctx context.Context, id uuid.UUID) (*User, error) {
row := r.db.QueryRowContext(ctx, "SELECT id, email, name, active, created_at FROM users WHERE id = $1", id)
user := &User{}
err := row.Scan(&user.ID, &user.Email, &user.Name, &user.Active, &user.CreatedAt)
if errors.Is(err, sql.ErrNoRows) {
return nil, fmt.Errorf("find user by id: %w", ErrUserNotFound)
}
return user, err
}
func main() {
cfg := config.Load()
db := connectDB(cfg.DatabaseURL)
defer db.Close()
userRepo := postgres.NewUserRepository(db)
userService := domain.NewUserService(userRepo)
createUserUC := application.NewCreateUserUseCase(userService)
handler := http.NewUserHandler(createUserUC)
server := &http.Server{
Addr: ":" + cfg.Port,
Handler: handler,
}
// Graceful shutdown
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("server error: %v", err)
}
}()
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
server.Shutdown(shutdownCtx)
}
// internal/domain/errors.go -- sentinel errors
var (
ErrUserNotFound = errors.New("user not found")
ErrEmailAlreadyExists = errors.New("email already exists")
ErrValidation = errors.New("validation error")
)
// internal/application -- wrap with context
func (uc *CreateUserUseCase) Execute(ctx context.Context, dto CreateUserDTO) (*UserDTO, error) {
if err := dto.Validate(); err != nil {
return nil, fmt.Errorf("create user: %w", domain.ErrValidation)
}
user, err := uc.userService.Register(ctx, dto.Email, dto.Name)
if err != nil {
return nil, fmt.Errorf("create user: %w", err)
}
return toDTO(user), nil
}
// internal/infrastructure/http -- map to HTTP
func errorResponse(w http.ResponseWriter, err error) {
switch {
case errors.Is(err, domain.ErrUserNotFound):
writeJSON(w, http.StatusNotFound, ErrorResponse{Code: "NOT_FOUND", Message: err.Error()})
case errors.Is(err, domain.ErrEmailAlreadyExists):
writeJSON(w, http.StatusConflict, ErrorResponse{Code: "CONFLICT", Message: err.Error()})
case errors.Is(err, domain.ErrValidation):
writeJSON(w, http.StatusBadRequest, ErrorResponse{Code: "VALIDATION_ERROR", Message: err.Error()})
default:
log.Printf("unhandled error: %v", err)
writeJSON(w, http.StatusInternalServerError, ErrorResponse{Code: "INTERNAL", Message: "An error occurred"})
}
}
// internal/config/config.go
type Config struct {
Port string `envconfig:"PORT" default:"8080"`
DatabaseURL string `envconfig:"DATABASE_URL" required:"true"`
LogLevel string `envconfig:"LOG_LEVEL" default:"info"`
JWTSecret string `envconfig:"JWT_SECRET" required:"true"`
RedisURL string `envconfig:"REDIS_URL"`
}
func Load() (*Config, error) {
var cfg Config
if err := envconfig.Process("", &cfg); err != nil {
return nil, fmt.Errorf("load config: %w", err)
}
return &cfg, nil
}
// internal/infrastructure/http/health.go
type HealthHandler struct {
db *sql.DB
}
func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := h.db.PingContext(r.Context()); err != nil {
writeJSON(w, http.StatusServiceUnavailable, map[string]string{"status": "unhealthy", "error": err.Error()})
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "healthy"})
}
// internal/infrastructure/postgres/transaction.go
type TransactionKey struct{}
type Transaction struct{ *sql.Tx }
func WithTransaction(ctx context.Context, db *sql.DB, fn func(ctx context.Context) error) error {
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
if err != nil {
return fmt.Errorf("begin tx: %w", err)
}
ctx = context.WithValue(ctx, TransactionKey{}, &Transaction{tx})
if err := fn(ctx); err != nil {
if rbErr := tx.Rollback(); rbErr != nil {
return fmt.Errorf("rollback: %v (orig: %w)", rbErr, err)
}
return err
}
return tx.Commit()
}
func GetQuerier(ctx context.Context) Querier {
if tx, ok := ctx.Value(TransactionKey{}).(*Transaction); ok {
return tx
}
return getDB(ctx) // fallback to direct DB
}
// Usage
func (s *UserService) CreateUser(ctx context.Context, user *User) error {
return postgres.WithTransaction(ctx, s.db, func(ctx context.Context) error {
q := postgres.GetQuerier(ctx)
if err := q.InsertUser(ctx, user); err != nil {
return fmt.Errorf("insert user: %w", err)
}
return q.InsertAuditLog(ctx, user.ID, "created")
})
}
// internal/infrastructure/http/middleware.go
type responseWriter struct {
http.ResponseWriter
statusCode int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}
func LoggingMiddleware(log *slog.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(rw, r)
log.InfoContext(r.Context(), "HTTP request",
"method", r.Method,
"path", r.URL.Path,
"status", rw.statusCode,
"duration", time.Since(start).String(),
"ip", r.RemoteAddr,
)
})
}
}
pgx over database/sql for PostgreSQL (faster, connection pooling built-in)sync.Pool for frequently allocated objectspprof — import net/http/pprof behind a build tagSetMaxOpenConns(25), SetMaxIdleConns(10), SetConnMaxLifetime(5*time.Minute)| Anti-Pattern | Why | Fix |
|---|---|---|
init() functions | Unclear execution order, hard to test | Explicit initialization in main() |
utils/common packages | Dependency magnet, unclear purpose | Specific package names |
| Package names != dir name | Confusing imports | Match package name to directory |
| Interface on implementer side | Extra indirection, violates DI principle | Interface at consumer side |
context.Background in libs | Never cancelled | Accept context.Context from caller |
| Global variables | Hidden dependencies, race conditions | Pass via struct fields or parameters |
bcrypt for passwords, never SHA/MD5golang-jwt/jwt/v5, validate alg header to avoid alg confusionrs/cors middleware with explicit originsulule/limiter or middlewarefunc TestUserService_Register_DuplicateEmail(t *testing.T) {
mockRepo := new(MockUserRepository)
mockRepo.On("FindByEmail", mock.Anything, "test@test.com").
Return(&User{Email: "test@test.com"}, nil)
svc := domain.NewUserService(mockRepo)
_, err := svc.Register(context.Background(), "test@test.com", "Test")
assert.ErrorIs(t, err, domain.ErrEmailAlreadyExists)
}
Use testcontainers-go for PostgreSQL. Each test creates its own DB. Use txdb for transaction-based test isolation. Run with go test -tags=integration ./...
func TestDomainDoesNotImportInfrastructure(t *testing.T) {
domainPkg := "github.com/org/project/internal/domain"
infraPkg := "github.com/org/project/internal/infrastructure"
imports, err := pkgimports.List(domainPkg)
require.NoError(t, err)
assert.False(t, imports.Contains(infraPkg), "domain must not import infrastructure")
}
sync.Once to lazily initialize.No artifact produced. Next skill: golang-patterns — concurrency, HTTP servers, error handling. Carry forward: package structure, interface definitions, DI wiring approach.
function createModule<T>(config: ModuleConfig): T { const dependencies = initializeDependencies(config); const module = new Module(dependencies); module.hooks.onInit(); return module as T; }
class ConfigBuilder { private config: AppConfig = new AppConfig(); withDatabase(url: string): ConfigBuilder { ... } withCache(ttl: number): ConfigBuilder { ... } withLogging(level: string): ConfigBuilder { ... } build(): AppConfig { return this.config; } }
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% | Critical | Rollback or fix |
| p95 latency | > 500ms | Warning | Profile and optimize |
| Uptime | < 99.9% | Critical | Investigate infrastructure |
| Memory usage | > 80% | Warning | Check for leaks |
| CPU usage | > 80% | Warning | Scale up or optimize |