diff --git a/apps/user_ldap/lib/Jobs/UpdateGroups.php b/apps/user_ldap/lib/Jobs/UpdateGroups.php
index 58254bf41e9ecd0a5edf578f823cdead82994ce3..035caf0c33c71875e04f8ffe1eb8c26b41a8cee6 100644
--- a/apps/user_ldap/lib/Jobs/UpdateGroups.php
+++ b/apps/user_ldap/lib/Jobs/UpdateGroups.php
@@ -44,6 +44,8 @@ use OCA\User_LDAP\LogWrapper;
 use OCA\User_LDAP\Mapping\GroupMapping;
 use OCA\User_LDAP\Mapping\UserMapping;
 use OCA\User_LDAP\User\Manager;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Group\Events\UserRemovedEvent;
 use OCP\ILogger;
 
 class UpdateGroups extends \OC\BackgroundJob\TimedJob {
@@ -94,6 +96,10 @@ class UpdateGroups extends \OC\BackgroundJob\TimedJob {
 	 * @param string[] $groups
 	 */
 	private static function handleKnownGroups($groups) {
+		$dispatcher = \OC::$server->query(IEventDispatcher::class);
+		$groupManager = \OC::$server->getGroupManager();
+		$userManager = \OC::$server->getUserManager();
+
 		\OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Dealing with known Groups.', ILogger::DEBUG);
 		$query = \OC_DB::prepare('
 			UPDATE `*PREFIX*ldap_group_members`
@@ -105,8 +111,11 @@ class UpdateGroups extends \OC\BackgroundJob\TimedJob {
 			$knownUsers = unserialize(self::$groupsFromDB[$group]['owncloudusers']);
 			$actualUsers = self::getGroupBE()->usersInGroup($group);
 			$hasChanged = false;
+
+			$groupObject = $groupManager->get($group);
 			foreach (array_diff($knownUsers, $actualUsers) as $removedUser) {
-				\OCP\Util::emitHook('OC_User', 'post_removeFromGroup', ['uid' => $removedUser, 'gid' => $group]);
+				$userObject = $userManager->get($removedUser);
+				$dispatcher->dispatchTyped(new UserRemovedEvent($groupObject, $userObject));
 				\OCP\Util::writeLog('user_ldap',
 				'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".',
 					ILogger::INFO);
diff --git a/lib/base.php b/lib/base.php
index a491cb11c80795faeca2c99bbecb627fbc3f50a4..8b8e8e5fe32c2bc82e9452ac6a64867f6ccc17e5 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -61,6 +61,7 @@
  *
  */
 
+use OCP\EventDispatcher\IEventDispatcher;
 use OCP\Group\Events\UserRemovedEvent;
 use OCP\ILogger;
 use OCP\Share;
@@ -898,11 +899,10 @@ class OC {
 	public static function registerShareHooks() {
 		if (\OC::$server->getSystemConfig()->getValue('installed')) {
 			OC_Hook::connect('OC_User', 'post_deleteUser', Hooks::class, 'post_deleteUser');
-			OC_Hook::connect('OC_User', 'post_removeFromGroup', Hooks::class, 'post_removeFromGroupLDAP');
 			OC_Hook::connect('OC_User', 'post_deleteGroup', Hooks::class, 'post_deleteGroup');
 
-			/** @var \OCP\EventDispatcher\IEventDispatcher $dispatcher */
-			$dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class);
+			/** @var IEventDispatcher $dispatcher */
+			$dispatcher = \OC::$server->get(IEventDispatcher::class);
 			$dispatcher->addServiceListener(UserRemovedEvent::class, \OC\Share20\UserRemovedListener::class);
 		}
 	}
diff --git a/lib/private/Share20/Hooks.php b/lib/private/Share20/Hooks.php
index 0e41e20a2cd0f921f8a2acfcaeedecec64c49a6d..b596123bbe0cc9e0366db9471ff2bce46ea77c35 100644
--- a/lib/private/Share20/Hooks.php
+++ b/lib/private/Share20/Hooks.php
@@ -30,8 +30,4 @@ class Hooks {
 	public static function post_deleteGroup($arguments) {
 		\OC::$server->getShareManager()->groupDeleted($arguments['gid']);
 	}
-
-	public static function post_removeFromGroupLDAP($arguments) {
-		\OC::$server->getShareManager()->userDeletedFromGroup($arguments['uid'], $arguments['gid']);
-	}
 }
diff --git a/lib/private/User/User.php b/lib/private/User/User.php
index 08bbce4701b103ce3b6dc96f5e170f929ad09cc5..2d27b204d090b6373d40cc0d51141a26781bb6ff 100644
--- a/lib/private/User/User.php
+++ b/lib/private/User/User.php
@@ -41,6 +41,9 @@ use OC\Avatar\AvatarManager;
 use OC\Files\Cache\Storage;
 use OC\Hooks\Emitter;
 use OC_Helper;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Group\Events\BeforeUserRemovedEvent;
+use OCP\Group\Events\UserRemovedEvent;
 use OCP\IAvatarManager;
 use OCP\IConfig;
 use OCP\IImage;
@@ -61,6 +64,9 @@ class User implements IUser {
 	/** @var UserInterface|null */
 	private $backend;
 	/** @var EventDispatcherInterface */
+	private $legacyDispatcher;
+
+	/** @var IEventDispatcher */
 	private $dispatcher;
 
 	/** @var bool */
@@ -87,7 +93,7 @@ class User implements IUser {
 	public function __construct(string $uid, ?UserInterface $backend, EventDispatcherInterface $dispatcher, $emitter = null, IConfig $config = null, $urlGenerator = null) {
 		$this->uid = $uid;
 		$this->backend = $backend;
-		$this->dispatcher = $dispatcher;
+		$this->legacyDispatcher = $dispatcher;
 		$this->emitter = $emitter;
 		if (is_null($config)) {
 			$config = \OC::$server->getConfig();
@@ -100,6 +106,8 @@ class User implements IUser {
 		if (is_null($this->urlGenerator)) {
 			$this->urlGenerator = \OC::$server->getURLGenerator();
 		}
+		// TODO: inject
+		$this->dispatcher = \OC::$server->query(IEventDispatcher::class);
 	}
 
 	/**
@@ -203,7 +211,7 @@ class User implements IUser {
 	 * @return bool
 	 */
 	public function delete() {
-		$this->dispatcher->dispatch(IUser::class . '::preDelete', new GenericEvent($this));
+		$this->legacyDispatcher->dispatch(IUser::class . '::preDelete', new GenericEvent($this));
 		if ($this->emitter) {
 			$this->emitter->emit('\OC\User', 'preDelete', [$this]);
 		}
@@ -219,9 +227,9 @@ class User implements IUser {
 			foreach ($groupManager->getUserGroupIds($this) as $groupId) {
 				$group = $groupManager->get($groupId);
 				if ($group) {
-					\OC_Hook::emit("OC_Group", "pre_removeFromGroup", ["run" => true, "uid" => $this->uid, "gid" => $groupId]);
+					$this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($group, $this));
 					$group->removeUser($this);
-					\OC_Hook::emit("OC_User", "post_removeFromGroup", ["uid" => $this->uid, "gid" => $groupId]);
+					$this->dispatcher->dispatchTyped(new UserRemovedEvent($group, $this));
 				}
 			}
 			// Delete the user's keys in preferences
@@ -252,7 +260,7 @@ class User implements IUser {
 			$accountManager = \OC::$server->query(AccountManager::class);
 			$accountManager->deleteUser($this);
 
-			$this->dispatcher->dispatch(IUser::class . '::postDelete', new GenericEvent($this));
+			$this->legacyDispatcher->dispatch(IUser::class . '::postDelete', new GenericEvent($this));
 			if ($this->emitter) {
 				$this->emitter->emit('\OC\User', 'postDelete', [$this]);
 			}
@@ -268,7 +276,7 @@ class User implements IUser {
 	 * @return bool
 	 */
 	public function setPassword($password, $recoveryPassword = null) {
-		$this->dispatcher->dispatch(IUser::class . '::preSetPassword', new GenericEvent($this, [
+		$this->legacyDispatcher->dispatch(IUser::class . '::preSetPassword', new GenericEvent($this, [
 			'password' => $password,
 			'recoveryPassword' => $recoveryPassword,
 		]));
@@ -277,7 +285,7 @@ class User implements IUser {
 		}
 		if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
 			$result = $this->backend->setPassword($this->uid, $password);
-			$this->dispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [
+			$this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [
 				'password' => $password,
 				'recoveryPassword' => $recoveryPassword,
 			]));
@@ -474,7 +482,7 @@ class User implements IUser {
 	}
 
 	public function triggerChange($feature, $value = null, $oldValue = null) {
-		$this->dispatcher->dispatch(IUser::class . '::changeUser', new GenericEvent($this, [
+		$this->legacyDispatcher->dispatch(IUser::class . '::changeUser', new GenericEvent($this, [
 			'feature' => $feature,
 			'value' => $value,
 			'oldValue' => $oldValue,
diff --git a/lib/public/Group/Events/BeforeUserRemovedEvent.php b/lib/public/Group/Events/BeforeUserRemovedEvent.php
index cc657f952e6ef2341bfd22781b240a5790649416..fb0f7f4519167bf6f4cef2a5e256d5f310486958 100644
--- a/lib/public/Group/Events/BeforeUserRemovedEvent.php
+++ b/lib/public/Group/Events/BeforeUserRemovedEvent.php
@@ -32,6 +32,10 @@ use OCP\IUser;
 
 /**
  * @since 18.0.0
+ * @deprecated 20.0.0 - it can't be guaranteed that this event is triggered in
+ * all case (e.g. for LDAP users this isn't possible) - if there is a valid use
+ * case please reach out in the issue tracker at
+ * https://github.com/nextcloud/server/issues
  */
 class BeforeUserRemovedEvent extends Event {
 
@@ -43,6 +47,7 @@ class BeforeUserRemovedEvent extends Event {
 
 	/**
 	 * @since 18.0.0
+	 * @deprecated 20.0.0
 	 */
 	public function __construct(IGroup $group, IUser $user) {
 		parent::__construct();
@@ -53,6 +58,7 @@ class BeforeUserRemovedEvent extends Event {
 	/**
 	 * @return IGroup
 	 * @since 18.0.0
+	 * @deprecated 20.0.0
 	 */
 	public function getGroup(): IGroup {
 		return $this->group;
@@ -61,6 +67,7 @@ class BeforeUserRemovedEvent extends Event {
 	/**
 	 * @return IUser
 	 * @since 18.0.0
+	 * @deprecated 20.0.0
 	 */
 	public function getUser(): IUser {
 		return $this->user;