diff --git a/pkg/clients/middleware/caching_middleware_test.go b/pkg/clients/middleware/caching_middleware_test.go
index 047000373bc34a3db0f6bfc1fa3403b2929c1975..b842e1183a5425a4b3aec8d59b0720e5627767ed 100644
--- a/pkg/clients/middleware/caching_middleware_test.go
+++ b/pkg/clients/middleware/caching_middleware_test.go
@@ -24,7 +24,7 @@ func TestClientsCache(t *testing.T) {
 		ttl      = 20 * time.Millisecond
 	)
 
-	ErrNotFound := errors.NotFound(errors.New("not found"))
+	errNotFound := errors.NotFound(errors.New("not found"))
 
 	ctx := context.Background()
 
@@ -200,9 +200,9 @@ func TestClientsCache(t *testing.T) {
 			err = svc.Delete(ctx, spaceID, cltID)
 			require.NoError(t, err)
 
-			cs.On("Get", mock.Anything, spaceID, cltID).Return(nil, ErrNotFound).Once()
-			cs.On("GetBy", mock.Anything, spaceID, &clients.GetByParams{OAuthClientID: clientID}).Return(nil, ErrNotFound).Once()
-			cs.On("List", mock.Anything, spaceID).Return(nil, ErrNotFound).Once()
+			cs.On("Get", mock.Anything, spaceID, cltID).Return(nil, errNotFound).Once()
+			cs.On("GetBy", mock.Anything, spaceID, &clients.GetByParams{OAuthClientID: clientID}).Return(nil, errNotFound).Once()
+			cs.On("List", mock.Anything, spaceID).Return(nil, errNotFound).Once()
 
 			_, err = svc.Get(ctx, spaceID, cltID)
 			require.Error(t, err)
@@ -238,7 +238,7 @@ func TestClientsCache(t *testing.T) {
 			err = svc.Delete(ctx, spaceID, cltID)
 			require.NoError(t, err)
 
-			cs.On("List", mock.Anything, spaceID).Return(nil, ErrNotFound).Once()
+			cs.On("List", mock.Anything, spaceID).Return(nil, errNotFound).Once()
 
 			_, err = svc.List(ctx, spaceID)
 			require.Error(t, err)
diff --git a/pkg/collaborators/middleware/caching_middleware_test.go b/pkg/collaborators/middleware/caching_middleware_test.go
index 090e03890daa1b3e71b04308d6136b3d46336614..da1d6e842f542e1c664f988f38c4797ea31c38f1 100644
--- a/pkg/collaborators/middleware/caching_middleware_test.go
+++ b/pkg/collaborators/middleware/caching_middleware_test.go
@@ -24,7 +24,7 @@ func TestCollaboratorsCache(t *testing.T) {
 		ttl       = 20 * time.Millisecond
 	)
 
-	ErrNotFound := errors.NotFound(errors.New("not found"))
+	errNotFound := errors.NotFound(errors.New("not found"))
 
 	ctx := context.Background()
 
@@ -108,9 +108,9 @@ func TestCollaboratorsCache(t *testing.T) {
 
 			cs.On("Remove", mock.Anything, spaceID, userID).Return(nil).Once()
 
-			cs.On("Get", mock.Anything, spaceID, userID).Return("", ErrNotFound).Once()
-			cs.On("ListCollaborators", mock.Anything, spaceID).Return(nil, ErrNotFound).Once()
-			cs.On("ListSpaces", mock.Anything, userID).Return(nil, ErrNotFound).Once()
+			cs.On("Get", mock.Anything, spaceID, userID).Return("", errNotFound).Once()
+			cs.On("ListCollaborators", mock.Anything, spaceID).Return(nil, errNotFound).Once()
+			cs.On("ListSpaces", mock.Anything, userID).Return(nil, errNotFound).Once()
 
 			err = svc.Remove(ctx, spaceID, userID)
 
@@ -162,9 +162,9 @@ func TestCollaboratorsCache(t *testing.T) {
 
 			cs.On("Remove", mock.Anything, spaceID, userID).Return(nil).Once()
 
-			cs.On("Get", mock.Anything, spaceID, userID).Return("", ErrNotFound).Once()
-			cs.On("ListCollaborators", mock.Anything, spaceID).Return(nil, ErrNotFound).Once()
-			cs.On("ListSpaces", mock.Anything, userID).Return(nil, ErrNotFound).Once()
+			cs.On("Get", mock.Anything, spaceID, userID).Return("", errNotFound).Once()
+			cs.On("ListCollaborators", mock.Anything, spaceID).Return(nil, errNotFound).Once()
+			cs.On("ListSpaces", mock.Anything, userID).Return(nil, errNotFound).Once()
 
 			err = svc.Remove(ctx, spaceID, userID)
 
diff --git a/pkg/collections/middleware/caching_middleware_test.go b/pkg/collections/middleware/caching_middleware_test.go
index 3a5f88e647b83f99662a4ee8e1701230954afe30..967a75bce7fa7d933918edc9f05600f75a6c0ff5 100644
--- a/pkg/collections/middleware/caching_middleware_test.go
+++ b/pkg/collections/middleware/caching_middleware_test.go
@@ -28,7 +28,7 @@ func TestCollections_Cache(t *testing.T) {
 		ttl      = 20 * time.Millisecond
 	)
 
-	ErrNotFound := errors.NotFound(errors.New("not found"))
+	errNotFound := errors.NotFound(errors.New("not found"))
 
 	ctx := context.Background()
 
@@ -354,7 +354,7 @@ func TestCollections_Cache(t *testing.T) {
 			require.NoError(t, err)
 
 			//env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
-			col.On("Get", mock.Anything, spaceID, envID, colID).Return(nil, ErrNotFound).Once()
+			col.On("Get", mock.Anything, spaceID, envID, colID).Return(nil, errNotFound).Once()
 			col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{}, nil).Once()
 
 			_, err = svc.Get(ctx, spaceID, envID, colID)
@@ -365,7 +365,7 @@ func TestCollections_Cache(t *testing.T) {
 			require.NoError(t, err)
 			assert.Len(t, vl4, 0, "Ожидает что элементы были удалены из кэша.")
 
-			col.On("Get", mock.Anything, spaceID, envAlias, colID).Return(nil, ErrNotFound).Once()
+			col.On("Get", mock.Anything, spaceID, envAlias, colID).Return(nil, errNotFound).Once()
 
 			_, err = svc.Get(ctx, spaceID, envAlias, colID)
 			require.Error(t, err)
diff --git a/pkg/environments/middleware/caching_middleware.go b/pkg/environments/middleware/caching_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..18b594bc3a193d962d63ef3d5eae6217408d61bd
--- /dev/null
+++ b/pkg/environments/middleware/caching_middleware.go
@@ -0,0 +1,167 @@
+package service
+
+import (
+	"context"
+	"strings"
+
+	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	service "git.perx.ru/perxis/perxis-go/pkg/environments"
+)
+
+func makeKey(ss ...string) string {
+	return strings.Join(ss, "-")
+}
+
+func CachingMiddleware(cache *cache.Cache) Middleware {
+	return func(next service.Environments) service.Environments {
+		return &cachingMiddleware{
+			cache: cache,
+			next:  next,
+		}
+	}
+}
+
+type cachingMiddleware struct {
+	cache *cache.Cache
+	next  service.Environments
+}
+
+func (m cachingMiddleware) Create(ctx context.Context, env *service.Environment) (environment *service.Environment, err error) {
+
+	environment, err = m.next.Create(ctx, env)
+	if err == nil {
+		m.cache.Remove(environment.SpaceID)
+	}
+	return environment, err
+}
+
+func (m cachingMiddleware) Get(ctx context.Context, spaceId string, envId string) (environment *service.Environment, err error) {
+
+	value, e := m.cache.Get(makeKey(spaceId, envId))
+	if e == nil {
+		return value.(*service.Environment), err
+	}
+	environment, err = m.next.Get(ctx, spaceId, envId)
+	if err == nil {
+		m.cache.Set(makeKey(spaceId, environment.ID), environment)
+		for _, a := range environment.Aliases {
+			m.cache.Set(makeKey(spaceId, a), environment)
+		}
+	}
+	return environment, err
+}
+
+func (m cachingMiddleware) List(ctx context.Context, spaceId string) (environments []*service.Environment, err error) {
+
+	value, e := m.cache.Get(spaceId)
+	if e == nil {
+		return value.([]*service.Environment), err
+	}
+	environments, err = m.next.List(ctx, spaceId)
+	if err == nil {
+		m.cache.Set(spaceId, environments)
+	}
+	return environments, err
+}
+
+func (m cachingMiddleware) Update(ctx context.Context, env *service.Environment) (err error) {
+
+	err = m.next.Update(ctx, env)
+	if err == nil {
+		value, e := m.cache.Get(makeKey(env.SpaceID, env.ID))
+		if e == nil {
+			env := value.(*service.Environment)
+			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			for _, a := range env.Aliases {
+				m.cache.Remove(makeKey(env.SpaceID, a))
+			}
+		}
+		m.cache.Remove(env.SpaceID)
+	}
+	return err
+}
+
+func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) {
+
+	err = m.next.Delete(ctx, spaceId, envId)
+	if err == nil {
+		value, e := m.cache.Get(makeKey(spaceId, envId))
+		if e == nil {
+			env := value.(*service.Environment)
+			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			for _, a := range env.Aliases {
+				m.cache.Remove(makeKey(env.SpaceID, a))
+			}
+		}
+		m.cache.Remove(spaceId)
+	}
+	return err
+}
+
+func (m cachingMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+
+	err = m.next.SetAlias(ctx, spaceId, envId, alias)
+	if err == nil {
+		value, e := m.cache.Get(makeKey(spaceId, alias))
+		if e == nil {
+			env := value.(*service.Environment)
+			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			for _, a := range env.Aliases {
+				m.cache.Remove(makeKey(env.SpaceID, a))
+			}
+		}
+
+		value, e = m.cache.Get(makeKey(spaceId, envId))
+		if e == nil {
+			env := value.(*service.Environment)
+			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			for _, a := range env.Aliases {
+				m.cache.Remove(makeKey(env.SpaceID, a))
+			}
+		}
+		m.cache.Remove(spaceId)
+	}
+	return err
+}
+
+func (m cachingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+
+	err = m.next.RemoveAlias(ctx, spaceId, envId, alias)
+	if err == nil {
+		m.cache.Remove(spaceId)
+		value, e := m.cache.Get(makeKey(spaceId, alias))
+		if e == nil {
+			env := value.(*service.Environment)
+			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			for _, a := range env.Aliases {
+				m.cache.Remove(makeKey(env.SpaceID, a))
+			}
+		}
+
+		value, e = m.cache.Get(makeKey(spaceId, envId))
+		if e == nil {
+			env := value.(*service.Environment)
+			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			for _, a := range env.Aliases {
+				m.cache.Remove(makeKey(env.SpaceID, a))
+			}
+		}
+	}
+	return err
+}
+
+func (m cachingMiddleware) Migrate(ctx context.Context, spaceId, envId string, options ...*service.MigrateOptions) (err error) {
+	err = m.next.Migrate(ctx, spaceId, envId, options...)
+
+	// значение из кэша удалить вне зависимости от наличия ошибки, поскольку состояние окружения могло измениться
+	value, e := m.cache.Get(makeKey(spaceId, envId))
+	if e == nil {
+		env := value.(*service.Environment)
+		m.cache.Remove(makeKey(env.SpaceID, env.ID))
+		for _, a := range env.Aliases {
+			m.cache.Remove(makeKey(env.SpaceID, a))
+		}
+	}
+	m.cache.Remove(spaceId)
+	return err
+}
diff --git a/pkg/environments/middleware/caching_middleware_test.go b/pkg/environments/middleware/caching_middleware_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f5bfa7a9b5d207ed0c00d60cd126950a6da5aba0
--- /dev/null
+++ b/pkg/environments/middleware/caching_middleware_test.go
@@ -0,0 +1,387 @@
+package service
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	mocksenvironments "git.perx.ru/perxis/perxis-go/pkg/environments/mocks"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
+)
+
+func TestEnvironmentsCache(t *testing.T) {
+
+	const (
+		envID    = "envID"
+		spaceID  = "spaceID"
+		envAlias = "envAlias"
+		size     = 5
+		ttl      = 20 * time.Millisecond
+	)
+
+	errNotFound := errors.NotFound(errors.New("not found"))
+
+	ctx := context.Background()
+
+	t.Run("Get from cache", func(t *testing.T) {
+		envs := &mocksenvironments.Environments{}
+
+		svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+		envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}, nil).Once()
+
+		v1, err := svc.Get(ctx, spaceID, envID)
+		require.NoError(t, err)
+
+		v2, err := svc.Get(ctx, spaceID, envID)
+		require.NoError(t, err)
+		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша при повторном запросе по ID.")
+
+		v3, err := svc.Get(ctx, spaceID, envAlias)
+		require.NoError(t, err)
+		assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
+
+		envs.AssertExpectations(t)
+	})
+
+	t.Run("Get from cache(by Alias)", func(t *testing.T) {
+		envs := &mocksenvironments.Environments{}
+
+		svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+		envs.On("Get", mock.Anything, spaceID, envAlias).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}, nil).Once()
+
+		v1, err := svc.Get(ctx, spaceID, envAlias)
+		require.NoError(t, err)
+
+		v2, err := svc.Get(ctx, spaceID, envAlias)
+		require.NoError(t, err)
+		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша по alias.")
+
+		v3, err := svc.Get(ctx, spaceID, envID)
+		require.NoError(t, err)
+		assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
+
+		envs.AssertExpectations(t)
+	})
+
+	t.Run("List from cache", func(t *testing.T) {
+		envs := &mocksenvironments.Environments{}
+
+		svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+		envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment"}}, nil).Once()
+
+		vl1, err := svc.List(ctx, spaceID)
+		require.NoError(t, err)
+
+		vl2, err := svc.List(ctx, spaceID)
+		require.NoError(t, err)
+		assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+
+		envs.AssertExpectations(t)
+	})
+
+	t.Run("Invalidate cache", func(t *testing.T) {
+		t.Run("After SetAlias", func(t *testing.T) {
+			envs := &mocksenvironments.Environments{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2"}}, nil).Once()
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2"}}}, nil).Once()
+			envs.On("SetAlias", mock.Anything, spaceID, envID, envAlias).Return(nil).Once()
+
+			v1, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+
+			vl1, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+
+			vl2, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+
+			err = svc.SetAlias(ctx, spaceID, envID, envAlias)
+			require.NoError(t, err)
+
+			envs.On("Get", mock.Anything, spaceID, envAlias).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2", envAlias}}, nil).Once()
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2", envAlias}}}, nil).Once()
+
+			v4, err := svc.Get(ctx, spaceID, envAlias)
+			require.NoError(t, err)
+			assert.Contains(t, v4.Aliases, envAlias, "Ожидает что элемент будет запрошен из сервиса по Alias.")
+
+			v5, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.Same(t, v4, v5, "Ожидается получение объекта из кэша по ID.")
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+
+			envs.AssertExpectations(t)
+		})
+
+		t.Run("After RemoveAlias", func(t *testing.T) {
+			envs := &mocksenvironments.Environments{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2", envAlias}}, nil).Once()
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2", envAlias}}}, nil).Once()
+			envs.On("RemoveAlias", mock.Anything, spaceID, envID, envAlias).Return(nil).Once()
+
+			v1, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша по ID.")
+
+			v3, err := svc.Get(ctx, spaceID, envAlias)
+			require.NoError(t, err)
+			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+
+			vl1, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+
+			vl2, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+
+			err = svc.RemoveAlias(ctx, spaceID, envID, envAlias)
+			require.NoError(t, err)
+
+			envs.On("Get", mock.Anything, spaceID, envAlias).Return(nil, errNotFound).Once()
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2"}}, nil).Once()
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2"}}}, nil).Once()
+
+			_, err = svc.Get(ctx, spaceID, envAlias)
+			require.Error(t, err)
+			assert.EqualError(t, err, "not found", "Ожидает что элемент был удален из кеша и сервис вернул ошибку на запрос по несуществующему Alias.")
+
+			v4, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.NotSame(t, v3, v4, "Ожидает что элемент был удален из кеша и получен из сервиса по ID.")
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+
+			envs.AssertExpectations(t)
+		})
+
+		t.Run("After Update", func(t *testing.T) {
+			envs := &mocksenvironments.Environments{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}, nil).Once()
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}}, nil).Once()
+			envs.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
+
+			v1, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+
+			v3, err := svc.Get(ctx, spaceID, envAlias)
+			require.NoError(t, err)
+			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+
+			vl1, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+
+			vl2, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+
+			err = svc.Update(ctx, &environments.Environment{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}})
+			require.NoError(t, err)
+
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}}, nil).Once()
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}}}, nil).Once()
+
+			_, err = svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+
+			v4, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.NotSame(t, v2, v4, "Ожидает что элемент был удален из кэша и будет запрошен заново из сервиса.")
+
+			v5, err := svc.Get(ctx, spaceID, envAlias)
+			require.NoError(t, err)
+			assert.Same(t, v4, v5, "Ожидается получение объекта из кэша по Alias после обновления объекта и получения по ID.")
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+
+			envs.AssertExpectations(t)
+		})
+
+		t.Run("After Update(List)", func(t *testing.T) {
+			envs := &mocksenvironments.Environments{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}}, nil).Once()
+			envs.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
+
+			vl1, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+
+			vl2, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+
+			err = svc.Update(ctx, &environments.Environment{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}})
+			require.NoError(t, err)
+
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}}}, nil).Once()
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+
+			envs.AssertExpectations(t)
+		})
+
+		t.Run("After Delete", func(t *testing.T) {
+			envs := &mocksenvironments.Environments{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}, nil).Once()
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}}, nil).Once()
+			envs.On("Delete", mock.Anything, spaceID, envID).Return(nil).Once()
+
+			v1, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+
+			v3, err := svc.Get(ctx, spaceID, envAlias)
+			require.NoError(t, err)
+			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+
+			vl1, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+
+			vl2, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+
+			err = svc.Delete(ctx, spaceID, envID)
+			require.NoError(t, err)
+
+			envs.On("Get", mock.Anything, spaceID, envID).Return(nil, errNotFound).Once()
+			envs.On("Get", mock.Anything, spaceID, envAlias).Return(nil, errNotFound).Once()
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{}, nil).Once()
+
+			_, err = svc.Get(ctx, spaceID, envID)
+			require.Error(t, err)
+			assert.EqualError(t, err, "not found", "Ожидает что элемент был удален из кэша по ID и получена ошибка от сервиса.")
+
+			_, err = svc.Get(ctx, spaceID, envAlias)
+			require.Error(t, err)
+			assert.EqualError(t, err, "not found", "Ожидает что элемент был удален  из кэша по Alias и получена ошибка от сервиса.")
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Len(t, vl3, 0, "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+
+			envs.AssertExpectations(t)
+		})
+
+		t.Run("After Create", func(t *testing.T) {
+			envs := &mocksenvironments.Environments{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment"}}, nil).Once()
+
+			vl1, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+
+			vl2, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+
+			envs.On("Create", mock.Anything, mock.Anything).Return(&environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"}, nil).Once()
+			_, err = svc.Create(ctx, &environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"})
+			require.NoError(t, err)
+
+			envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment"}, {ID: "envID2", SpaceID: spaceID, Description: "Environment2"}}, nil).Once()
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Len(t, vl3, 2, "Ожидает что объекты были удалены из кэша и запрошены заново из сервиса.")
+
+			envs.AssertExpectations(t)
+		})
+
+		t.Run("After size exceeded", func(t *testing.T) {
+			envs := &mocksenvironments.Environments{}
+
+			svc := CachingMiddleware(cache.NewCache(1, ttl))(envs)
+
+			envs.On("Get", mock.Anything, spaceID, "envID2").Return(&environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"}, nil).Once()
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment"}, nil).Once()
+			envs.On("Get", mock.Anything, spaceID, "envID2").Return(&environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"}, nil).Once()
+
+			v1, err := svc.Get(ctx, spaceID, "envID2")
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, "envID2")
+			require.NoError(t, err)
+			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+
+			_, err = svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+
+			v5, err := svc.Get(ctx, spaceID, "envID2")
+			require.NoError(t, err)
+			assert.NotSame(t, v2, v5, "Ожидает что объект был удален из кэша и будет запрошен заново из сервиса.")
+
+			envs.AssertExpectations(t)
+		})
+
+		t.Run("After TTL expired", func(t *testing.T) {
+			envs := &mocksenvironments.Environments{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(envs)
+
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment"}, nil).Once()
+			v1, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+
+			time.Sleep(2 * ttl)
+
+			envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment"}, nil).Once()
+			v3, err := svc.Get(ctx, spaceID, envID)
+			require.NoError(t, err)
+			assert.NotSame(t, v2, v3, "Ожидает что объект был удален из кэша и будет запрошен заново из сервиса.")
+
+			envs.AssertExpectations(t)
+		})
+	})
+}
diff --git a/pkg/environments/middleware/error_logging_middleware.go b/pkg/environments/middleware/error_logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..b365f36d591257fe74ad49f8350755b8f6da9815
--- /dev/null
+++ b/pkg/environments/middleware/error_logging_middleware.go
@@ -0,0 +1,110 @@
+// Code generated by gowrap. DO NOT EDIT.
+// template: ../../../assets/templates/middleware/error_log
+// gowrap: http://github.com/hexdigest/gowrap
+
+package service
+
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ../../../assets/templates/middleware/error_log -o error_logging_middleware.go -l ""
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"go.uber.org/zap"
+)
+
+// errorLoggingMiddleware implements environments.Environments that is instrumented with logging
+type errorLoggingMiddleware struct {
+	logger *zap.Logger
+	next   environments.Environments
+}
+
+// ErrorLoggingMiddleware instruments an implementation of the environments.Environments with simple logging
+func ErrorLoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next environments.Environments) environments.Environments {
+		return &errorLoggingMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *errorLoggingMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Create(ctx, env)
+}
+
+func (m *errorLoggingMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Delete(ctx, spaceId, envId)
+}
+
+func (m *errorLoggingMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Get(ctx, spaceId, envId)
+}
+
+func (m *errorLoggingMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.List(ctx, spaceId)
+}
+
+func (m *errorLoggingMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Migrate(ctx, spaceId, envId, options...)
+}
+
+func (m *errorLoggingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.RemoveAlias(ctx, spaceId, envId, alias)
+}
+
+func (m *errorLoggingMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.SetAlias(ctx, spaceId, envId, alias)
+}
+
+func (m *errorLoggingMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Update(ctx, env)
+}
diff --git a/pkg/environments/middleware/logging_middleware.go b/pkg/environments/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..74a2efbf83a466387219ad4fba8e0c0241d71735
--- /dev/null
+++ b/pkg/environments/middleware/logging_middleware.go
@@ -0,0 +1,325 @@
+// Code generated by gowrap. DO NOT EDIT.
+// template: ../../../assets/templates/middleware/access_log
+// gowrap: http://github.com/hexdigest/gowrap
+
+package service
+
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ../../../assets/templates/middleware/access_log -o logging_middleware.go -l ""
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/auth"
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+// loggingMiddleware implements environments.Environments that is instrumented with logging
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   environments.Environments
+}
+
+// LoggingMiddleware instruments an implementation of the environments.Environments with simple logging
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next environments.Environments) environments.Environments {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx": ctx,
+		"env": env} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Create.Request", fields...)
+
+	created, err = m.next.Create(ctx, env)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"created": created,
+		"err":     err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Create.Response", fields...)
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":     ctx,
+		"spaceId": spaceId,
+		"envId":   envId} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Delete.Request", fields...)
+
+	err = m.next.Delete(ctx, spaceId, envId)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Delete.Response", fields...)
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":     ctx,
+		"spaceId": spaceId,
+		"envId":   envId} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Get.Request", fields...)
+
+	env, err = m.next.Get(ctx, spaceId, envId)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"env": env,
+		"err": err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Get.Response", fields...)
+
+	return env, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":     ctx,
+		"spaceId": spaceId} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("List.Request", fields...)
+
+	envs, err = m.next.List(ctx, spaceId)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"envs": envs,
+		"err":  err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("List.Response", fields...)
+
+	return envs, err
+}
+
+func (m *loggingMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":     ctx,
+		"spaceId": spaceId,
+		"envId":   envId,
+		"options": options} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Migrate.Request", fields...)
+
+	err = m.next.Migrate(ctx, spaceId, envId, options...)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Migrate.Response", fields...)
+
+	return err
+}
+
+func (m *loggingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":     ctx,
+		"spaceId": spaceId,
+		"envId":   envId,
+		"alias":   alias} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("RemoveAlias.Request", fields...)
+
+	err = m.next.RemoveAlias(ctx, spaceId, envId, alias)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("RemoveAlias.Response", fields...)
+
+	return err
+}
+
+func (m *loggingMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":     ctx,
+		"spaceId": spaceId,
+		"envId":   envId,
+		"alias":   alias} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("SetAlias.Request", fields...)
+
+	err = m.next.SetAlias(ctx, spaceId, envId, alias)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("SetAlias.Response", fields...)
+
+	return err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx": ctx,
+		"env": env} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Update.Request", fields...)
+
+	err = m.next.Update(ctx, env)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Update.Response", fields...)
+
+	return err
+}
diff --git a/pkg/environments/middleware/middleware.go b/pkg/environments/middleware/middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..b5e29a99db170df7d2cc5003b2d9a206a423fef2
--- /dev/null
+++ b/pkg/environments/middleware/middleware.go
@@ -0,0 +1,28 @@
+// Code generated by gowrap. DO NOT EDIT.
+// template: ../../../assets/templates/middleware/middleware
+// gowrap: http://github.com/hexdigest/gowrap
+
+package service
+
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ../../../assets/templates/middleware/middleware -o middleware.go -l ""
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"go.uber.org/zap"
+)
+
+type Middleware func(environments.Environments) environments.Environments
+
+func WithLog(s environments.Environments, logger *zap.Logger, log_access bool) environments.Environments {
+	if logger == nil {
+		logger = zap.NewNop()
+	}
+
+	logger = logger.Named("Environments")
+	s = ErrorLoggingMiddleware(logger)(s)
+	if log_access {
+		s = LoggingMiddleware(logger)(s)
+	}
+	s = RecoveringMiddleware(logger)(s)
+	return s
+}
diff --git a/pkg/environments/middleware/recovering_middleware.go b/pkg/environments/middleware/recovering_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..bf4bec7d46fa8b3e963c65330c6883c9c7fb0c20
--- /dev/null
+++ b/pkg/environments/middleware/recovering_middleware.go
@@ -0,0 +1,127 @@
+// Code generated by gowrap. DO NOT EDIT.
+// template: ../../../assets/templates/middleware/recovery
+// gowrap: http://github.com/hexdigest/gowrap
+
+package service
+
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ../../../assets/templates/middleware/recovery -o recovering_middleware.go -l ""
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"go.uber.org/zap"
+)
+
+// recoveringMiddleware implements environments.Environments that is instrumented with logging
+type recoveringMiddleware struct {
+	logger *zap.Logger
+	next   environments.Environments
+}
+
+// RecoveringMiddleware instruments an implementation of the environments.Environments with simple logging
+func RecoveringMiddleware(logger *zap.Logger) Middleware {
+	return func(next environments.Environments) environments.Environments {
+		return &recoveringMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *recoveringMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Create(ctx, env)
+}
+
+func (m *recoveringMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Delete(ctx, spaceId, envId)
+}
+
+func (m *recoveringMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Get(ctx, spaceId, envId)
+}
+
+func (m *recoveringMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.List(ctx, spaceId)
+}
+
+func (m *recoveringMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Migrate(ctx, spaceId, envId, options...)
+}
+
+func (m *recoveringMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.RemoveAlias(ctx, spaceId, envId, alias)
+}
+
+func (m *recoveringMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.SetAlias(ctx, spaceId, envId, alias)
+}
+
+func (m *recoveringMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Update(ctx, env)
+}