Recommended Project Structure and Layout for a Kratos Microservice

Use the kratos new CLI command to scaffold a layered directory structure that isolates business logic in internal/biz, data access in internal/data, and transport concerns in internal/service, based on the official kratos-layout template.

The go-kratos/kratos framework enforces an opinionated Kratos microservice project structure that promotes clean architecture and testability. When you run the project generator, it clones the kratos-layout repository and creates a standard directory hierarchy that separates concerns across distinct layers. This layout ensures that domain logic remains independent of infrastructure and transport protocols.

Standard Directory Layout

Running kratos new <service> produces the following top-level structure:


<service>/
├── cmd/
│   └── <service>/          # Application entry point (main.go)

├── internal/
│   ├── biz/                # Business use-cases and domain logic

│   ├── data/               # Data access layer (repositories, DB clients)

│   ├── service/            # gRPC and HTTP transport handlers

│   └── server/             # Wire dependency injection and server wiring

├── configs/                # YAML/JSON configuration files

├── deployments/            # Docker Compose, Kubernetes manifests

├── pkg/                    # Optional reusable library code

├── go.mod                  # Go module definition

└── README.md

The cmd/<service>/ directory contains the executable entry point, while internal/ houses the core application layers that implement the hexagonal architecture pattern.

Layer Responsibilities and Design Philosophy

Each directory in the Kratos project structure serves a specific architectural purpose:

Layer Directory Purpose Typical Contents
Entry Point cmd/<service>/ Application bootstrap and server lifecycle management main.go that initializes configuration and calls server.NewApp
Business Logic internal/biz/ Domain use-cases and business rules independent of transport or storage Use-case structs, domain interfaces, and entity validation logic
Data Access internal/data/ Persistence adapters and external service clients Repository implementations, GORM models, Redis wrappers, third-party API clients
Transport internal/service/ Protocol handlers that convert requests/responses to business calls Generated protobuf service implementations, HTTP handlers, request DTOs
Wiring internal/server/ Dependency injection configuration using Google Wire wire.go provider sets that compose the application graph

This separation ensures that changes to database schema (in data) or communication protocols (in service) do not leak into the core business logic (in biz).

CLI Implementation Details

The project scaffolding logic resides in the Kratos CLI source code at cmd/kratos/internal/project/.

The template URL is defined in project.go at line 20, which maps the service template to the kratos-layout GitHub repository. When executing kratos new, the tool clones this template and performs automated renaming operations. Specifically, in new.go lines 45-48, the generator renames the default cmd/server directory to match your provided service name, ensuring import paths align correctly.

The validity of this structure is verified in project_test.go at line 40, which asserts that generated projects correctly import internal/biz, confirming the business layer is properly wired into the module.

Anatomy of the Generated Code

Entry Point (cmd/<service>/main.go)

The generated main.go bootstraps the Kratos application by loading configuration and invoking the Wire-generated NewApp function:

package main

import (
	"flag"

	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/config"
	"github.com/go-kratos/kratos/v2/log"
	"helloworld/internal/server"
)

func main() {
	var (
		cfgPath = flag.String("conf", "./configs", "config path")
	)
	flag.Parse()

	// Load configuration from YAML/JSON files
	c, err := config.New(
		config.WithSource(
			file.NewSource(*cfgPath),
		),
	)
	if err != nil {
		panic(err)
	}
	if err = c.Load(); err != nil {
		panic(err)
	}

	// Build the application via Wire injection
	app, cleanup, err := server.NewApp(c)
	if err != nil {
		panic(err)
	}
	defer cleanup()

	// Run the server
	if err = app.Run(); err != nil {
		log.Fatalf("server exited with error: %v", err)
	}
}

This file is created automatically during the copy step in new.go at line 42.

Business Logic (internal/biz/)

The biz layer defines use-case interfaces and implementations that operate on domain entities. It declares repository interfaces that the data layer will implement:

package biz

type Greeter interface {
	SayHello(name string) (*HelloReply, error)
}

type GreeterRepo interface {
	SaveGreeting(greeting string) error
}

type greeterUseCase struct {
	repo GreeterRepo
}

func NewGreeterUseCase(repo GreeterRepo) Greeter {
	return &greeterUseCase{repo: repo}
}

func (g *greeterUseCase) SayHello(name string) (*HelloReply, error) {
	// Pure domain logic without transport or storage concerns
	return &HelloReply{Message: "Hello " + name}, nil
}

Data Layer (internal/data/)

The data package implements repository interfaces declared in biz. It contains concrete database models, ORM configurations, and cache clients:

package data

type greeterRepo struct {
	db *gorm.DB
}

func NewGreeterRepo(db *gorm.DB) biz.GreeterRepo {
	return &greeterRepo{db: db}
}

func (r *greeterRepo) SaveGreeting(greeting string) error {
	// Persistence logic using GORM or SQL drivers
	return nil
}

Transport Layer (internal/service/)

The service package implements generated protobuf service interfaces, translating gRPC or HTTP requests into business method calls:

package service

import (
	"context"
	pb "helloworld/api/helloworld/v1"
	"helloworld/internal/biz"
)

type GreeterService struct {
	biz.Greeter
}

func (s *GreeterService) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
	reply, err := s.Greeter.SayHello(req.Name)
	if err != nil {
		return nil, err
	}
	return &pb.HelloReply{Message: reply.Message}, nil
}

Dependency Wiring (internal/server/wire.go)

The server package uses Google Wire to compose the application graph. The wire.go file declares provider sets that connect the layers:

//go:build wireinject
// +build wireinject

package server

import (
	"github.com/go-kratos/kratos/v2"
	"github.com/google/wire"
	"helloworld/internal/biz"
	"helloworld/internal/data"
	"helloworld/internal/service"
)

func NewApp(conf config.Config) (*kratos.App, func(), error) {
	panic(wire.Build(
		// Provider sets for each layer
		biz.NewGreeterUseCase,
		data.NewGreeterRepo,
		service.NewGreeterService,
		
		// Server configuration
		NewServer,
	))
}

Running wire in this directory generates the concrete initialization code that satisfies all interface dependencies.

Summary

  • The Kratos microservice project structure is generated by the kratos new command, which clones the official kratos-layout template.
  • The layout enforces strict separation between entry point (cmd/), business logic (internal/biz/), data access (internal/data/), and transport (internal/service/).
  • Dependency injection is centralized in internal/server/wire.go, using Google Wire to compose layers without manual singleton management.
  • Configuration files belong in configs/, while deployment artifacts reside in deployments/.
  • The CLI automates directory renaming and import path validation, ensuring the generated module is immediately runnable.

Frequently Asked Questions

Run kratos new <service-name> from your terminal. This command clones the kratos-layout template repository and renames the default cmd/server directory to cmd/<service-name> according to the logic in cmd/kratos/internal/project/new.go lines 45-48, producing a ready-to-use module.

Why does Kratos separate business logic into internal/biz rather than placing it in the service handlers?

Isolating domain use-cases in internal/biz ensures that business rules remain independent of transport protocols (gRPC/HTTP) and storage technologies. This allows you to test core logic without spinning up servers or databases, and to swap infrastructure implementations without modifying domain code.

Where should database models and third-party API clients reside?

Place concrete implementations in internal/data/. This package implements repository interfaces defined in internal/biz/, housing GORM models, SQL queries, Redis clients, and external API wrappers. Keep domain entities in internal/biz/ while persistence details remain encapsulated in internal/data/.

Can I customize the generated project structure or remove the Wire dependency?

While you can manually modify the directory layout after generation, deviating from the standard structure may break assumptions in the Kratos ecosystem and complicate future updates. Wire is optional but recommended; removing it requires manual dependency injection in internal/server/, as the NewApp function expects pre-wired dependencies to build the kratos.App instance.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →