portsmith

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 20, 2026 License: MIT Imports: 1 Imported by: 0

README

portsmith

portsmith is a Go framework for building backends with Clean Architecture — out of the box.

It gives your team:

  • A shared structure every developer and QA engineer understands instantly
  • CLI tools to generate, scaffold, mock, and validate architecture
  • Runtime libraries for HTTP, database, config, pagination, and testing
  • A CI/CD-ready architecture linter (portsmith check)

Contents


Quick start

# Install the CLI
go install github.com/miilkaa/portsmith/cmd/portsmith@latest

# Scaffold a new package
portsmith new internal/orders

# Implement your domain, service, and repository, then generate ports:
portsmith gen internal/orders

# Generate mocks (requires mockery)
go install github.com/vektra/mockery/v2@latest
portsmith mock internal/orders

# Validate architecture
portsmith check ./internal/...

In main.go:

package main

import (
    "log"
    "os"

    "github.com/miilkaa/portsmith/pkg/config"
    "github.com/miilkaa/portsmith/pkg/database"
    "github.com/miilkaa/portsmith/pkg/server"

    "yourmodule/internal/orders"
)

type Config struct {
    Port        int    `env:"PORT"         env-default:"8080"`
    DatabaseURL string `env:"DATABASE_URL" env-required:"true"`
}

func main() {
    var cfg Config
    if err := config.Load(&cfg); err != nil {
        log.Fatal(err)
    }

    db, err := database.Connect(database.Config{DSN: cfg.DatabaseURL})
    if err != nil {
        log.Fatal(err)
    }
    database.Register(db, &orders.Order{})

    repo := orders.NewRepository(db.DB())
    svc  := orders.NewService(repo)
    h    := orders.NewHandler(svc)

    srv := server.New(server.Config{Port: cfg.Port})
    h.Routes(srv.Router().Group("/api/v1"))
    log.Fatal(srv.Run())
}

Clean Architecture overview

Dependencies point inward only. Outer layers know about inner layers, never the reverse.

Handler → ServicePort ← Service → RepositoryPort ← Repository
File Layer Knows about Does NOT know about
domain.go Core Go types database, HTTP
errors.go Core apperrors HTTP codes
ports.go Interfaces domain SQL, HTTP
service.go Business logic ports, domain SQL, HTTP
repository.go Data gorm or sqlx, domain HTTP
handler.go HTTP ports, dto (gin or chi) SQL
dto.go HTTP Go types, validator SQL
mappers.go Transform domain, dto anything

See docs/en/architecture.md for detailed explanation.


Project layout

your-app/
├── internal/
│   ├── orders/
│   │   ├── domain.go
│   │   ├── errors.go
│   │   ├── ports.go          ← generated by portsmith gen
│   │   ├── service.go
│   │   ├── repository.go
│   │   ├── handler.go
│   │   ├── dto.go
│   │   └── mocks/            ← generated by portsmith mock
│   └── users/
│       └── ...
└── main.go

Technology stacks

portsmith new and portsmith check support two stacks:

Stack HTTP Database Scaffolding
gin-gorm (default) Gin GORM uint IDs, pkg/server, pkg/database
chi-sqlx Chi v5 sqlx + PostgreSQL (pgx driver) uuid.UUID IDs, raw SQL in repository

How the stack is chosen (highest priority first):

  1. --stack gin-gorm or --stack chi-sqlx on the CLI
  2. portsmith.yaml in the project root: stack: chi-sqlx
  3. go.mod: if github.com/go-chi/chi appears → chi-sqlx; if github.com/gin-gonic/gingin-gorm
  4. Default: gin-gorm

Example portsmith.yaml:

stack: chi-sqlx

Libraries

pkg/apperrors

Typed domain errors that map to HTTP status codes automatically.

var ErrNotFound = apperrors.NotFound("order not found")   // → 404
var ErrConflict = apperrors.Conflict("duplicate order")   // → 409

// In service — return domain errors, not HTTP codes:
if errors.Is(err, ErrNotFound) { return nil, ErrNotFound }

// The server middleware handles HTTP mapping automatically.

Full documentation


pkg/database

