# Go‑Platform Boilerplate Play‑book  
**“Plug‑in‑friendly SaaS/Enterprise Platform – Go Edition”**  

## 1 ARCHITECTURAL IMPERATIVES (Go‑flavoured)

| Principle | Go‑specific rationale | Enforcement Technique |
|-----------|-----------------------|------------------------|
| **Hexagonal Architecture** | Go’s package‑level visibility (`internal/`) naturally creates a *boundary* between core and plug‑ins. | Keep all **domain** code in `internal/domain`, expose only **interfaces** in `pkg/`. |
| **Dependency Injection (DI) via Constructors** | Go avoids reflection‑heavy containers; compile‑time wiring is preferred. | Use **uber‑go/fx** (runtime graph) *or* **uber‑go/dig** for optional runtime DI. For a lighter weight solution, use plain **constructor injection** with a small **registry**. |
| **microMicroservices Architecture** | Each service is independently deployable from day one. Services communicate via gRPC/HTTP through service clients. | Each service has its own entry point (`cmd/{service}/`), Go module (`go.mod`), database connection, and deployment. Services discover each other via Consul service registry. |
| **Plugin‑first design** | Go’s `plugin` package allows runtime loading of compiled `.so` files (Linux/macOS). | Provide an **IModule** interface and a **loader** that discovers `*.so` files (or compiled‑in modules for CI). |
| **API‑First (OpenAPI + gin/gorilla)** | Guarantees language‑agnostic contracts. | Generate server stubs from an `openapi.yaml` stored in `api/`. |
| **Security‑by‑Design** | Go’s static typing makes it easy to keep auth data out of the request flow. | Central middleware for JWT verification + context‑based user propagation. |
| **Observability (OpenTelemetry)** | The Go ecosystem ships first‑class OTEL SDK. | Instrument HTTP, DB, queues, and custom events automatically. |
| **Configuration‑as‑Code** | Viper + Cobra give hierarchical config and flag parsing. | Load defaults → file → env → secret manager (AWS Secrets Manager / Vault). |
| **Testing & CI** | `go test` is fast; Testcontainers via **testcontainers-go** can spin up DB, Redis, Kafka. | CI pipeline runs unit, integration, and contract tests on each PR. |
| **Semantic Versioning & Compatibility** | Go modules already enforce version constraints. | Core declares **minimal required versions** in `go.mod` and uses `replace` for local dev. |

---

## 2 CORE KERNEL (Infrastructure Only)

The core kernel provides foundational infrastructure that all services depend on. It contains **no business logic**.

| Component | Public Interfaces (exported from `pkg/`) | Recommended Packages | Brief Implementation Sketch |
|--------|-------------------------------------------|----------------------|------------------------------|
| **Config** | `type ConfigProvider interface { Get(key string) any; Unmarshal(v any) error }` | `github.com/spf13/viper` | Load defaults (`config/default.yaml`), then env overrides, then optional secret‑store. |
| **Logger** | `type Logger interface { Debug(msg string, fields ...Field); Info(...); Error(...); With(fields ...Field) Logger }` | `go.uber.org/zap` (or `zerolog`) | Global logger is created in each service's `cmd/{service}/main.go`; exported via `pkg/logger`. |
| **DI / Service Registry** | `type Container interface { Provide(constructor any) error; Invoke(fn any) error }` | `go.uber.org/fx` (for lifecycle) | Each service creates its own `fx.New()` container, registers service-specific services. |
| **Health & Metrics** | `type HealthChecker interface { Check(ctx context.Context) error }` | `github.com/prometheus/client_golang/prometheus`, `github.com/heptiolabs/healthcheck` | Each service exposes `/healthz`, `/ready`, `/metrics`. |
| **Error Bus** | `type ErrorPublisher interface { Publish(err error) }` | Simple channel‐based implementation + optional Sentry (`github.com/getsentry/sentry-go`) | Each service registers its own `ErrorBus`. |
| **Service Registry** | `type ServiceRegistry interface { Register(ctx, service) error; Discover(ctx, name) ([]Service, error) }` | `github.com/hashicorp/consul/api` | Consul-based service discovery. Services register on startup, clients discover via registry. |
| **Observability** | `type Tracer interface { StartSpan(ctx, name) (Span, context.Context) }` | `go.opentelemetry.io/otel` | OpenTelemetry integration for distributed tracing across services. |
| **Event Bus** | `type EventBus interface { Publish(ctx context.Context, ev Event) error; Subscribe(topic string, handler EventHandler) }` | `github.com/segmentio/kafka-go` | Kafka-based event bus for asynchronous cross-service communication. |
| **Scheduler / Background Jobs** | `type Scheduler interface { Cron(spec string, job JobFunc) error; Enqueue(q string, payload any) error }` | `github.com/robfig/cron/v3`, `github.com/hibiken/asynq` (Redis‑backed) | Shared infrastructure for background jobs. |
| **Notification** | `type Notifier interface { Send(ctx context.Context, n Notification) error }` | `github.com/go-mail/mail` (SMTP), `github.com/aws/aws-sdk-go-v2/service/ses` | Shared infrastructure for notifications. |
| **Multitenancy (optional)** | `type TenantResolver interface { Resolve(ctx context.Context) (tenantID string, err error) }` | Header/ sub‑domain parser + JWT claim scanner | Tenant ID is stored in request context and automatically added to SQL queries via Ent's `Client` interceptor. |

