Claude Code Plugins

Community-maintained marketplace

Feedback

brokle-backend-dev

@brokle-ai/brokle
2
0

Use this skill when developing, implementing, or modifying Go backend code for the Brokle platform. This includes creating services, repositories, handlers, domain entities, API endpoints, middleware, workers, or any other Go backend components. Invoke this skill at the start of backend development tasks.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name brokle-backend-dev
description Use this skill when developing, implementing, or modifying Go backend code for the Brokle platform. This includes creating services, repositories, handlers, domain entities, API endpoints, middleware, workers, or any other Go backend components. Invoke this skill at the start of backend development tasks.

Brokle Backend Development Skill

This skill provides comprehensive guidance for Go backend development following Brokle's scalable monolith architecture with Domain-Driven Design.

Architecture Overview

Scalable Monolith with Independent Scaling

  • Separate Binaries: HTTP server (cmd/server) + Background workers (cmd/worker)
  • Shared Codebase: Single codebase with modular DI container
  • Multi-Database Strategy: PostgreSQL (transactional) + ClickHouse (analytics) + Redis (cache/queues)
  • Domain-Driven Design: Clean layer separation with domain-driven architecture

Application Structure

internal/
├── core/
│   ├── domain/{domain}/      # Entities, interfaces (see internal/core/domain/)
│   └── services/{domain}/    # Business logic implementations
├── infrastructure/
│   ├── database/
│   │   ├── postgres/repository/
│   │   └── clickhouse/repository/
│   └── repository/           # Main repository layer
├── transport/http/
│   ├── handlers/{domain}/
│   ├── middleware/
│   └── server.go
├── app/                      # DI container
└── workers/                  # Background jobs

Domains

Primary domains in internal/core/domain/:

  • auth - Authentication, sessions, API keys
  • billing - Usage tracking, subscriptions
  • common - Shared transaction patterns, utilities
  • gateway - AI provider routing and management
  • observability - Traces, spans, quality scores
  • organization - Multi-tenant org management
  • user - User management and profiles

Pattern: Each domain has entities.go, repository.go, service.go, errors.go Reference: See internal/core/domain/ directory for complete list and implementation status

Critical Development Patterns

1. Domain Alias Pattern (MANDATORY)

Always use professional domain aliases for imports:

// ✅ Correct
import (
    "context"
    "fmt"

    "gorm.io/gorm"

    authDomain "brokle/internal/core/domain/auth"
    orgDomain "brokle/internal/core/domain/organization"
    userDomain "brokle/internal/core/domain/user"
    "brokle/pkg/ulid"
)

// ❌ Incorrect
import (
    "brokle/internal/core/domain/auth"
)

Standard Domain Aliases:

Domain Alias Usage
auth authDomain authDomain.User, authDomain.ErrNotFound
organization orgDomain orgDomain.Organization
user userDomain userDomain.User
billing billingDomain billingDomain.Subscription
observability obsDomain obsDomain.Trace

2. Industrial Error Handling (MANDATORY)

Three-Layer Pattern: Repository (Domain Errors) → Service (AppErrors) → Handler (HTTP Response)

Repository Layer (Standard Pattern - errors.Is() for GORM):

All repositories use errors.Is() for GORM error checking (standardized for wrapped error compatibility).

Standard Pattern:

import "errors"

if errors.Is(err, gorm.ErrRecordNotFound) {  // ✅ Standardized
    return nil, fmt.Errorf("context: %w", domainError)
}

Don't Use:

