diff --git a/pkg/roles/middleware/caching_middleware.go b/pkg/roles/middleware/caching_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..19bdfe6b890806a98ac4aed81485f2f5314020b6
--- /dev/null
+++ b/pkg/roles/middleware/caching_middleware.go
@@ -0,0 +1,80 @@
+package service
+
+import (
+	"context"
+	"strings"
+
+	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	service "git.perx.ru/perxis/perxis-go/pkg/roles"
+)
+
+func makeKey(ss ...string) string {
+	return strings.Join(ss, "-")
+}
+
+func CachingMiddleware(cache *cache.Cache) Middleware {
+	return func(next service.Roles) service.Roles {
+		return &cachingMiddleware{
+			cache: cache,
+			next:  next,
+		}
+	}
+}
+
+type cachingMiddleware struct {
+	cache *cache.Cache
+	next  service.Roles
+}
+
+func (m cachingMiddleware) Create(ctx context.Context, role *service.Role) (rl *service.Role, err error) {
+	rl, err = m.next.Create(ctx, role)
+	if err == nil {
+		m.cache.Remove(rl.SpaceID)
+	}
+	return rl, err
+}
+
+func (m cachingMiddleware) Get(ctx context.Context, spaceId string, roleId string) (rl *service.Role, err error) {
+	key := makeKey(spaceId, roleId)
+	value, e := m.cache.Get(key)
+	if e == nil {
+		return value.(*service.Role), err
+	}
+	rl, err = m.next.Get(ctx, spaceId, roleId)
+	if err == nil {
+		m.cache.Set(key, rl)
+	}
+	return rl, err
+}
+
+func (m cachingMiddleware) List(ctx context.Context, spaceId string) (roles []*service.Role, err error) {
+	value, e := m.cache.Get(spaceId)
+	if e == nil {
+		return value.([]*service.Role), err
+	}
+	roles, err = m.next.List(ctx, spaceId)
+	if err == nil {
+		m.cache.Set(spaceId, roles)
+	}
+	return roles, err
+}
+
+func (m cachingMiddleware) Update(ctx context.Context, role *service.Role) (err error) {
+	err = m.next.Update(ctx, role)
+	if err == nil {
+		key := makeKey(role.SpaceID, role.ID)
+		m.cache.Remove(key)
+		m.cache.Remove(role.SpaceID)
+	}
+	return err
+}
+
+func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, roleId string) (err error) {
+	err = m.next.Delete(ctx, spaceId, roleId)
+	if err == nil {
+		key := makeKey(spaceId, roleId)
+		m.cache.Remove(key)
+		m.cache.Remove(spaceId)
+	}
+	return err
+}
diff --git a/pkg/roles/middleware/caching_middleware_test.go b/pkg/roles/middleware/caching_middleware_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..bb5496b55fae164ea3681e644c7625ab9a2b9407
--- /dev/null
+++ b/pkg/roles/middleware/caching_middleware_test.go
@@ -0,0 +1,201 @@
+package service
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	rsmocks "git.perx.ru/perxis/perxis-go/pkg/roles/mocks"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
+)
+
+func TestRolesCache(t *testing.T) {
+
+	const (
+		roleID  = "roleID"
+		spaceID = "spaceID"
+		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) {
+		rl := &rsmocks.Roles{}
+
+		svc := CachingMiddleware(cache.NewCache(size, ttl))(rl)
+
+		rl.On("Get", mock.Anything, spaceID, roleID).Return(&roles.Role{ID: roleID, SpaceID: spaceID, Description: "Role"}, nil).Once()
+
+		v1, err := svc.Get(ctx, spaceID, roleID)
+		require.NoError(t, err)
+
+		v2, err := svc.Get(ctx, spaceID, roleID)
+		require.NoError(t, err)
+		assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+
+		rl.AssertExpectations(t)
+	})
+
+	t.Run("List from cache", func(t *testing.T) {
+		rl := &rsmocks.Roles{}
+
+		svc := CachingMiddleware(cache.NewCache(size, ttl))(rl)
+
+		rl.On("List", mock.Anything, spaceID).Return([]*roles.Role{{ID: roleID, SpaceID: spaceID, Description: "Role"}}, 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], "Ожидается при повторном запросе получение объектов из кэша.")
+
+		rl.AssertExpectations(t)
+	})
+
+	t.Run("Invalidate cache", func(t *testing.T) {
+		t.Run("After Update", func(t *testing.T) {
+			rl := &rsmocks.Roles{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(rl)
+
+			rl.On("Get", mock.Anything, spaceID, roleID).Return(&roles.Role{ID: roleID, SpaceID: spaceID, Description: "Role"}, nil).Once()
+			rl.On("List", mock.Anything, spaceID).Return([]*roles.Role{{ID: roleID, SpaceID: spaceID, Description: "Role"}}, nil).Once()
+
+			v1, err := svc.Get(ctx, spaceID, roleID)
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, roleID)
+			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], "Ожидается при повторном запросе получение объектов из кэша.")
+
+			rl.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
+
+			err = svc.Update(ctx, &roles.Role{ID: roleID, SpaceID: spaceID, Description: "RoleUPD"})
+			require.NoError(t, err)
+
+			rl.On("Get", mock.Anything, spaceID, roleID).Return(&roles.Role{ID: roleID, SpaceID: spaceID, Description: "RoleUPD"}, nil).Once()
+			rl.On("List", mock.Anything, spaceID).Return([]*roles.Role{{ID: roleID, SpaceID: spaceID, Description: "RoleUPD"}}, nil).Once()
+
+			v3, err := svc.Get(ctx, spaceID, roleID)
+			require.NoError(t, err)
+			assert.NotSame(t, v2, v3, "Ожидается что кеш объекта был удален после его обновления и объект был запрошен из сервиса.")
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.NotSame(t, vl2[0], vl3[0], "Ожидается что кеш объектов был удален после обновления объекта.")
+
+			rl.AssertExpectations(t)
+		})
+
+		t.Run("After Delete", func(t *testing.T) {
+			rl := &rsmocks.Roles{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(rl)
+
+			rl.On("Get", mock.Anything, spaceID, roleID).Return(&roles.Role{ID: roleID, SpaceID: spaceID, Description: "Role"}, nil).Once()
+			rl.On("List", mock.Anything, spaceID).Return([]*roles.Role{{ID: roleID, SpaceID: spaceID, Description: "Role"}}, nil).Once()
+
+			v1, err := svc.Get(ctx, spaceID, roleID)
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, roleID)
+			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], "Ожидается при повторном запросе получение объектов из кэша.")
+
+			rl.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
+
+			err = svc.Update(ctx, &roles.Role{ID: roleID, SpaceID: spaceID, Description: "RoleUPD"})
+			require.NoError(t, err)
+
+			rl.On("Get", mock.Anything, spaceID, roleID).Return(nil, errNotFound).Once()
+			rl.On("List", mock.Anything, spaceID).Return(nil, errNotFound).Once()
+
+			v3, err := svc.Get(ctx, spaceID, roleID)
+			require.Error(t, err)
+			assert.EqualError(t, err, "not found", "Ожидается что после удаления кеш объекта был удален и получена ошибка сервиса.")
+			assert.Nil(t, v3)
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.Error(t, err)
+			assert.EqualError(t, err, "not found", "Ожидается что после удаления кеш объекта был удален и получена ошибка сервиса.")
+			assert.Nil(t, vl3)
+
+			rl.AssertExpectations(t)
+		})
+
+		t.Run("After Create", func(t *testing.T) {
+			rl := &rsmocks.Roles{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(rl)
+
+			rl.On("List", mock.Anything, spaceID).Return([]*roles.Role{{ID: roleID, SpaceID: spaceID, Description: "Role"}}, 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], "Ожидается при повторном запросе получение объекта из кэша.")
+
+			rl.On("Create", mock.Anything, mock.Anything).Return(&roles.Role{ID: "roleID2", SpaceID: spaceID, Description: "Role2"}, nil).Once()
+
+			_, err = svc.Create(ctx, &roles.Role{ID: "roleID2", SpaceID: spaceID, Description: "Role2"})
+			require.NoError(t, err)
+
+			rl.On("List", mock.Anything, spaceID).Return([]*roles.Role{{ID: roleID, SpaceID: spaceID, Description: "Role"}, {ID: "roleID2", SpaceID: spaceID, Description: "Role2"}}, nil).Once()
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Len(t, vl3, 2, "Ожидает что после создания нового объекта,  кеш будет очищен и объекты запрошены заново из сервиса.")
+
+			rl.AssertExpectations(t)
+		})
+
+		t.Run("After TTL expired", func(t *testing.T) {
+			rl := &rsmocks.Roles{}
+
+			svc := CachingMiddleware(cache.NewCache(size, ttl))(rl)
+
+			rl.On("Get", mock.Anything, spaceID, roleID).Return(&roles.Role{ID: roleID, SpaceID: spaceID, Description: "Role"}, nil).Once()
+
+			v1, err := svc.Get(ctx, spaceID, roleID)
+			require.NoError(t, err)
+
+			v2, err := svc.Get(ctx, spaceID, roleID)
+			require.NoError(t, err)
+			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+
+			time.Sleep(2 * ttl)
+			rl.On("Get", mock.Anything, spaceID, roleID).Return(&roles.Role{ID: roleID, SpaceID: spaceID, Description: "Role"}, nil).Once()
+
+			v3, err := svc.Get(ctx, spaceID, roleID)
+			require.NoError(t, err)
+			assert.NotSame(t, v2, v3, "Ожидается что объект был удален из кеша и получен заново из сервиса.")
+
+			rl.AssertExpectations(t)
+		})
+	})
+}
diff --git a/pkg/roles/middleware/error_logging_middleware.go b/pkg/roles/middleware/error_logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..a6ff0af444ed4154354d0d732f2b5edae612bcdc
--- /dev/null
+++ b/pkg/roles/middleware/error_logging_middleware.go
@@ -0,0 +1,80 @@
+// 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/roles -i Roles -t ../../../assets/templates/middleware/error_log -o error_logging_middleware.go -l ""
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	"go.uber.org/zap"
+)
+
+// errorLoggingMiddleware implements roles.Roles that is instrumented with logging
+type errorLoggingMiddleware struct {
+	logger *zap.Logger
+	next   roles.Roles
+}
+
+// ErrorLoggingMiddleware instruments an implementation of the roles.Roles with simple logging
+func ErrorLoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next roles.Roles) roles.Roles {
+		return &errorLoggingMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *errorLoggingMiddleware) Create(ctx context.Context, role *roles.Role) (created *roles.Role, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Create(ctx, role)
+}
+
+func (m *errorLoggingMiddleware) Delete(ctx context.Context, spaceId string, roleId string) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Delete(ctx, spaceId, roleId)
+}
+
+func (m *errorLoggingMiddleware) Get(ctx context.Context, spaceId string, roleId string) (role *roles.Role, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Get(ctx, spaceId, roleId)
+}
+
+func (m *errorLoggingMiddleware) List(ctx context.Context, spaceId string) (roles []*roles.Role, 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) Update(ctx context.Context, role *roles.Role) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Update(ctx, role)
+}
diff --git a/pkg/roles/middleware/logging_middleware.go b/pkg/roles/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..764a0136fc2bc72d040d3cb429ccc40103678190
--- /dev/null
+++ b/pkg/roles/middleware/logging_middleware.go
@@ -0,0 +1,214 @@
+// 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/roles -i Roles -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/roles"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+// loggingMiddleware implements roles.Roles that is instrumented with logging
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   roles.Roles
+}
+
+// LoggingMiddleware instruments an implementation of the roles.Roles with simple logging
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next roles.Roles) roles.Roles {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, role *roles.Role) (created *roles.Role, err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":  ctx,
+		"role": role} {
+		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, role)
+
+	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, roleId string) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":     ctx,
+		"spaceId": spaceId,
+		"roleId":  roleId} {
+		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, roleId)
+
+	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, roleId string) (role *roles.Role, err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":     ctx,
+		"spaceId": spaceId,
+		"roleId":  roleId} {
+		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...)
+
+	role, err = m.next.Get(ctx, spaceId, roleId)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	}
+
+	for k, v := range map[string]interface{}{
+		"role": role,
+		"err":  err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Get.Response", fields...)
+
+	return role, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (roles []*roles.Role, 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...)
+
+	roles, 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{}{
+		"roles": roles,
+		"err":   err} {
+		if k == "err" {
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("List.Response", fields...)
+
+	return roles, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, role *roles.Role) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":  ctx,
+		"role": role} {
+		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, role)
+
+	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/roles/middleware/middleware.go b/pkg/roles/middleware/middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..0a5198110dfc463b80274230a7d1cbc65283debd
--- /dev/null
+++ b/pkg/roles/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/roles -i Roles -t ../../../assets/templates/middleware/middleware -o middleware.go -l ""
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	"go.uber.org/zap"
+)
+
+type Middleware func(roles.Roles) roles.Roles
+
+func WithLog(s roles.Roles, logger *zap.Logger, log_access bool) roles.Roles {
+	if logger == nil {
+		logger = zap.NewNop()
+	}
+
+	logger = logger.Named("Roles")
+	s = ErrorLoggingMiddleware(logger)(s)
+	if log_access {
+		s = LoggingMiddleware(logger)(s)
+	}
+	s = RecoveringMiddleware(logger)(s)
+	return s
+}
diff --git a/pkg/roles/middleware/recovering_middleware.go b/pkg/roles/middleware/recovering_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..0c0f023b6fa4ccb6f8dc4dadbf59a58bf024a6cb
--- /dev/null
+++ b/pkg/roles/middleware/recovering_middleware.go
@@ -0,0 +1,91 @@
+// 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/roles -i Roles -t ../../../assets/templates/middleware/recovery -o recovering_middleware.go -l ""
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	"go.uber.org/zap"
+)
+
+// recoveringMiddleware implements roles.Roles that is instrumented with logging
+type recoveringMiddleware struct {
+	logger *zap.Logger
+	next   roles.Roles
+}
+
+// RecoveringMiddleware instruments an implementation of the roles.Roles with simple logging
+func RecoveringMiddleware(logger *zap.Logger) Middleware {
+	return func(next roles.Roles) roles.Roles {
+		return &recoveringMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *recoveringMiddleware) Create(ctx context.Context, role *roles.Role) (created *roles.Role, 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, role)
+}
+
+func (m *recoveringMiddleware) Delete(ctx context.Context, spaceId string, roleId 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, roleId)
+}
+
+func (m *recoveringMiddleware) Get(ctx context.Context, spaceId string, roleId string) (role *roles.Role, 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, roleId)
+}
+
+func (m *recoveringMiddleware) List(ctx context.Context, spaceId string) (roles []*roles.Role, 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) Update(ctx context.Context, role *roles.Role) (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, role)
+}