## 2.1 CORE SERVICES (Independent Microservices)

Core business services are implemented as separate, independently deployable services:

| Service | Entry Point | Responsibilities | Service Client Interface |
|--------|-------------|------------------|-------------------------|
| **Auth Service** | `cmd/auth-service/` | JWT token generation/validation, authentication | `AuthServiceClient` in `pkg/services/auth.go` |
| **Identity Service** | `cmd/identity-service/` | User CRUD, password management, email verification | `IdentityServiceClient` in `pkg/services/identity.go` |
| **Authz Service** | `cmd/authz-service/` | Permission resolution, RBAC/ABAC authorization | `AuthzServiceClient` in `pkg/services/authz.go` |
| **Audit Service** | `cmd/audit-service/` | Audit logging, immutable audit records | `AuditServiceClient` in `pkg/services/audit.go` |
| **API Gateway** | `cmd/api-gateway/` | Request routing, authentication, rate limiting, CORS | N/A (entry point) |

Each service:

- Has its own `go.mod` (or shared workspace)
- Manages its own database connection pool and schema
- Exposes gRPC server (and optional HTTP)
- Registers with Consul service registry
- Uses service clients for inter-service communication

All *public* interfaces live under `pkg/` so that services can import them without pulling in implementation details. The concrete implementations stay in `internal/` (for core kernel) or `services/{service}/internal/` (for service implementations) and are **registered with the container** during service bootstrap.

**Note:** Business logic services (Auth, Identity, Authz, Audit) are NOT in the core kernel. They are separate services implemented in Epic 2.

---

## 3 MODULE (PLUGIN) FRAMEWORK

### 3.1 Interface that every module must implement

```go
// pkg/module/module.go
package module

import (
    "context"
    "go.uber.org/fx"
)

// IModule is the contract a feature plug‑in must fulfil.
type IModule interface {
    // Name returns a unique, human‑readable identifier.
    Name() string
    // Init registers the module's services, routes, jobs, and permissions.
    // The fx.Options returned are merged into the app's lifecycle.
    Init() fx.Option
    // Migrations returns a slice of Ent migration functions (or raw SQL) that
    // the core will run when the platform starts.
    Migrations() []func(*ent.Client) error
}
```

### 3.2 Registration Mechanics

Two ways to get a module into the platform:

| Approach | When to use | Pros | Cons |
|----------|-------------|------|------|
| **Static registration** – each module imports `core` and calls `module.Register(MyBlogModule{})` in its own `init()` | Development, CI (no `.so` needed) | Simpler, works on Windows; compile‑time type safety | Requires recompiling the binary for new modules |
| **Runtime `plugin` loading** – compile each module as a `go build -buildmode=plugin -o blog.so ./modules/blog` | Production SaaS where clients drop new modules, or separate micro‑service extraction | Hot‑swap without rebuild | Only works on Linux/macOS; plugins must be compiled with same Go version & same `go.mod` replace graph; debugging harder |

**Static registration example**