if err == gorm.ErrRecordNotFound {  // ❌ Old pattern - don't use

Example:

func (r *userRepository) GetByID(ctx context.Context, id ulid.ULID) (*authDomain.User, error) {
    var user authDomain.User
    err := r.db.WithContext(ctx).Where("id = ?", id).First(&user).Error
    if err != nil {
        if errors.Is(err, gorm.ErrRecordNotFound) {  // ✅ Standardized pattern
            return nil, fmt.Errorf("get user by ID %s: %w", id, authDomain.ErrNotFound)
        }
        return nil, fmt.Errorf("database query failed for user ID %s: %w", id, err)
    }
    return &user, nil
}

Service Layer:

func (s *userService) GetUser(ctx context.Context, id ulid.ULID) (*GetUserResponse, error) {
    user, err := s.userRepo.GetByID(ctx, id)
    if err != nil {
        if errors.Is(err, authDomain.ErrNotFound) {
            return nil, appErrors.NewNotFoundError("User not found")
        }
        return nil, appErrors.NewInternalError("Failed to retrieve user")
    }
    return &GetUserResponse{User: user}, nil
}

Handler Layer:

func (h *userHandler) GetUser(c *gin.Context) {
    userID := c.Param("id")
    id, err := ulid.Parse(userID)
    if err != nil {
        response.Error(c, appErrors.NewValidationError("Invalid user ID format", "id must be a valid ULID"))
        return
    }

    resp, err := h.userService.GetUser(c.Request.Context(), id)
    if err != nil {
        response.Error(c, err)  // Automatic HTTP mapping
        return
    }

    response.Success(c, resp)
}

Common AppError Constructors (see pkg/errors/errors.go for complete list):

  • appErrors.NewValidationError(message, details string)
  • appErrors.NewNotFoundError(resource string)
  • appErrors.NewConflictError(message string)
  • appErrors.NewUnauthorizedError(message string)
  • appErrors.NewForbiddenError(message string)
  • appErrors.NewInternalError(message string, err error)
  • appErrors.NewRateLimitError(message string)

3. Authentication Patterns

Two Authentication Systems:

SDK Routes (/v1/*): API Key Authentication

// Middleware: SDKAuthMiddleware
// Extract context: middleware.GetSDKAuthContext(c)
// Rate limiting: API key-based

sdkRoutes := r.Group("/v1")
sdkRoutes.Use(sdkAuthMiddleware.RequireSDKAuth())
sdkRoutes.Use(rateLimitMiddleware.RateLimitByAPIKey())

Dashboard Routes (/api/v1/*): JWT Authentication

// Middleware: Authentication()
// Extract user: middleware.GetUserID(c)
// Rate limiting: IP + user-based

// Middleware instances injected via DI (server.go:219-223)
protected := router.Group("")
protected.Use(s.authMiddleware.RequireAuth())           // Instance method - JWT validation
protected.Use(s.csrfMiddleware.ValidateCSRF())          // Instance method - CSRF protection
protected.Use(s.rateLimitMiddleware.RateLimitByUser()) // Instance method - User rate limiting

API Key Format: bk_{40_char_random} with SHA-256 hashing

4. Testing Philosophy (CRITICAL)

Test Business Logic, Not Framework Behavior

What to Test:

  • Complex business logic and calculations
  • Batch operations and orchestration workflows
  • Error handling patterns and retry mechanisms
  • Analytics, aggregations, and metrics
  • Multi-step operations with dependencies

What NOT to Test:

  • Simple CRUD operations without business logic
  • Field validation (already in domain layer)
  • Trivial constructors and getters
  • Framework behavior (ULID, time.Now(), errors.Is)
  • Static constant definitions

Test Pattern (Table-Driven):

func TestUserService_CreateUser(t *testing.T) {
    tests := []struct {
        name        string
        input       *CreateUserRequest
        mockSetup   func(*MockUserRepository)
        expectedErr error
        checkResult func(*testing.T, *CreateUserResponse)
    }{
        {
            name: "success - valid user",
            input: &CreateUserRequest{
                Email: "test@example.com",
                Name:  "Test User",
            },
            mockSetup: func(repo *MockUserRepository) {
                repo.On("Create", mock.Anything, mock.Anything).Return(nil)
            },
            expectedErr: nil,
            checkResult: func(t *testing.T, resp *CreateUserResponse) {
                assert.NotNil(t, resp)
                assert.NotEqual(t, ulid.ULID{}, resp.User.ID)
            },
        },
        // More test cases...
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            mockRepo := new(MockUserRepository)
            tt.mockSetup(mockRepo)

            service := NewUserService(mockRepo)
            result, err := service.CreateUser(context.Background(), tt.input)

            if tt.expectedErr != nil {
                assert.ErrorIs(t, err, tt.expectedErr)
            } else {
                assert.NoError(t, err)
            }

            if tt.checkResult != nil {
                tt.checkResult(t, result)
            }

            mockRepo.AssertExpectations(t)
        })
    }
}

Code Templates

See code-templates.md for complete repository, service, and handler templates.

Development Commands

# Start development stack
make dev                    # Full stack (server + worker)
make dev-server            # HTTP server only
make dev-worker            # Workers only

# Testing
make test                  # All tests
make test-unit            # Unit tests only
make test-integration     # Integration tests

# Database
make migrate-up           # Run migrations
make migrate-status       # Check status
make seed-dev            # Load dev data

# Code quality
make lint                # Lint all code
make fmt                 # Format code

Key References

When working on specific areas, invoke these specialized skills:

  • Domain Architecture: Use brokle-domain-architecture skill
  • API Routes: Use brokle-api-routes skill
  • Error Handling: Use brokle-error-handling skill
  • Testing: Use brokle-testing skill
  • Migrations: Use brokle-migration-workflow skill

Documentation

  • Architecture Overview: CLAUDE.md
  • Error Handling: docs/development/ERROR_HANDLING_GUIDE.md
  • Domain Patterns: docs/development/DOMAIN_ALIAS_PATTERNS.md
  • API Development: docs/development/API_DEVELOPMENT_GUIDE.md
  • Testing Guide: docs/TESTING.md

Development Workflow

  1. Analyze First: Use Read, Grep, Glob to understand existing patterns
  2. Find Similar Code: Search for existing implementations
  3. Follow Patterns: Match the established architecture
  4. Test Business Logic: Write pragmatic tests for complex logic only
  5. Review Errors: Ensure proper error handling across all layers

Multi-Tenant Scoping

All entities must be organization-scoped:

type Project struct {
    ID             ulid.ULID
    OrganizationID ulid.ULID  // Required for multi-tenancy
    Name           string
    // ... other fields
}

Enterprise Edition Pattern

Use build tags for enterprise features:

# OSS build
go build ./cmd/server

# Enterprise build
go build -tags="enterprise" ./cmd/server

Enterprise features go in internal/ee/ with stub implementations for OSS.

Quick Decision Tree

Creating new functionality?

  1. Is it a new domain? → Use brokle-domain-architecture skill
  2. Is it an API endpoint? → Use brokle-api-routes skill
  3. Is it complex business logic? → Follow service layer pattern + write tests
  4. Does it need database schema? → Use brokle-migration-workflow skill

Debugging errors?

  1. Check error handling layer (repo vs service vs handler)
  2. Verify domain alias imports
  3. Ensure AppError constructors in services
  4. Confirm response.Error() in handlers

Adding tests?

  1. Is it complex business logic? → Write tests
  2. Is it simple CRUD? → Skip tests
  3. Use table-driven test pattern
  4. Mock complete interfaces
  5. Verify expectations with AssertExpectations(t)