victorzhuk/skills

z-go-clean-arch-di

Clean architecture wiring, layer direction, and dependency injection for Go services.

查看源码

使用方法

  1. 复制安装命令,在自己的智能体环境安装前先检查源码包。
  2. 从原始文档核对所需工具、凭据和支持的宿主。
  3. 说明输入材料与期望交付物,并对照来源检查输出。
npx skills add https://github.com/victorzhuk/skills --skill z-go-clean-arch-di

开始前需了解

  • 安装不包含模型订阅、API 额度或访问文件的权限。
  • 仓库星数和安装量反映活跃情况,不等于独立质量或安全测试。
仓库原始内容

按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。

Clean Architecture & DI

Layout

text
cmd/{service}/main.go      — flags, load config, call app.New(cfg).Run(ctx)
internal/
  app/
    app.go        — exported App type, Run(ctx) error
    bootstrap.go  — phased wiring, private build funcs
    config.go     — config load/validation
    logger.go     — logger setup
  domain/         — entities, value objects, repo/service interfaces (stdlib only)
  usecase/        — use-case structs + narrow local contracts
  service/        — domain services
  infra/          — DB, cache, external APIs — implements domain interfaces
  transport/      — HTTP/gRPC handlers — depends on use-case contracts

Transport never imports internal/app. It receives its deps through provider interfaces.

Container structs, a full wiring example, provider methods, interface narrowing, and a composition-root test: references/di-contracts.md.

Phased Wiring

Build in three phases so construction order is explicit — this lives in bootstrap.go:

go
type container struct {
    infra    infraLayer
    services serviceLayer
    usecases usecaseLayer
}

func buildContainer(ctx context.Context, cfg *config.Config) (container, error) {
    infra, err := buildInfra(ctx, cfg)
    if err != nil {
        return container{}, fmt.Errorf("build infra: %w", err)
    }
    services := buildServices(infra)
    usecases := buildUsecases(services)
    return container{infra, services, usecases}, nil
}

Infra → services → usecases. Never reverse.

Constructors

Prefer concrete return types and narrow interfaces for dependencies that need a contract:

go
func NewUserService(repo UserRepository, log *slog.Logger) *UserService

Shared protocols, runtime implementation selection, and deliberate API restriction can justify interface returns. See [[z-go-interfaces]].

Consumer structs hold interface-typed fields where callers need that contract:

go
type OrderUseCase struct {
    users   service.UserService
    auditor service.AuditService
}

Interface Placement

Prefer interfaces at the consumption site:

go
// domain/usecase/user.go — shared across layers
type UserUseCase interface {
    FindByID(ctx context.Context, id string) (*entity.User, error)
    Create(ctx context.Context, req CreateUserReq) (*entity.User, error)
}

// internal/usecase/orders/contract.go — narrow, local
type userLookup interface {
    FindByID(ctx context.Context, id string) (*entity.User, error)
}

Use domain/ for contracts shared across layers. Use package-local interfaces for deliberately narrower contracts. Preserve shared or generated protocol ownership when the contract requires another location. See [[z-go-interfaces]].

Contract Aliases

go
// internal/app/bootstrap.go — reduce repetition in the wiring func
type (
    userService  = service.UserService
    auditService = service.AuditService
)

Never use aliases to hide cross-layer imports that violate direction rules.

Lifecycle

Expose one owned blocking Run(ctx) error when that fits the existing lifecycle; preserve an approved Start/Shutdown seam. bootstrap wires the app and returns an internal shutdown closure that Run owns; nothing outside app.go calls shutdown directly.

go
func (a *App) Run(ctx context.Context) error {
    srv, shutdown := bootstrap(ctx, a.cfg)

    runCtx, stop := context.WithCancel(ctx)
    defer stop()
    g, gCtx := errgroup.WithContext(runCtx)
    g.Go(func() error {
        defer stop()
        if err := srv.Start(); err != nil && !errors.Is(err, http.ErrServerClosed) {
            return fmt.Errorf("http server: %w", err)
        }
        return nil
    })
    g.Go(func() error {
        <-gCtx.Done()
        shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()
        return shutdown(shutdownCtx)
    })
    return g.Wait()
}

Never call os.Exit inside Run — the caller (main) decides logging and exit code. Shutdown always runs on a fresh context (the parent is already cancelled by the time gCtx.Done() fires).

Layer Direction

Domain      → nothing (stdlib only)
Infra       → Domain
UseCase     → Domain, Infra (via interface)
Transport   → Domain, UseCase (via interface)
App         → all layers (composition root only)

Enforce with go-arch-lint, depguard, or a custom CI check — violations accumulate silently otherwise.

DI Library Decision

Prefer manual constructor injection for new wiring. Preserve an established DI framework; constructor count alone does not justify a migration.

Reach for a DI library only once you hit a concrete need manual wiring can't satisfy on its own — dynamic plugin loading, or a lifecycle graph so tangled that phased build funcs stop being readable. Until that need is real, adding one is speculative complexity for a problem manual wiring already solves.

google/wire is archived and no longer maintained: keep it as a legacy maintenance branch, not a new dependency recommendation (repository status). For a justified library choice, uber-go/fx is reflect-based with rich lifecycle hooks but steeper learning curve; samber/do is generic-based with built-in health checks and graceful shutdown.

Quick Reference

PatternRule
Composition rootinternal/app/ — only package that knows all concrete types
Phased wiringinfra → services → usecases, never reverse
ConstructorsPrefer concrete returns and narrow dependency contracts. See exceptions in [[z-go-interfaces]]
Interface placementConsumer side or shared contract by default. Respect protocol ownership. See [[z-go-interfaces]]
Contract aliasesfor readability in bootstrap.go, not to hide bad imports
Lifecycleone blocking Run(ctx) error in app.go; shutdown is an internal closure
DI librarymanual constructor injection by default, regardless of scale
Nil validationcheck at external API boundaries only, not inside internal graph
Layer directionenforced by lint tool in CI
Service locatornever pass the container as a dependency
Global vars / init()never use for service setup

Verify

sh
go build ./...
go-arch-lint check    # or: golangci-lint run --enable depguard
timeout 5m go test -parallel 4 -p 2 -short -timeout 2m ./...
来自同一仓库

更多 Skills

全部 Skills
victorzhuk
社区

z-accessibility

Audit and improve web accessibility following WCAG 2.2 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible".

安装量
1
GitHub Stars
0
最近更新
10月22日
victorzhuk
社区

z-algo-core

DS/algorithm selection judgment for production code, not competitive tricks — hash map for membership/dedup, slice+sort beats a tree until proven, heap for top-k, two-pointer/sliding-window for scans, BFS/DFS/topo-sort for dependency shapes. Triggers on Big-O, hash map vs tree. Does not cover profiling/tuning; see [[z-go-performance]].

安装量
1
GitHub Stars
0
最近更新
10月22日
victorzhuk
社区

z-bdd

BDD acceptance contracts for humans and agents across UI, API, and CLI products. Use for Gherkin, Given-When-Then, E2E acceptance, or scenario evidence. Define repeatable scenarios with explicit assertions and required outcomes. Test selection belongs to [[z-testing-strategy]]. Go harness wiring belongs to [[z-go-bdd]].

安装量
1
GitHub Stars
0
最近更新
10月22日
victorzhuk
社区

z-codegraph

Use an existing CodeGraph integration for call graphs, impact analysis, affected tests, or route discovery. Discover installed tools and index freshness; follow host LSP precedence and verify current source. Do not initialize indexes without user authorization.

安装量
1
GitHub Stars
0
最近更新
10月22日