GORM wrapper with auto-migration and a generic Repository[T].

db, _ := database.Connect(database.Config{DSN: os.Getenv("DATABASE_URL")})
database.Register(db, &orders.Order{}, &users.User{})

// Generic CRUD base — embed in your domain repository:
type Repository struct {
    base database.Repository[Order]
    db   *gorm.DB
}

// Transactions:
database.WithTx(ctx, db, func(tx *database.DB) error {
    return repo.Create(ctx, order)
})

Full documentation


pkg/pagination

Offset-based pagination with HTTP query parsing.

// In handler:
page := pagination.OffsetFromQuery(c.Request)  // ?page=2&limit=20

// In repository:
query.Offset(page.Offset()).Limit(page.Limit())

// In response:
totalPages := pagination.TotalPages(total, page.Limit())

Full documentation


pkg/config

Load configuration from environment variables.

type Config struct {
    Port int    `env:"PORT" env-default:"8080"`
    DSN  string `env:"DATABASE_URL" env-required:"true"`
}
var cfg Config
config.Load(&cfg)

Full documentation


pkg/server

Gin server with batteries included.

srv := server.New(server.Config{Port: 8080})
// Built-in: GET /health, recovery, requestID, CORS, error → HTTP mapping
userHandler.Routes(srv.Router().Group("/api/v1"))
srv.Run()

Full documentation


pkg/chiserver

Chi-based HTTP server with request ID, recovery, CORS, /health, BindAndValidate, and RespondError (uses apperrors for status codes). Use with the chi-sqlx stack.

srv := chiserver.New(chiserver.Config{Port: 8080})
srv.Router().Mount("/api/v1", ordersHandler.Routes())
log.Fatal(srv.Run())

pkg/sqlxdb

PostgreSQL connectivity via sqlx and the pgx database/sql driver (sqlx.Connect("pgx", url)), plus WithTx for transactions. No migrations — use your own tool.

db, err := sqlxdb.Connect(sqlxdb.Config{URL: os.Getenv("DATABASE_URL")})

pkg/testkit

Testing helpers for all architecture layers.

// Handler test (no database):
suite := testkit.NewHTTPSuite(t, router)
suite.POST("/orders", `{"item":"book"}`).ExpectStatus(201).ExpectJSONPath("$.id", float64(1))

// Repository test (SQLite in-memory):
db := testkit.NewTestDB(t, &orders.Order{})

// Table-driven tests:
testkit.Table(t, []testkit.Case{
    {Name: "success", Run: func(t *testing.T) { ... }},
})

Full documentation


CLI tools

portsmith gen

Generate ports.go — minimal interfaces from actual usage.

portsmith gen internal/orders          # single package
portsmith gen --all                    # all packages under internal/
portsmith gen --dry-run internal/orders # preview without writing

Interface names in ports.go use the folder name as a default prefix (webpushWebpushRepository). If your Handler / Service structs already reference types like WebPushRepository / WebPushService, gen picks up that prefix so names stay consistent.

portsmith new

Scaffold a new package with all Clean Architecture files. The stack affects generated imports and types (Gin+GORM vs Chi+sqlx).

portsmith new internal/products
portsmith new --stack chi-sqlx internal/widgets
# creates: domain.go, errors.go, ports.go, service.go, repository.go, handler.go, dto.go
portsmith mock

Generate mocks for all interfaces in ports.go (wraps mockery).

portsmith mock internal/orders
# creates: internal/orders/mocks/mock_orders_repository.go
#          internal/orders/mocks/mock_orders_service.go
portsmith check

Validate architecture rules. Exits with code 1 on violations. Prints the detected stack (same resolution as portsmith new); override with --stack if needed.

portsmith check ./internal/...
portsmith check --stack gin-gorm ./internal/...

Rules checked:

  • Handler does not import gorm.io/gorm, jmoiron/sqlx, or database/sql
  • Service does not import net/http, gin, or chi
  • Handler and Service structs use interfaces, not concrete types
  • ports.go exists when the three-file pattern is present

Testing

portsmith follows Red → Green → Refactor TDD:

  1. Write contract tests first (define the public API)
  2. Implement until tests pass
  3. Refactor — tests guard against regressions

