diff --git a/pkg/environments/environment.go b/pkg/environments/environment.go
new file mode 100644
index 0000000000000000000000000000000000000000..cbd468dc361951e048a0840f4158a42c962e7af1
--- /dev/null
+++ b/pkg/environments/environment.go
@@ -0,0 +1,114 @@
+package environments
+
+import "time"
+
+const (
+	DefaultEnvironment = "master"
+)
+
+type State int
+
+const (
+	StateUnknown State = iota
+	StateNew
+	StatePreparing
+	StateReady
+	StateError
+
+	StateInfoEmpty = "EMPTY"
+)
+
+func (s State) String() string {
+	switch s {
+	case StateNew:
+		return "new"
+	case StatePreparing:
+		return "preparing"
+	case StateReady:
+		return "ready"
+	case StateError:
+		return "error"
+	default:
+		return "unknown"
+	}
+}
+
+type StateInfo struct {
+	State     State     `json:"state" bson:"state"`
+	StartedAt time.Time `json:"started_at,omitempty" bson:"started_at,omitempty"`
+	Info      string    `json:"info,omitempty" bson:"info,omitempty"`
+}
+
+type Config struct {
+	SourceID string
+
+	// Deprecated
+	Features []string
+}
+
+// Environment - представляет рабочее окружения для пространства
+// Каждое окружение может иметь собственный набор коллекций и данных и
+// использоваться независимо друг от друга
+type Environment struct {
+	ID          string `json:"id" bson:"_id"` // Идентификатор окружения, задается пользователем при создании. Уникален в рамках пространства `SpaceID`
+	SpaceID     string `json:"spaceID" bson:"-"`
+	Description string `json:"description" bson:"desc,omitempty"` // Описание для окружения
+	//State       State  `json:"state" bson:"state"`                // Состояние окружения (Preparing/Ready/Failed)
+	//StateInfo   string   `json:"state_info,omitempty" bson:"state_info,omitempty"`
+
+	// StateInfo отображает состояние коллекции:
+	// - State: идентификатор состояния окружения (unknown/new/preparing/ready/error)
+	// - Info: дополнительная информация о состоянии коллекции (например, если при
+	//   применении схемы к коллекции произошла ошибка)
+	// - StartedAt: время, в которое коллекция перешла в состояние `Preparing`
+	StateInfo *StateInfo `json:"state_info" bson:"state_info,omitempty"`
+
+	Aliases []string `json:"aliases" bson:"aliases,omitempty"` // Синонимы окружения (только чтение)
+	Config  *Config  `json:"config,omitempty" bson:"config,omitempty"`
+}
+
+func (e Environment) Clone() *Environment {
+	clone := &Environment{
+		ID:          e.ID,
+		SpaceID:     e.SpaceID,
+		Description: e.Description,
+		Aliases:     append([]string(nil), e.Aliases...),
+		Config:      nil,
+	}
+
+	if e.StateInfo != nil {
+		clone.StateInfo = &StateInfo{
+			State:     e.StateInfo.State,
+			Info:      e.StateInfo.Info,
+			StartedAt: e.StateInfo.StartedAt,
+		}
+	}
+
+	if e.Config != nil {
+		clone.Config = &Config{
+			SourceID: e.Config.SourceID,
+		}
+	}
+
+	return clone
+}
+
+func (e Environment) Fetch(i interface{}) interface{} {
+	p, _ := i.(string)
+	switch p {
+	case "ID":
+		return e.ID
+	case "SpaceID":
+		return e.SpaceID
+	case "Description":
+		return e.Description
+	case "StateInfo":
+		return e.StateInfo
+	case "Aliases":
+		return e.Aliases
+	case "Config":
+		return e.Config
+	default:
+		panic("unknown parameter")
+	}
+}
diff --git a/pkg/environments/mocks/Environments.go b/pkg/environments/mocks/Environments.go
new file mode 100644
index 0000000000000000000000000000000000000000..8c8d099f32cbcbd7cd1edf377b3d46e6756a1d72
--- /dev/null
+++ b/pkg/environments/mocks/Environments.go
@@ -0,0 +1,176 @@
+// Code generated by mockery v2.14.0. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	environments "git.perx.ru/perxis/perxis-go/pkg/environments"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// Environments is an autogenerated mock type for the Environments type
+type Environments struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, env
+func (_m *Environments) Create(ctx context.Context, env *environments.Environment) (*environments.Environment, error) {
+	ret := _m.Called(ctx, env)
+
+	var r0 *environments.Environment
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment) *environments.Environment); ok {
+		r0 = rf(ctx, env)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*environments.Environment)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.Environment) error); ok {
+		r1 = rf(ctx, env)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, spaceId, envId
+func (_m *Environments) Delete(ctx context.Context, spaceId string, envId string) error {
+	ret := _m.Called(ctx, spaceId, envId)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
+		r0 = rf(ctx, spaceId, envId)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Get provides a mock function with given fields: ctx, spaceId, envId
+func (_m *Environments) Get(ctx context.Context, spaceId string, envId string) (*environments.Environment, error) {
+	ret := _m.Called(ctx, spaceId, envId)
+
+	var r0 *environments.Environment
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) *environments.Environment); ok {
+		r0 = rf(ctx, spaceId, envId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*environments.Environment)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
+		r1 = rf(ctx, spaceId, envId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, spaceId
+func (_m *Environments) List(ctx context.Context, spaceId string) ([]*environments.Environment, error) {
+	ret := _m.Called(ctx, spaceId)
+
+	var r0 []*environments.Environment
+	if rf, ok := ret.Get(0).(func(context.Context, string) []*environments.Environment); ok {
+		r0 = rf(ctx, spaceId)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*environments.Environment)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
+		r1 = rf(ctx, spaceId)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Migrate provides a mock function with given fields: ctx, spaceId, envId, options
+func (_m *Environments) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) error {
+	_va := make([]interface{}, len(options))
+	for _i := range options {
+		_va[_i] = options[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, spaceId, envId)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, ...*environments.MigrateOptions) error); ok {
+		r0 = rf(ctx, spaceId, envId, options...)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// RemoveAlias provides a mock function with given fields: ctx, spaceId, envId, alias
+func (_m *Environments) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) error {
+	ret := _m.Called(ctx, spaceId, envId, alias)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok {
+		r0 = rf(ctx, spaceId, envId, alias)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// SetAlias provides a mock function with given fields: ctx, spaceId, envId, alias
+func (_m *Environments) SetAlias(ctx context.Context, spaceId string, envId string, alias string) error {
+	ret := _m.Called(ctx, spaceId, envId, alias)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok {
+		r0 = rf(ctx, spaceId, envId, alias)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Update provides a mock function with given fields: ctx, env
+func (_m *Environments) Update(ctx context.Context, env *environments.Environment) error {
+	ret := _m.Called(ctx, env)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment) error); ok {
+		r0 = rf(ctx, env)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+type mockConstructorTestingTNewEnvironments interface {
+	mock.TestingT
+	Cleanup(func())
+}
+
+// NewEnvironments creates a new instance of Environments. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+func NewEnvironments(t mockConstructorTestingTNewEnvironments) *Environments {
+	mock := &Environments{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/environments/options.go b/pkg/environments/options.go
new file mode 100644
index 0000000000000000000000000000000000000000..02f1a1fea3229ccfe702401c2dfa06e5094d9671
--- /dev/null
+++ b/pkg/environments/options.go
@@ -0,0 +1,37 @@
+package environments
+
+type MigrateOptions struct {
+
+	// Ожидать завершения миграции в синхронном режиме
+	Wait bool
+}
+
+func MergeMigrateOptions(opts ...*MigrateOptions) *MigrateOptions {
+	o := &MigrateOptions{}
+	for _, opt := range opts {
+		if opt.Wait {
+			o.Wait = true
+		}
+	}
+	return o
+}
+
+type UpdateOptions struct {
+
+	// Состояние будет обновлено только в том случае, если выполняется указанное условие
+	// Cond указывается с использованием синтаксиса `expr`
+	Cond string
+}
+
+func MergeUpdateOptions(opts ...*UpdateOptions) *UpdateOptions {
+	o := &UpdateOptions{}
+	for _, opt := range opts {
+		if opt.Cond != "" {
+			if o.Cond != "" {
+				o.Cond += " && "
+			}
+			o.Cond += opt.Cond
+		}
+	}
+	return o
+}
diff --git a/pkg/environments/service.go b/pkg/environments/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..cd0a37f449819a4b92239f8d8b38a3a0036ec8d8
--- /dev/null
+++ b/pkg/environments/service.go
@@ -0,0 +1,20 @@
+package environments
+
+import (
+	"context"
+)
+
+// Environments
+// @microgen grpc
+// @protobuf git.perx.ru/perxis/perxis-go/proto/environments
+// @grpc-addr content.environments.Environments
+type Environments interface {
+	Create(ctx context.Context, env *Environment) (created *Environment, err error)
+	Get(ctx context.Context, spaceId, envId string) (env *Environment, err error)
+	List(ctx context.Context, spaceId string) (envs []*Environment, err error)
+	Update(ctx context.Context, env *Environment) (err error)
+	Delete(ctx context.Context, spaceId, envId string) (err error)
+	SetAlias(ctx context.Context, spaceId, envId, alias string) (err error)
+	RemoveAlias(ctx context.Context, spaceId, envId, alias string) (err error)
+	Migrate(ctx context.Context, spaceId, envId string, options ...*MigrateOptions) (err error)
+}
diff --git a/pkg/environments/transport/client.microgen.go b/pkg/environments/transport/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..96094fda943e407d0264a8abc738fd5c3afe440e
--- /dev/null
+++ b/pkg/environments/transport/client.microgen.go
@@ -0,0 +1,126 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+	"errors"
+
+	environments "git.perx.ru/perxis/perxis-go/pkg/environments"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+func (set EndpointsSet) Create(arg0 context.Context, arg1 *environments.Environment) (res0 *environments.Environment, res1 error) {
+	request := CreateRequest{Env: arg1}
+	response, res1 := set.CreateEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*CreateResponse).Created, res1
+}
+
+func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res0 *environments.Environment, res1 error) {
+	request := GetRequest{
+		EnvId:   arg2,
+		SpaceId: arg1,
+	}
+	response, res1 := set.GetEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*GetResponse).Env, res1
+}
+
+func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*environments.Environment, res1 error) {
+	request := ListRequest{SpaceId: arg1}
+	response, res1 := set.ListEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*ListResponse).Envs, res1
+}
+
+func (set EndpointsSet) Update(arg0 context.Context, arg1 *environments.Environment) (res0 error) {
+	request := UpdateRequest{Env: arg1}
+	_, res0 = set.UpdateEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
+
+func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (res0 error) {
+	request := DeleteRequest{
+		EnvId:   arg2,
+		SpaceId: arg1,
+	}
+	_, res0 = set.DeleteEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
+
+func (set EndpointsSet) SetAlias(arg0 context.Context, arg1 string, arg2 string, arg3 string) (res0 error) {
+	request := SetAliasRequest{
+		Alias:   arg3,
+		EnvId:   arg2,
+		SpaceId: arg1,
+	}
+	_, res0 = set.SetAliasEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
+
+func (set EndpointsSet) RemoveAlias(arg0 context.Context, arg1 string, arg2 string, arg3 string) (res0 error) {
+	request := RemoveAliasRequest{
+		Alias:   arg3,
+		EnvId:   arg2,
+		SpaceId: arg1,
+	}
+	_, res0 = set.RemoveAliasEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
+
+func (set EndpointsSet) Migrate(arg0 context.Context, arg1 string, arg2 string, arg3 ...*environments.MigrateOptions) (res0 error) {
+	request := MigrateRequest{
+		EnvId:   arg2,
+		Options: arg3,
+		SpaceId: arg1,
+	}
+	_, res0 = set.MigrateEndpoint(arg0, &request)
+	if res0 != nil {
+		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res0 = errors.New(e.Message())
+		}
+		return
+	}
+	return res0
+}
diff --git a/pkg/environments/transport/endpoints.microgen.go b/pkg/environments/transport/endpoints.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..5639637d2b479721e21d5c3fe4ea097d2f2a6cde
--- /dev/null
+++ b/pkg/environments/transport/endpoints.microgen.go
@@ -0,0 +1,17 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import endpoint "github.com/go-kit/kit/endpoint"
+
+// EndpointsSet implements Environments API and used for transport purposes.
+type EndpointsSet struct {
+	CreateEndpoint      endpoint.Endpoint
+	GetEndpoint         endpoint.Endpoint
+	ListEndpoint        endpoint.Endpoint
+	UpdateEndpoint      endpoint.Endpoint
+	DeleteEndpoint      endpoint.Endpoint
+	SetAliasEndpoint    endpoint.Endpoint
+	RemoveAliasEndpoint endpoint.Endpoint
+	MigrateEndpoint     endpoint.Endpoint
+}
diff --git a/pkg/environments/transport/exchanges.microgen.go b/pkg/environments/transport/exchanges.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..a1a0ab068d575767c543d8db652c5d8c9a7e02f4
--- /dev/null
+++ b/pkg/environments/transport/exchanges.microgen.go
@@ -0,0 +1,66 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import environments "git.perx.ru/perxis/perxis-go/pkg/environments"
+
+type (
+	CreateRequest struct {
+		Env *environments.Environment `json:"env"`
+	}
+	CreateResponse struct {
+		Created *environments.Environment `json:"created"`
+	}
+
+	GetRequest struct {
+		SpaceId string `json:"space_id"`
+		EnvId   string `json:"env_id"`
+	}
+	GetResponse struct {
+		Env *environments.Environment `json:"env"`
+	}
+
+	ListRequest struct {
+		SpaceId string `json:"space_id"`
+	}
+	ListResponse struct {
+		Envs []*environments.Environment `json:"envs"`
+	}
+
+	UpdateRequest struct {
+		Env *environments.Environment `json:"env"`
+	}
+	// Formal exchange type, please do not delete.
+	UpdateResponse struct{}
+
+	DeleteRequest struct {
+		SpaceId string `json:"space_id"`
+		EnvId   string `json:"env_id"`
+	}
+	// Formal exchange type, please do not delete.
+	DeleteResponse struct{}
+
+	SetAliasRequest struct {
+		SpaceId string `json:"space_id"`
+		EnvId   string `json:"env_id"`
+		Alias   string `json:"alias"`
+	}
+	// Formal exchange type, please do not delete.
+	SetAliasResponse struct{}
+
+	RemoveAliasRequest struct {
+		SpaceId string `json:"space_id"`
+		EnvId   string `json:"env_id"`
+		Alias   string `json:"alias"`
+	}
+	// Formal exchange type, please do not delete.
+	RemoveAliasResponse struct{}
+
+	MigrateRequest struct {
+		SpaceId string                         `json:"space_id"`
+		EnvId   string                         `json:"env_id"`
+		Options []*environments.MigrateOptions `json:"options"` // This field was defined with ellipsis (...).
+	}
+	// Formal exchange type, please do not delete.
+	MigrateResponse struct{}
+)
diff --git a/pkg/environments/transport/grpc/client.microgen.go b/pkg/environments/transport/grpc/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..34e0fa4db1cf8149475dde562b3179091089f70f
--- /dev/null
+++ b/pkg/environments/transport/grpc/client.microgen.go
@@ -0,0 +1,75 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/environments/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/environments"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	grpc "google.golang.org/grpc"
+)
+
+func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	if addr == "" {
+		addr = "content.environments.Environments"
+	}
+	return transport.EndpointsSet{
+		CreateEndpoint: grpckit.NewClient(
+			conn, addr, "Create",
+			_Encode_Create_Request,
+			_Decode_Create_Response,
+			pb.CreateResponse{},
+			opts...,
+		).Endpoint(),
+		DeleteEndpoint: grpckit.NewClient(
+			conn, addr, "Delete",
+			_Encode_Delete_Request,
+			_Decode_Delete_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+		GetEndpoint: grpckit.NewClient(
+			conn, addr, "Get",
+			_Encode_Get_Request,
+			_Decode_Get_Response,
+			pb.GetResponse{},
+			opts...,
+		).Endpoint(),
+		ListEndpoint: grpckit.NewClient(
+			conn, addr, "List",
+			_Encode_List_Request,
+			_Decode_List_Response,
+			pb.ListResponse{},
+			opts...,
+		).Endpoint(),
+		MigrateEndpoint: grpckit.NewClient(
+			conn, addr, "Migrate",
+			_Encode_Migrate_Request,
+			_Decode_Migrate_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+		RemoveAliasEndpoint: grpckit.NewClient(
+			conn, addr, "RemoveAlias",
+			_Encode_RemoveAlias_Request,
+			_Decode_RemoveAlias_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+		SetAliasEndpoint: grpckit.NewClient(
+			conn, addr, "SetAlias",
+			_Encode_SetAlias_Request,
+			_Decode_SetAlias_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+		UpdateEndpoint: grpckit.NewClient(
+			conn, addr, "Update",
+			_Encode_Update_Request,
+			_Decode_Update_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
+	}
+}
diff --git a/pkg/environments/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/environments/transport/grpc/protobuf_endpoint_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..6411216cd4f54cedd9bf2a3c61ff77b98d70cc35
--- /dev/null
+++ b/pkg/environments/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -0,0 +1,307 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// Please, do not change functions names!
+package transportgrpc
+
+import (
+	"context"
+	"errors"
+
+	transport "git.perx.ru/perxis/perxis-go/pkg/environments/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/environments"
+	empty "github.com/golang/protobuf/ptypes/empty"
+)
+
+func _Encode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil CreateRequest")
+	}
+	req := request.(*transport.CreateRequest)
+	reqEnv, err := PtrEnvironmentToProto(req.Env)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.CreateRequest{Env: reqEnv}, nil
+}
+
+func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil GetRequest")
+	}
+	req := request.(*transport.GetRequest)
+	return &pb.GetRequest{
+		EnvId:   req.EnvId,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_List_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil ListRequest")
+	}
+	req := request.(*transport.ListRequest)
+	return &pb.ListRequest{SpaceId: req.SpaceId}, nil
+}
+
+func _Encode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil UpdateRequest")
+	}
+	req := request.(*transport.UpdateRequest)
+	reqEnv, err := PtrEnvironmentToProto(req.Env)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.UpdateRequest{Env: reqEnv}, nil
+}
+
+func _Encode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil DeleteRequest")
+	}
+	req := request.(*transport.DeleteRequest)
+	return &pb.DeleteRequest{
+		EnvId:   req.EnvId,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_SetAlias_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil SetAliasRequest")
+	}
+	req := request.(*transport.SetAliasRequest)
+	return &pb.SetAliasRequest{
+		Alias:   req.Alias,
+		EnvId:   req.EnvId,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_RemoveAlias_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil RemoveAliasRequest")
+	}
+	req := request.(*transport.RemoveAliasRequest)
+	return &pb.RemoveAliasRequest{
+		Alias:   req.Alias,
+		EnvId:   req.EnvId,
+		SpaceId: req.SpaceId,
+	}, nil
+}
+
+func _Encode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil CreateResponse")
+	}
+	resp := response.(*transport.CreateResponse)
+	respCreated, err := PtrEnvironmentToProto(resp.Created)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.CreateResponse{Created: respCreated}, nil
+}
+
+func _Encode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetResponse")
+	}
+	resp := response.(*transport.GetResponse)
+	respEnv, err := PtrEnvironmentToProto(resp.Env)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.GetResponse{Env: respEnv}, nil
+}
+
+func _Encode_List_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil ListResponse")
+	}
+	resp := response.(*transport.ListResponse)
+	respEnvs, err := ListPtrEnvironmentToProto(resp.Envs)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.ListResponse{Envs: respEnvs}, nil
+}
+
+func _Encode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_SetAlias_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_RemoveAlias_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil CreateRequest")
+	}
+	req := request.(*pb.CreateRequest)
+	reqEnv, err := ProtoToPtrEnvironment(req.Env)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.CreateRequest{Env: reqEnv}, nil
+}
+
+func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil GetRequest")
+	}
+	req := request.(*pb.GetRequest)
+	return &transport.GetRequest{
+		EnvId:   string(req.EnvId),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_List_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil ListRequest")
+	}
+	req := request.(*pb.ListRequest)
+	return &transport.ListRequest{SpaceId: string(req.SpaceId)}, nil
+}
+
+func _Decode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil UpdateRequest")
+	}
+	req := request.(*pb.UpdateRequest)
+	reqEnv, err := ProtoToPtrEnvironment(req.Env)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.UpdateRequest{Env: reqEnv}, nil
+}
+
+func _Decode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil DeleteRequest")
+	}
+	req := request.(*pb.DeleteRequest)
+	return &transport.DeleteRequest{
+		EnvId:   string(req.EnvId),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_SetAlias_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil SetAliasRequest")
+	}
+	req := request.(*pb.SetAliasRequest)
+	return &transport.SetAliasRequest{
+		Alias:   string(req.Alias),
+		EnvId:   string(req.EnvId),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_RemoveAlias_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil RemoveAliasRequest")
+	}
+	req := request.(*pb.RemoveAliasRequest)
+	return &transport.RemoveAliasRequest{
+		Alias:   string(req.Alias),
+		EnvId:   string(req.EnvId),
+		SpaceId: string(req.SpaceId),
+	}, nil
+}
+
+func _Decode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil CreateResponse")
+	}
+	resp := response.(*pb.CreateResponse)
+	respCreated, err := ProtoToPtrEnvironment(resp.Created)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.CreateResponse{Created: respCreated}, nil
+}
+
+func _Decode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetResponse")
+	}
+	resp := response.(*pb.GetResponse)
+	respEnv, err := ProtoToPtrEnvironment(resp.Env)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.GetResponse{Env: respEnv}, nil
+}
+
+func _Decode_List_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil ListResponse")
+	}
+	resp := response.(*pb.ListResponse)
+	respEnvs, err := ProtoToListPtrEnvironment(resp.Envs)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.ListResponse{Envs: respEnvs}, nil
+}
+
+func _Decode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_SetAlias_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_RemoveAlias_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Migrate_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil MigrateRequest")
+	}
+	req := request.(*transport.MigrateRequest)
+	opts, _ := ElPtrMigrateOptionsToProto(req.Options)
+	return &pb.MigrateRequest{
+		EnvId:   req.EnvId,
+		SpaceId: req.SpaceId,
+		Options: opts,
+	}, nil
+}
+
+func _Encode_Migrate_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Migrate_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil MigrateRequest")
+	}
+	req := request.(*pb.MigrateRequest)
+	opts, _ := ProtoToElPtrMigrateOptions(req.Options)
+	return &transport.MigrateRequest{
+		EnvId:   string(req.EnvId),
+		SpaceId: string(req.SpaceId),
+		Options: opts,
+	}, nil
+}
+
+func _Decode_Migrate_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
diff --git a/pkg/environments/transport/grpc/protobuf_type_converters.microgen.go b/pkg/environments/transport/grpc/protobuf_type_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..5402c513bc7ecbf3a7dc3b4a291282eed03f554e
--- /dev/null
+++ b/pkg/environments/transport/grpc/protobuf_type_converters.microgen.go
@@ -0,0 +1,99 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// It is better for you if you do not change functions names!
+// This file will never be overwritten.
+package transportgrpc
+
+import (
+	pb "git.perx.ru/perxis/perxis-go/proto/environments"
+	service "git.perx.ru/perxis/perxis-go/pkg/environments"
+	"github.com/golang/protobuf/ptypes"
+)
+
+func PtrEnvironmentToProto(env *service.Environment) (*pb.Environment, error) {
+	if env == nil {
+		return nil, nil
+	}
+	protoEnvironment := &pb.Environment{
+		Id:          env.ID,
+		SpaceId:     env.SpaceID,
+		Description: env.Description,
+		Aliases:     env.Aliases,
+	}
+	if env.StateInfo != nil {
+		protoEnvironment.StateInfo = &pb.StateInfo{
+			State: pb.StateInfo_State(env.StateInfo.State),
+			Info:  env.StateInfo.Info,
+		}
+		protoEnvironment.StateInfo.StartedAt, _ = ptypes.TimestampProto(env.StateInfo.StartedAt)
+	}
+	if env.Config != nil {
+		protoEnvironment.Config = &pb.Config{
+			SourceId: env.Config.SourceID,
+			Features: env.Config.Features,
+		}
+	}
+	return protoEnvironment, nil
+}
+
+func ProtoToPtrEnvironment(protoEnv *pb.Environment) (*service.Environment, error) {
+	if protoEnv == nil {
+		return nil, nil
+	}
+	env := &service.Environment{
+		ID:          protoEnv.Id,
+		SpaceID:     protoEnv.SpaceId,
+		Description: protoEnv.Description,
+		Aliases:     protoEnv.Aliases,
+	}
+	if protoEnv.StateInfo != nil {
+		env.StateInfo = &service.StateInfo{
+			State: service.State(protoEnv.StateInfo.State),
+			Info:  protoEnv.StateInfo.Info,
+		}
+		env.StateInfo.StartedAt, _ = ptypes.Timestamp(protoEnv.StateInfo.StartedAt)
+	}
+	if protoEnv.Config != nil {
+		env.Config = &service.Config{
+			SourceID: protoEnv.Config.SourceId,
+			Features: protoEnv.Config.Features,
+		}
+	}
+	return env, nil
+}
+
+func ListPtrEnvironmentToProto(envs []*service.Environment) ([]*pb.Environment, error) {
+	protoEnvironments := make([]*pb.Environment, 0, len(envs))
+	for _, environment := range envs {
+		protoEnvironment, err := PtrEnvironmentToProto(environment)
+		if err != nil {
+			return nil, err
+		}
+		protoEnvironments = append(protoEnvironments, protoEnvironment)
+	}
+	return protoEnvironments, nil
+}
+
+func ProtoToListPtrEnvironment(protoEnvs []*pb.Environment) ([]*service.Environment, error) {
+	environments := make([]*service.Environment, 0, len(protoEnvs))
+	for _, protoEnvironment := range protoEnvs {
+		environment, err := ProtoToPtrEnvironment(protoEnvironment)
+		if err != nil {
+			return nil, err
+		}
+		environments = append(environments, environment)
+	}
+	return environments, nil
+}
+
+func ElPtrMigrateOptionsToProto(options []*service.MigrateOptions) (*pb.MigrateOptions, error) {
+	opts := service.MergeMigrateOptions(options...)
+	return &pb.MigrateOptions{Wait: opts.Wait}, nil
+}
+
+func ProtoToElPtrMigrateOptions(protoOptions *pb.MigrateOptions) ([]*service.MigrateOptions, error) {
+	if protoOptions == nil {
+		return nil, nil
+	}
+	return []*service.MigrateOptions{{Wait: protoOptions.Wait}}, nil
+}
diff --git a/pkg/environments/transport/grpc/server.microgen.go b/pkg/environments/transport/grpc/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..1389d32692eaf27f5068073d330696d1ad221bd3
--- /dev/null
+++ b/pkg/environments/transport/grpc/server.microgen.go
@@ -0,0 +1,142 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// DO NOT EDIT.
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/environments/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/environments"
+	grpc "github.com/go-kit/kit/transport/grpc"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	context "golang.org/x/net/context"
+)
+
+type environmentsServer struct {
+	create      grpc.Handler
+	get         grpc.Handler
+	list        grpc.Handler
+	update      grpc.Handler
+	delete      grpc.Handler
+	setAlias    grpc.Handler
+	removeAlias grpc.Handler
+	migrate     grpc.Handler
+
+	pb.UnimplementedEnvironmentsServer
+}
+
+func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.EnvironmentsServer {
+	return &environmentsServer{
+		create: grpc.NewServer(
+			endpoints.CreateEndpoint,
+			_Decode_Create_Request,
+			_Encode_Create_Response,
+			opts...,
+		),
+		delete: grpc.NewServer(
+			endpoints.DeleteEndpoint,
+			_Decode_Delete_Request,
+			_Encode_Delete_Response,
+			opts...,
+		),
+		get: grpc.NewServer(
+			endpoints.GetEndpoint,
+			_Decode_Get_Request,
+			_Encode_Get_Response,
+			opts...,
+		),
+		list: grpc.NewServer(
+			endpoints.ListEndpoint,
+			_Decode_List_Request,
+			_Encode_List_Response,
+			opts...,
+		),
+		migrate: grpc.NewServer(
+			endpoints.MigrateEndpoint,
+			_Decode_Migrate_Request,
+			_Encode_Migrate_Response,
+			opts...,
+		),
+		removeAlias: grpc.NewServer(
+			endpoints.RemoveAliasEndpoint,
+			_Decode_RemoveAlias_Request,
+			_Encode_RemoveAlias_Response,
+			opts...,
+		),
+		setAlias: grpc.NewServer(
+			endpoints.SetAliasEndpoint,
+			_Decode_SetAlias_Request,
+			_Encode_SetAlias_Response,
+			opts...,
+		),
+		update: grpc.NewServer(
+			endpoints.UpdateEndpoint,
+			_Decode_Update_Request,
+			_Encode_Update_Response,
+			opts...,
+		),
+	}
+}
+
+func (S *environmentsServer) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateResponse, error) {
+	_, resp, err := S.create.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.CreateResponse), nil
+}
+
+func (S *environmentsServer) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) {
+	_, resp, err := S.get.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.GetResponse), nil
+}
+
+func (S *environmentsServer) List(ctx context.Context, req *pb.ListRequest) (*pb.ListResponse, error) {
+	_, resp, err := S.list.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.ListResponse), nil
+}
+
+func (S *environmentsServer) Update(ctx context.Context, req *pb.UpdateRequest) (*empty.Empty, error) {
+	_, resp, err := S.update.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
+func (S *environmentsServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*empty.Empty, error) {
+	_, resp, err := S.delete.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
+func (S *environmentsServer) SetAlias(ctx context.Context, req *pb.SetAliasRequest) (*empty.Empty, error) {
+	_, resp, err := S.setAlias.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
+func (S *environmentsServer) RemoveAlias(ctx context.Context, req *pb.RemoveAliasRequest) (*empty.Empty, error) {
+	_, resp, err := S.removeAlias.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
+func (S *environmentsServer) Migrate(ctx context.Context, req *pb.MigrateRequest) (*empty.Empty, error) {
+	_, resp, err := S.migrate.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
diff --git a/pkg/environments/transport/server.microgen.go b/pkg/environments/transport/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..3a0b20a55cdf3aff82beb803efb59c5833e5afd7
--- /dev/null
+++ b/pkg/environments/transport/server.microgen.go
@@ -0,0 +1,88 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+
+"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	endpoint "github.com/go-kit/kit/endpoint"
+)
+
+func Endpoints(svc environments.Environments) EndpointsSet {
+	return EndpointsSet{
+		CreateEndpoint:      CreateEndpoint(svc),
+		DeleteEndpoint:      DeleteEndpoint(svc),
+		GetEndpoint:         GetEndpoint(svc),
+		ListEndpoint:        ListEndpoint(svc),
+		MigrateEndpoint:     MigrateEndpoint(svc),
+		RemoveAliasEndpoint: RemoveAliasEndpoint(svc),
+		SetAliasEndpoint:    SetAliasEndpoint(svc),
+		UpdateEndpoint:      UpdateEndpoint(svc),
+	}
+}
+
+func CreateEndpoint(svc environments.Environments) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*CreateRequest)
+		res0, res1 := svc.Create(arg0, req.Env)
+		return &CreateResponse{Created: res0}, res1
+	}
+}
+
+func GetEndpoint(svc environments.Environments) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*GetRequest)
+		res0, res1 := svc.Get(arg0, req.SpaceId, req.EnvId)
+		return &GetResponse{Env: res0}, res1
+	}
+}
+
+func ListEndpoint(svc environments.Environments) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*ListRequest)
+		res0, res1 := svc.List(arg0, req.SpaceId)
+		return &ListResponse{Envs: res0}, res1
+	}
+}
+
+func UpdateEndpoint(svc environments.Environments) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*UpdateRequest)
+		res0 := svc.Update(arg0, req.Env)
+		return &UpdateResponse{}, res0
+	}
+}
+
+func DeleteEndpoint(svc environments.Environments) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*DeleteRequest)
+		res0 := svc.Delete(arg0, req.SpaceId, req.EnvId)
+		return &DeleteResponse{}, res0
+	}
+}
+
+func SetAliasEndpoint(svc environments.Environments) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*SetAliasRequest)
+		res0 := svc.SetAlias(arg0, req.SpaceId, req.EnvId, req.Alias)
+		return &SetAliasResponse{}, res0
+	}
+}
+
+func RemoveAliasEndpoint(svc environments.Environments) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*RemoveAliasRequest)
+		res0 := svc.RemoveAlias(arg0, req.SpaceId, req.EnvId, req.Alias)
+		return &RemoveAliasResponse{}, res0
+	}
+}
+
+func MigrateEndpoint(svc environments.Environments) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*MigrateRequest)
+		res0 := svc.Migrate(arg0, req.SpaceId, req.EnvId, req.Options...)
+		return &MigrateResponse{}, res0
+	}
+}