```go
// internal/registry/registry.go
package registry

import (
    "sync"
    "github.com/yourorg/platform/pkg/module"
)

var (
    mu      sync.Mutex
    modules = make(map[string]module.IModule)
)

func Register(m module.IModule) {
    mu.Lock()
    defer mu.Unlock()
    if _, ok := modules[m.Name()]; ok {
        panic("module already registered: " + m.Name())
    }
    modules[m.Name()] = m
}

func All() []module.IModule {
    mu.Lock()
    defer mu.Unlock()
    out := make([]module.IModule, 0, len(modules))
    for _, m := range modules {
        out = append(out, m)
    }
    return out
}
```

**Plugin loader skeleton**

```go
// internal/pluginloader/loader.go
package pluginloader

import (
    "plugin"
    "github.com/yourorg/platform/pkg/module"
)

func Load(path string) (module.IModule, error) {
    p, err := plugin.Open(path)
    if err != nil {
        return nil, err
    }
    sym, err := p.Lookup("Module")
    if err != nil {
        return nil, err
    }
    mod, ok := sym.(module.IModule)
    if !ok {
        return nil, fmt.Errorf("invalid module type")
    }
    return mod, nil
}
```

> **Tip:** Ship a tiny CLI (`platformctl modules list`) that scans `./plugins/*.so`, loads each via `Load`, and prints `Name()`. This is a great sanity check for ops.

### 3.3 Permissions DSL (compile‑time safety)

```go
// pkg/perm/perm.go
package perm

type Permission string

func (p Permission) String() string { return string(p) }

var (
    // Core permissions
    SystemHealthCheck Permission = "system.health.check"

    // Blog module – generated by a small go:generate script
    BlogPostCreate Permission = "blog.post.create"
    BlogPostRead   Permission = "blog.post.read"
    BlogPostUpdate Permission = "blog.post.update"
    BlogPostDelete Permission = "blog.post.delete"
)
```

A **code‑gen** tool (`go generate ./...`) can scan each module’s `module.yaml` for declared actions and emit a single `perm.go` file, guaranteeing no duplicate strings.

---

## 4 SAMPLE FEATURE SERVICE – **Blog Service**

Each feature module is implemented as an independent service:

```
cmd/
└─ blog-service/
   └─ main.go              # Service entry point

services/
└─ blog/
   ├─ go.mod               # Service dependencies
   ├─ module.yaml          # Service manifest
   ├─ api/
   │   └─ blog.proto       # gRPC service definition
   ├─ internal/
   │   ├─ api/
   │   │   └─ handler.go
   │   ├─ domain/
   │   │   ├─ post.go
   │   │   └─ post_repo.go
   │   └─ service/
   │       └─ post_service.go
   └─ pkg/
       └─ module.go
```

### 4.1 `module.yaml`

```yaml
name: blog
version: 0.1.0
dependencies:
  - core >= 1.3.0
permissions:
  - blog.post.create
  - blog.post.read
  - blog.post.update
  - blog.post.delete
routes:
  - method: POST
    path: /api/v1/blog/posts
    permission: blog.post.create
  - method: GET
    path: /api/v1/blog/posts/:id
    permission: blog.post.read
```

### 4.2 Service Entry Point

```go
// cmd/blog-service/main.go
package main

import (
    "context"
    "github.com/yourorg/platform/internal/config"
    "github.com/yourorg/platform/internal/di"
    "github.com/yourorg/platform/services/blog/internal/api"
    "github.com/yourorg/platform/services/blog/internal/service"
    "go.uber.org/fx"
)

func main() {
    cfg := config.Load()
    
    fx.New(
        // Core kernel services
        di.CoreModule(cfg),
        
        // Blog service implementation
        fx.Provide(service.NewPostService),
        fx.Provide(service.NewPostRepo),
        
        // gRPC server
        fx.Provide(api.NewGRPCServer),
        
        // Service registry
        fx.Provide(di.ProvideServiceRegistry),
        
        // Start service
        fx.Invoke(startService),
    ).Run()
}

func startService(lc fx.Lifecycle, server *api.GRPCServer, registry registry.ServiceRegistry) {
    lc.Append(fx.Hook{
        OnStart: func(ctx context.Context) error {
            // Register with Consul
            registry.Register(ctx, &registry.ServiceInstance{
                ID:      "blog-service-1",
                Name:    "blog-service",
                Address: "localhost",
                Port:    8091,
            })
            
            // Start gRPC server
            return server.Start()
        },
        OnStop: func(ctx context.Context) error {
            registry.Deregister(ctx, "blog-service-1")
            return server.Stop()
        },
    })
}
```