Three testing levels:

Layer What to use Database
Service mockery mocks None
Handler testkit.NewHTTPSuite None
Repository testkit.NewTestDB (SQLite) SQLite in-memory

See docs/en/tdd-guide.md for patterns and examples.


CI/CD integration

# GitHub Actions
- name: Architecture check
  run: go run ./cmd/portsmith check ./internal/...

# GitLab CI
architecture:
  script:
    - go run ./cmd/portsmith check ./internal/...

Example package

A complete reference implementation is available in two locales:

Both examples demonstrate all layers with detailed comments explaining every design decision.

Also available in Russian: README.ru.md


Requirements

  • Go 1.21+
  • PostgreSQL (or SQLite for development/tests)
  • mockery for mock generation

License

MIT

Documentation

Overview

Package portsmith provides embedded assets used by the portsmith CLI.

This root-level package exists solely to expose the examples/ directory via go:embed, since embed paths cannot contain "..". The binary reads ExamplesFS at runtime to copy reference examples into newly initialised projects.

Index

Constants

This section is empty.

Variables

View Source
var ExamplesFS embed.FS

ExamplesFS contains the embedded example packages. Used by "portsmith init" to seed internal/ with reference code.

Functions

This section is empty.

Types

This section is empty.

Directories

Path Synopsis
cmd
portsmith command
Command portsmith is the CLI entry point for the portsmith framework tools.
Command portsmith is the CLI entry point for the portsmith framework tools.
portsmith/check
Package check implements the portsmith check command — an architecture linter that validates Clean Architecture rules across Go packages.
Package check implements the portsmith check command — an architecture linter that validates Clean Architecture rules across Go packages.
portsmith/gen
Package gen implements the portsmith gen command.
Package gen implements the portsmith gen command.
portsmith/init
Package initcmd implements the "portsmith init" command.
Package initcmd implements the "portsmith init" command.
portsmith/mock
Package mockcmd implements the portsmith mock command.
Package mockcmd implements the portsmith mock command.
portsmith/new
Package newcmd implements the portsmith new command.
Package newcmd implements the portsmith new command.
examples
clean_package_example_en
Package example demonstrates a complete package structure in Clean Architecture.
Package example demonstrates a complete package structure in Clean Architecture.
clean_package_example_ru
Package example демонстрирует полную структуру пакета в Clean Architecture.
Package example демонстрирует полную структуру пакета в Clean Architecture.
internal
gen
Package gen implements the core logic for the portsmith gen command: AST-based method signature extraction and regex-based call collection.
Package gen implements the core logic for the portsmith gen command: AST-based method signature extraction and regex-based call collection.
stack
Package stack defines supported technology stacks and detects the active stack for a Go project (portsmith.yaml, go.mod, or explicit flag).
Package stack defines supported technology stacks and detects the active stack for a Go project (portsmith.yaml, go.mod, or explicit flag).
pkg
apperrors
Package apperrors provides typed domain errors with HTTP status mapping.
Package apperrors provides typed domain errors with HTTP status mapping.
chiserver
Package chiserver provides a production-ready Chi HTTP server with:
Package chiserver provides a production-ready Chi HTTP server with:
config
Package config provides a thin wrapper around cleanenv for loading application configuration from environment variables.
Package config provides a thin wrapper around cleanenv for loading application configuration from environment variables.
database
Package database provides GORM utilities for Clean Architecture backends:
Package database provides GORM utilities for Clean Architecture backends:
pagination
Package pagination provides offset-based pagination for HTTP handlers and repositories.
Package pagination provides offset-based pagination for HTTP handlers and repositories.
server
Package server provides a production-ready Gin HTTP server for portsmith applications.
Package server provides a production-ready Gin HTTP server for portsmith applications.
sqlxdb
Package sqlxdb provides sqlx + PostgreSQL (pgx stdlib driver) helpers:
Package sqlxdb provides sqlx + PostgreSQL (pgx stdlib driver) helpers:
testkit
Package testkit provides testing helpers for portsmith Clean Architecture applications.
Package testkit provides testing helpers for portsmith Clean Architecture applications.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL