diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index ca85ba6b25758fa4adb02737dc7b5a67b205487f..4e7a716bfe411deafd8bd3e7cc7ece5781b2b772 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -74,8 +74,7 @@ class Projects::IssuesController < Projects::ApplicationController
   end
 
   def update
-    @issue.update_attributes(params[:issue])
-    @issue.reset_events_cache
+    @issue = Issues::UpdateService.new(project, current_user, params[:issue]).execute(issue)
 
     respond_to do |format|
       format.js
diff --git a/app/observers/issue_observer.rb b/app/observers/issue_observer.rb
index c2132ddca55dd3e6169ef904ca7c62148ae512ba..b4880b12fd7768ed3bbfb25e83e33ca7dbd74994 100644
--- a/app/observers/issue_observer.rb
+++ b/app/observers/issue_observer.rb
@@ -12,16 +12,6 @@ class IssueObserver < BaseObserver
     execute_hooks(issue)
   end
 
-  def after_update(issue)
-    if issue.is_being_reassigned?
-      notification.reassigned_issue(issue, current_user)
-      create_assignee_note(issue)
-    end
-
-    issue.notice_added_references(issue.project, current_user)
-    execute_hooks(issue)
-  end
-
   protected
 
   # Create issue note with service comment like 'Status changed to closed'
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e04c1c6fb7a88fb4f9762e4a06855dfcb283f645
--- /dev/null
+++ b/app/services/issues/base_service.rb
@@ -0,0 +1,19 @@
+module Issues
+  class BaseService < ::BaseService
+
+    private
+
+    # Create issue note with service comment like 'Status changed to closed'
+    def create_note(issue)
+      Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit)
+    end
+
+    def create_assignee_note(issue)
+      Note.create_assignee_change_note(issue, issue.project, current_user, issue.assignee)
+    end
+
+    def execute_hooks(issue)
+      issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)
+    end
+  end
+end
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 37f440fc40e7c96e634f1fa7b58f7d3d0cae0208..8008086dae93b2bb358dcabe958ea3aeb30363c2 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -13,11 +13,5 @@ module Issues
 
       issue
     end
-
-    private
-
-    def execute_hooks(issue)
-      issue.project.execute_hooks(issue.to_hook_data, :issue_hooks)
-    end
   end
 end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4db9dee11d3d6c0e49c88f983e8e2e56299b25a9
--- /dev/null
+++ b/app/services/issues/update_service.rb
@@ -0,0 +1,19 @@
+module Issues
+  class UpdateService < BaseService
+    def execute(issue)
+      if issue.update_attributes(params)
+        issue.reset_events_cache
+
+        if issue.is_being_reassigned?
+          notification.reassigned_issue(issue, current_user)
+          create_assignee_note(issue)
+        end
+
+        issue.notice_added_references(issue.project, current_user)
+        execute_hooks(issue)
+      end
+
+      issue
+    end
+  end
+end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 169c58b00751f9abe2f88f3047867517a56d371e..f50be3a815d9038b8345c998d22ed1a09f385411 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -74,18 +74,18 @@ module API
       # Example Request:
       #   PUT /projects/:id/issues/:issue_id
       put ":id/issues/:issue_id" do
-        set_current_user_for_thread do
-          @issue = user_project.issues.find(params[:issue_id])
-          authorize! :modify_issue, @issue
+        issue = user_project.issues.find(params[:issue_id])
+        authorize! :modify_issue, issue
 
-          attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
-          attrs[:label_list] = params[:labels] if params[:labels].present?
+        attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
+        attrs[:label_list] = params[:labels] if params[:labels].present?
+
+        issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
 
-          if @issue.update_attributes attrs
-            present @issue, with: Entities::Issue
-          else
-            not_found!
-          end
+        if issue.valid?
+          present issue, with: Entities::Issue
+        else
+          not_found!
         end
       end
 
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 7e2d5ad2e81d8f0864bb95a1dd5819c8d31e7cdc..90720be5ded3618eca088277014a943151a84ee4 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -17,6 +17,7 @@ describe Issues::CreateService do
       end
 
       it { @issue.should be_valid }
+      it { @issue.title.should == 'Awesome issue' }
     end
   end
 end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9bfc0f674de726f9897592b61c71486e5f89d028
--- /dev/null
+++ b/spec/services/issues/update_service_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Issues::UpdateService do
+  let(:project) { create(:empty_project) }
+  let(:user) { create(:user) }
+  let(:issue) { create(:issue) }
+
+  describe :execute do
+    context "valid params" do
+      before do
+        project.team << [user, :master]
+        opts = {
+          title: 'New title',
+          description: 'Also please fix'
+        }
+
+        @issue = Issues::UpdateService.new(project, user, opts).execute(issue)
+      end
+
+      it { @issue.should be_valid }
+      it { @issue.title.should == 'New title' }
+    end
+  end
+end