### 4.3 Service Implementation

```go
// services/blog/internal/service/post_service.go
package service

import (
    "context"
    "github.com/yourorg/platform/pkg/services"
    "github.com/yourorg/platform/services/blog/internal/domain"
)

type PostService struct {
    repo          *domain.PostRepo
    authzClient   services.AuthzServiceClient
    identityClient services.IdentityServiceClient
    auditClient   services.AuditServiceClient
}

func NewPostService(
    repo *domain.PostRepo,
    authzClient services.AuthzServiceClient,
    identityClient services.IdentityServiceClient,
    auditClient services.AuditServiceClient,
) *PostService {
    return &PostService{
        repo:          repo,
        authzClient:   authzClient,
        identityClient: identityClient,
        auditClient:   auditClient,
    }
}

func (s *PostService) CreatePost(ctx context.Context, req *CreatePostRequest) (*Post, error) {
    // Check permission via Authz Service
    if err := s.authzClient.Authorize(ctx, "blog.post.create"); err != nil {
        return nil, err
    }
    
    // Get user info via Identity Service
    user, err := s.identityClient.GetUser(ctx, req.AuthorID)
    if err != nil {
        return nil, err
    }
    
    // Create post
    post, err := s.repo.Create(ctx, &domain.Post{
        Title:    req.Title,
        Content:  req.Content,
        AuthorID: user.ID,
    })
    if err != nil {
        return nil, err
    }
    
    // Audit log via Audit Service
    s.auditClient.Record(ctx, &services.AuditAction{
        ActorID:  user.ID,
        Action:   "blog.post.create",
        TargetID: post.ID,
    })
    
    return post, nil
}
```

### 4.4 gRPC Handler

```go
// services/blog/internal/api/handler.go
package api

import (
    "context"
    "github.com/yourorg/platform/services/blog/api/pb"
    "github.com/yourorg/platform/services/blog/internal/service"
)

type BlogServer struct {
    pb.UnimplementedBlogServiceServer
    service *service.PostService
}

func (s *BlogServer) CreatePost(ctx context.Context, req *pb.CreatePostRequest) (*pb.CreatePostResponse, error) {
    post, err := s.service.CreatePost(ctx, &service.CreatePostRequest{
        Title:    req.Title,
        Content:  req.Content,
        AuthorID: req.AuthorId,
    })
    if err != nil {
        return nil, err
    }
    
    return &pb.CreatePostResponse{
        Post: &pb.Post{
            Id:      post.ID,
            Title:   post.Title,
            Content: post.Content,
        },
    }, nil
}
```

**Repository using Ent**

```go
// internal/domain/post_repo.go
package domain

import (
    "context"
    "github.com/yourorg/platform/internal/ent"
    "github.com/yourorg/platform/internal/ent/post"
)

type PostRepo struct{ client *ent.Client }

func NewPostRepo(client *ent.Client) *PostRepo { return &PostRepo{client} }

func (r *PostRepo) Create(ctx context.Context, p *Post) (*Post, error) {
    entPost, err := r.client.Post.
        Create().
        SetTitle(p.Title).
        SetContent(p.Content).
        SetAuthorID(p.AuthorID).
        Save(ctx)
    if err != nil {
        return nil, err
    }
    return fromEnt(entPost), nil
}
```

> **Result:** Adding a new feature is just a matter of creating a new folder under `modules/`, implementing `module.IModule`, registering routes, permissions and migrations. The core automatically wires everything together.

---

## 5 INFRASTRUCTURE ADAPTERS (swap‑able, per‑environment)

