private (TaskRelatedData removingData, TaskRelatedData addingData) DetermineAddedAndRemovedItems(
            TaskAggregate currentTask, TaskRelatedData newData)
        {
            IEnumerable <UserModel>  addedMembers = newData.Users.Except <UserModel>(currentTask.Members, _modelsByIdComparer).ToList();
            IEnumerable <LabelModel> addedLabels  = newData.Labels.Except <LabelModel>(currentTask.Labels, _modelsByIdComparer).ToList();

            IEnumerable <UserModel>  removedMembers = currentTask.Members.Except <UserModel>(newData.Users, _modelsByIdComparer).ToList();
            IEnumerable <LabelModel> removedLabels  = currentTask.Labels.Except <LabelModel>(newData.Labels, _modelsByIdComparer).ToList();

            TaskRelatedData addingCollections = new TaskRelatedData
            {
                Users  = addedMembers,
                Labels = addedLabels
            };

            TaskRelatedData removingCollections = new TaskRelatedData
            {
                Users  = removedMembers,
                Labels = removedLabels
            };

            return(removingCollections, addingCollections);
        }
        public async Task <TaskAggregate> UpdateTaskAsync(TaskModel updatingTask, IEnumerable <string> usersIds,
                                                          IEnumerable <string> labelsIds)
        {
            TaskAggregate currentTask = await _tasksRepository.GetTaskAsync(updatingTask.Id);

            if (currentTask == null)
            {
                throw new NotFoundException($"Task with id = {updatingTask.Id} not found");
            }

            if (currentTask.Version != updatingTask.Version)
            {
                throw new VersionsNotMatchException();
            }

            TaskRelatedData taskData = await GetAndCheckTaskRelatedDataAsync(updatingTask, usersIds, labelsIds);

            EnsureProjectNotChanged(currentTask, taskData);

            var(removingCollections, addingCollections) = DetermineAddedAndRemovedItems(currentTask, taskData);
            var list = taskData.List;

            var addedMembersRecords   = _mapper.Map <IEnumerable <UserModel>, IEnumerable <TaskUserRecord> >(addingCollections.Users);
            var removedMembersRecords = _mapper.Map <IEnumerable <UserModel>, IEnumerable <TaskUserRecord> >(removingCollections.Users);
            var addedLabelsRecords    = _mapper.Map <IEnumerable <LabelModel>, IEnumerable <TaskLabelRecord> >(addingCollections.Labels);
            var removedLabelsRecords  = _mapper.Map <IEnumerable <LabelModel>, IEnumerable <TaskLabelRecord> >(removingCollections.Labels);

            var outboxMessage = OutboxMessageModel.Create(
                new TaskUpdatedMessage
            {
                TaskId         = updatingTask.Id,
                Title          = updatingTask.Title,
                ProjectId      = list.ProjectId,
                ProjectTitle   = list.ProjectTitle,
                ListId         = list.Id,
                ListTitle      = list.Title,
                RemovedMembers = removedMembersRecords,
                AddedMembers   = addedMembersRecords,
                RemovedLabels  = removedLabelsRecords,
                AddedLabels    = addedLabelsRecords
            }, Topics.Tasks, MessageActions.Updated);

            updatingTask.Version += 1;

            var updatedTask = await _tasksRepository.UpdateTaskAsync(updatingTask, addingCollections.ToTaskCollections(),
                                                                     removingCollections.ToTaskCollections(), outboxMessage);


            string cacheKey = string.Format(CacheSettings.TaskIdCacheKeyPattern, updatedTask.Id);

            List <Task> cacheInvalidationTasks = new List <Task>();

            cacheInvalidationTasks.Add(_cache.SetCacheValueAsync(cacheKey, updatedTask, CacheSettings.TaskIdCacheLifetime));

            foreach (var user in usersIds)
            {
                string userCacheKey = string.Format(CacheSettings.UserTasksCacheKeyPattern, user);
                cacheInvalidationTasks.Add(_cache.RemoveAsync(userCacheKey));
            }

            foreach (var user in removingCollections.Users)
            {
                string userCacheKey = string.Format(CacheSettings.UserTasksCacheKeyPattern, user.Id);
                cacheInvalidationTasks.Add(_cache.RemoveAsync(userCacheKey));
            }

            await Task.WhenAll(cacheInvalidationTasks);

            return(updatedTask);
        }