Пример

Структура микросервиса (которую я делаю)

pkg/
    endpoint/                tips: Endpoint Layer
        data.go              tips: Формат запросов и ответов сервера
        endpoint.go          tips: Хендлеры транспортного уровня
    repositories/            tips: Общение с persistance store
        repository.go        tips: Интерфейс CRUD низкоуровневой работы с хранилищем 
        repository_impl.go   tips: Реализация интерфейса
    service/                 tips: Service Layer
        service.go           tips: Интерфейс микросервиса: по сути описание бизнес-задачи
        service_impl.go      tips: Реализация интерфейса
    transport/               tips: Transport Layer
        transport.go         tips: Описание эндпоинтов сервера и настройка самого сервера
        util.go              tips: Encode/Decode запросов и ответов 
    stub.go                  tips: Запуск тестового сервера

service

service.go

// DatabaseService describe the Database service.
type DatabaseService interface {
	GetSources(ctx context.Context) (interface{}, error)
}

service_impl.go

package service

import (
	"context"
	"github.com/go-kit/kit/log"
	"some/database/pkg/repositories"
	"some/model/data"
)

type service struct {
	Repository repositories.Repository
	logger     log.Logger
}

func NewService(logger log.Logger) DatabaseService {
	return &service{
		Repository: repositories.NewRepository(),
		logger: logger,
	}
}

func (s *service) GetSources(ctx context.Context) (interface{}, error) {
	sources := s.Repository.GetSources()

	return sources, nil
}

some/model/data

package data

import "net/url"

type SourceType int

type Source struct {
	Url 		*url.URL 	`json:"url"`
	Type 		SourceType 	`json:"type"`
	Signature 	string 		`json:"signature,omitempty"` // commit or hash
}

// SourceList Список источников
type SourceList []Source

repositories

repository.go

package repositories

import "some/model/data"

type Repository interface {
	GetSources() data.SourceList
}

repository_impl.go

package repositories

import "some/model/data"

type ClientTest map[string]interface{}

func client() ClientTest {

	return map[string]interface{}{
		"sources": data.SourceList{},
	}
}

type repositoryTest struct {
	client ClientTest
}

func NewRepository() Repository {

	return &repositoryTest{
		client: client(),
	}
}

func (repo *repositoryTest) GetSources() data.SourceList {

	return repo.client["sources"].(data.SourceList)
}

endpoint

endpoint.go

package endpoint

import "github.com/go-kit/kit/endpoint"

type Endpoints struct {
	GetSources endpoint.Endpoint
}

func MakeEndpoints(s service.DatabaseService) Endpoints {
	return Endpoints{
		GetSources: makeGetSourcesEndpoint(s),
	}
}

func makeGetSourcesEndpoint(s service.DatabaseService) endpoint.Endpoint {
	return func(ctx context.Context, request interface{}) (response interface{}, err error) {

		// req := request.(GetSourcesRequest)
		sources, err := s.GetSources(ctx)

		return GetSourcesResponse{Sources: sources.(data.SourceList)}, err
	}
}

data.go

package endpoint

type GetSourcesRequest struct {

}

type GetSourcesResponse struct {
	Sources data.SourceList `json:"sources"`
}

transport

transport.go

package transport

import (
	"github.com/go-kit/kit/log"
	kithttp "github.com/go-kit/kit/transport/http"
	"github.com/gorilla/mux"
	"net/http"
)

func NewService(svcEndpoints endpoint.Endpoints, logger log.Logger) http.Handler {
	r := mux.NewRouter()
	options := []kithttp.ServerOption{

	}

	r.Methods("GET").Path("/sources").Handler(kithttp.NewServer(
		svcEndpoints.GetSources,
		decodeGetSourcesRequest,
		encodeResponse,
		options...
	))
	
	return r
}

util.go

package transport

func decodeGetSourcesRequest(_ context.Context, r *http.Request) (request interface{}, err error) {
	// var req endpoint.GetSourcesRequest

	return endpoint.GetSourcesRequest{}, nil

}

func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
	if e, ok := response.(errorer); ok && e.error() != nil {
		encodeErrorResponse(ctx, e.error(), w)
		return nil
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	return json.NewEncoder(w).Encode(response)
}

type errorer interface {
	error() error
}

func encodeErrorResponse(_ context.Context, err error, w http.ResponseWriter) {
	if err == nil {
		panic("encodeError with nil error")
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(codeFrom(err))
	json.NewEncoder(w).Encode(map[string]interface{}{
		"error": err.Error(),
	})
}

func codeFrom(err error) int {
	switch err {
	default:
		return http.StatusInternalServerError
	}
}

stub.go

package pkg

import (
	"flag"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	...
	"net/http"
	"net/url"
	"os"
)

func UpDatabaseStub() {
	// payloads := payloads()

	var (
		httpAddr = flag.String("http.addr", ":9091", "Database SVC HTTP listen address")
	)
	flag.Parse()

	var logger log.Logger
	{
		logger = log.NewLogfmtLogger(os.Stderr)
		logger = log.NewSyncLogger(logger)
		logger = log.With(logger,
			"svc",
			"database",
			"ts", log.DefaultTimestampUTC,
			"caller", log.DefaultCaller,
		)
	}

	level.Info(logger).Log("msg", "database service started")
	defer level.Info(logger).Log("msg", "database service end")

	var svc service.DatabaseService
	{
		svc = service.NewServiceStub(logger)
	}

	var h http.Handler
	{
		endpoints := endpoint.MakeEndpoints(svc)
		h = transport.NewService(endpoints, logger)
	}

	errs := make(chan error)
	go func() {
		level.Info(logger).Log("transport", "HTTP", "addr", *httpAddr)
		server := &http.Server{
			Addr: *httpAddr,
			Handler: h,
		}
		errs <- server.ListenAndServe()
	}()

	level.Error(logger).Log("exit", <-errs)
}

Last updated