| Concern | Implementation (Go) | Where it lives |
|---------|---------------------|----------------|
| **Database** | `entgo.io/ent` (code‑gen) | `internal/infra/ent/` |
| **Cache** | `github.com/go-redis/redis/v9` | `internal/infra/cache/` |
| **Message Queue** | `github.com/segmentio/kafka-go` (Kafka) **or** `github.com/hibiken/asynq` (Redis) | `internal/infra/bus/` |
| **Blob Storage** | `github.com/aws/aws-sdk-go-v2/service/s3` (or GCS) | `internal/infra/blob/` |
| **Email** | `github.com/go-mail/mail` (SMTP) | `internal/infra/email/` |
| **SMS / Push** | Twilio SDK, Firebase Cloud Messaging | `internal/infra/notify/` |
| **Secret Store** | AWS Secrets Manager (`aws-sdk-go-v2`) or HashiCorp Vault (`github.com/hashicorp/vault/api`) | `internal/infra/secret/` |

All adapters expose an **interface** in `pkg/infra/…` and are registered in the DI container as **singletons**.

---

## 6 OBSERVABILITY STACK

| Layer | Library | What it does |
|-------|---------|--------------|
| **Tracing** | `go.opentelemetry.io/otel`, `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` | Auto‑instrument HTTP, DB (Ent plugin), Kafka, Redis |
| **Metrics** | `github.com/prometheus/client_golang/prometheus` | Counter/Histogram per request, DB latency, job execution |
| **Logging** | `go.uber.org/zap` (structured JSON) | Global logger, request‑scoped fields (`request_id`, `user_id`, `tenant_id`) |
| **Error Reporting** | `github.com/getsentry/sentry-go` (optional) | Capture panics & errors, link to trace ID |
| **Dashboard** | Grafana + Prometheus + Loki (logs) | Provide ready‑made dashboards in `ops/` folder |

**Instrumentation example (HTTP)**

```go
import (
    "net/http"
    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func main() {
    r := gin.New()
    // Wrap the entire router with OTEL middleware
    wrapped := otelhttp.NewHandler(r, "http-server")
    http.ListenAndServe(":8080", wrapped)
}
```

**Metrics middleware**

```go
func PromMetrics() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        c.Next()
        duration := time.Since(start).Seconds()
        method := c.Request.Method
        path := c.FullPath()
        status := fmt.Sprintf("%d", c.Writer.Status())
        requestDuration.WithLabelValues(method, path, status).Observe(duration)
    }
}
```

---

## 7 CONFIGURATION & ENVIRONMENT

```
config/
├─ default.yaml          # baseline values
├─ development.yaml
├─ production.yaml
└─ secrets/              # git‑ignored, loaded via secret manager
```

**Bootstrap**

```go
func LoadConfig() (*Config, error) {
    v := viper.New()
    v.SetConfigName("default")
    v.AddConfigPath("./config")
    if err := v.ReadInConfig(); err != nil { return nil, err }

    env := v.GetString("environment") // dev / prod / test
    v.SetConfigName(env)
    v.MergeInConfig() // overrides defaults

    v.AutomaticEnv() // env vars win
    // optional: secret manager overlay
    return &Config{v}, nil
}
```

All services receive a `*Config` via DI.

---

## 8 CI / CD PIPELINE (GitHub Actions)

```yaml
name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      GO111MODULE: on
    steps:
      - uses: actions/checkout@v4
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.22'
      - name: Cache Go modules
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/go-build
            ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
      - name: Install Tools
        run: |
          go install github.com/vektra/mockery/v2@latest
          go install github.com/golang/mock/mockgen@latest
      - name: Lint
        run: |
          go install golang.org/x/lint/golint@latest
          golint ./...
      - name: Unit Tests
        run: |
          go test ./... -cover -race -short
      - name: Integration Tests (Docker Compose)
        run: |
          docker compose -f ./docker-compose.test.yml up -d
          go test ./... -tags=integration -count=1
          docker compose -f ./docker-compose.test.yml down
      - name: Build Binaries
        run: |
          go build -ldflags="-X main.version=${{ github.sha }}" -o bin/platform ./cmd/platform
      - name: Publish Docker Image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.ref == 'refs/heads/main' }}
          tags: ghcr.io/yourorg/platform:${{ github.sha }}
```

**Key points**

* `go test ./... -tags=integration` runs tests that spin up Postgres, Redis, Kafka via **Testcontainers** (`github.com/testcontainers/testcontainers-go`).
* Linting via `golint` or `staticcheck`.
* Docker image built from the compiled binary (multi‑stage: `golang:1.22-alpine` → `scratch` or `distroless`).
* Semantic‑release can be added on top (`semantic-release` action) to tag releases automatically.

---

## 9 TESTING STRATEGY

| Test type | Tools | Typical coverage |
|-----------|-------|------------------|
| **Unit** | `testing`, `github.com/stretchr/testify`, `github.com/golang/mock` | Individual services, repositories (use in‑memory DB or mocks). |
| **Integration** | `testcontainers-go` for Postgres, Redis, Kafka; real Ent client | End‑to‑end request → DB → event bus flow. |
| **Contract** | `pact-go` or **OpenAPI** validator middleware (`github.com/getkin/kin-openapi`) | Guarantees that modules do not break the published API. |
| **Load / Stress** | `k6` or `vegeta` scripts in `perf/` | Verify that auth middleware adds < 2 ms latency per request. |
| **Security** | `gosec`, `zap` for secret detection, OWASP ZAP for API scan | Detect hard‑coded secrets, SQL injection risk. |

**Example integration test skeleton**

```go
func TestCreatePost_Integration(t *testing.T) {
    ctx := context.Background()
    // Spin up a PostgreSQL container
    pg, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
        ContainerRequest: testcontainers.ContainerRequest{
            Image:        "postgres:15-alpine",
            Env:          map[string]string{"POSTGRES_PASSWORD": "secret", "POSTGRES_DB": "test"},
            ExposedPorts: []string{"5432/tcp"},
            WaitingFor:   wait.ForListeningPort("5432/tcp"),
        },
        Started: true,
    })
    require.NoError(t, err)
    defer pg.Terminate(ctx)

    // Build DSN from container host/port
    host, _ := pg.Endpoint(ctx)
    dsn := fmt.Sprintf("postgres://postgres:secret@%s/test?sslmode=disable", host)

    // Initialize Ent client against that DSN
    client, err := ent.Open("postgres", dsn)
    require.NoError(t, err)
    defer client.Close()
    // Run schema migration
    err = client.Schema.Create(ctx)
    require.NoError(t, err)

    // Build the whole app using fx, injecting the test client
    app := fx.New(
        core.ProvideAll(),
        fx.Provide(func() *ent.Client { return client }),
        blog.Module.Init(),
        // ...
    )
    // Start the app, issue a HTTP POST request through httptest.Server,
    // assert 201 and DB row existence.
}
```

---

## 10 COMMON PITFALLS & SOLUTIONS (Go‑centric)

| Pitfall | Symptom | Remedy |
|---------|----------|--------|
| **Circular imports** (core ↔ module) | `import cycle not allowed` build error | Keep **interfaces only** in `pkg/`. Core implements, modules depend on the interface only. |
| **Too‑big binary** (all modules compiled in) | Long build times, memory pressure on CI | Use **Go plugins** for truly optional modules; keep core binary minimal. |
| **Version mismatch with plugins** | Panic: `plugin was built with a different version of package X` | Enforce a **single `replace` directive** for core in each module’s `go.mod` (e.g., `replace github.com/yourorg/platform => ../../platform`). |
| **Context leakage** (request data not passed) | Logs missing `user_id`, permission checks use zero‑value user | Always store user/tenant info in `context.Context` via middleware; provide helper `auth.FromContext(ctx)`. |
| **Ent migrations out‑of‑sync** | Startup fails with “column does not exist” | Run `go generate ./...` (ent codegen) and `ent/migrate` automatically at boot **after all module migrations are collected**. |
| **Hard‑coded permission strings** | Typos go unnoticed → 403 bugs | Use the **generated `perm` package** (see §4.1) and reference constants everywhere. |
| **Blocking I/O in request path** | 500 ms latency spikes | Off‑load long‑running work to **asynq jobs** or **Kafka consumers**; keep request handlers thin. |
| **Over‑exposed HTTP handlers** | Missing auth middleware → open endpoint | Wrap the router with a **global security middleware** that checks a whitelist and enforces `Authorizer` for everything else. |
| **Memory leaks with goroutine workers** | Leak after many module reloads | Use **fx.Lifecycle** to start/stop background workers; always cancel contexts on `Stop`. |
| **Testing with real DB slows CI** | Pipeline > 10 min | Use **testcontainers** in parallel jobs, cache Docker images, or use in‑memory SQLite for unit tests and only run DB‑heavy tests in a dedicated job. |

---

## 11 QUICK‑START STEPS (What to code first)

1. **Bootstrap repo**  
   ```bash
   mkdir platform && cd platform
   go mod init github.com/yourorg/platform
   mkdir cmd internal pkg config modules
   touch cmd/main.go
   ```
2. **Create core container** (`internal/di/container.go`) using `dig`. Register Config, Logger, DB, EventBus, Authenticator, Authorizer.
3. **Add Auth middleware** (`pkg/auth/middleware.go`) that extracts JWT, validates claims, injects `User` into context.
4. **Implement Permission DSL** (`pkg/perm/perm.go`) and a **simple in‑memory resolver** (`pkg/perm/resolver.go`).
5. **Write `module` interface** (`pkg/module/module.go`) and a **static registry** (`internal/registry/registry.go`).
6. **Add first plug‑in** – copy the **Blog** module skeleton from §4.  
   `cd modules/blog && go mod init github.com/yourorg/blog && go run ../..` (build & test).
7. **Wire everything in `cmd/main.go`** using `fx.New(...)`:
   ```go
   app := fx.New(
       core.Module,               // registers core services
       fx.Invoke(registry.RegisterAllModules), // loads static modules
       fx.Invoke(func(lc fx.Lifecycle, r *gin.Engine) {
           lc.Append(fx.Hook{
               OnStart: func(context.Context) error {
                   go r.Run(":8080")
                   return nil
               },
               OnStop: func(ctx context.Context) error { return nil },
           })
       }),
   )
   app.Run()
   ```
8. **Run integration test** (`go test ./... -tags=integration`) to sanity‑check DB + routes.
9. **Add CI workflow** (copy the `.github/workflows/ci.yml` from §8) and push.
10. **Publish first Docker image** (`docker build -t ghcr.io/yourorg/platform:dev .`).

After step 10 you have a **complete, production‑grade scaffolding** that:

* authenticates users via JWT (with optional OIDC),
* authorizes actions through a compile‑time‑checked permission DSL,
* logs/metrics/traces out of the box,
* lets any team drop a new `*.so` or static module under `modules/` to add resources,
* ships with a working CI pipeline and a ready‑to‑run Docker image.

---

## 12 REFERENCE IMPLEMENTATION (public)

If you prefer to start from a **real open‑source baseline**, check out the following community projects that already adopt most of the ideas above:

| Repo | Highlights |
|------|------------|
| `github.com/go‑microservices/clean‑arch` | Clean‑architecture skeleton, fx DI, Ent ORM, JWT auth |
| `github.com/ory/hydra` | Full OIDC provider (good for auth reference) |
| `github.com/segmentio/kafka-go` examples | Event‑bus integration |
| `github.com/ThreeDotsLabs/watermill` | Pub/sub abstraction usable as bus wrapper |
| `github.com/hibiken/asynq` | Background job framework (Redis based) |

Fork one, strip the business logic, and rename the packages to match *your* `github.com/yourorg/platform` namespace.

---

## 13 FINAL CHECKLIST (before you ship)

- [ ] Core modules compiled & registered in `internal/di`.  
- [ ] `module.IModule` interface and static registry in place.  
- [ ] JWT auth + middleware + `User` context helper.  
- [ ] Permission constants generated & compiled in `pkg/perm`.  
- [ ] Ent schema + migration runner that aggregates all module migrations.  
- [ ] OpenTelemetry tracer/provider wired at process start.  
- [ ] Prometheus metrics endpoint (`/metrics`).  
- [ ] Health endpoints (`/healthz`, `/ready`).  
- [ ] Dockerfile (multi‑stage) and `docker-compose.yml` for dev.  
- [ ] GitHub Actions pipeline (CI) passes all tests.  
- [ ] Sample plug‑in (Blog) builds, loads, registers routes, and passes integration test.  
- [ ] Documentation: `README.md`, `docs/architecture.md`, `docs/extension-points.md`.  

> **Congratulations!** You now have a **robust, extensible Go platform boilerplate** that can be the foundation for any SaaS, internal toolset, or micro‑service ecosystem you wish to build. Happy coding!