diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml
index e996a54372..6a7d7a6b4d 100644
--- a/mobile/analysis_options.yaml
+++ b/mobile/analysis_options.yaml
@@ -69,14 +69,14 @@ custom_lint:
         - integration_test/test_utils/general_helper.dart
         - lib/main.dart
         - lib/routing/router.dart
-        - lib/utils/{db,image_url_builder,migration,renderlist_generator}.dart
+        - lib/utils/{db,migration,renderlist_generator}.dart
         - test/**.dart
         # refactor to make the providers and services testable
-        - lib/pages/common/{album_asset_selection,gallery_viewer}.page.dart
+        - lib/pages/common/album_asset_selection.page.dart
         - lib/providers/{archive,asset,authentication,db,favorite,partner,trash,user}.provider.dart
         - lib/providers/{album/album,album/shared_album,asset_viewer/asset_stack,asset_viewer/render_list,backup/backup,backup/manual_upload,search/all_motion_photos,search/recently_added_asset}.provider.dart
-        - lib/services/{asset,background,backup,hash,immich_logger,memory,partner,person,search,stack,sync,user}.service.dart
-        - lib/widgets/asset_grid/{asset_grid_data_structure,thumbnail_image}.dart
+        - lib/services/{asset,background,backup,immich_logger,sync}.service.dart
+        - lib/widgets/asset_grid/asset_grid_data_structure.dart
 
     - import_rule_openapi:
       message: openapi must only be used through ApiRepositories
@@ -90,18 +90,16 @@ custom_lint:
         - test/modules/utils/openapi_patching_test.dart # filename is self-explanatory...
         # refactor
         - lib/models/map/map_marker.model.dart
-        - lib/models/search/search_filter.model.dart
         - lib/models/server_info/server_{config,disk_info,features,version}.model.dart
         - lib/models/shared_link/shared_link.model.dart
-        - lib/pages/search/search_input.page.dart
         - lib/providers/asset_viewer/asset_people.provider.dart
         - lib/providers/authentication.provider.dart
         - lib/providers/image/immich_remote_{image,thumbnail}_provider.dart
         - lib/providers/map/map_state.provider.dart
-        - lib/providers/search/{people,search,search_filter}.provider.dart
+        - lib/providers/search/{search,search_filter}.provider.dart
         - lib/providers/websocket.provider.dart
         - lib/routing/auth_guard.dart
-        - lib/services/{api,asset,backup,memory,oauth,partner,person,search,shared_link,stack,trash,user}.service.dart
+        - lib/services/{api,asset,backup,memory,oauth,search,shared_link,stack,trash}.service.dart
         - lib/widgets/album/album_thumbnail_listtile.dart
         - lib/widgets/forms/login/login_form.dart
         - lib/widgets/search/search_filter/{camera_picker,location_picker,people_picker}.dart
diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart
new file mode 100644
index 0000000000..8b74b1a66f
--- /dev/null
+++ b/mobile/lib/constants/constants.dart
@@ -0,0 +1 @@
+const int noDbId = -9223372036854775808; // from Isar
diff --git a/mobile/lib/interfaces/asset.interface.dart b/mobile/lib/interfaces/asset.interface.dart
index 98f4c7687c..0d2dcfa1b5 100644
--- a/mobile/lib/interfaces/asset.interface.dart
+++ b/mobile/lib/interfaces/asset.interface.dart
@@ -1,5 +1,6 @@
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
+import 'package:immich_mobile/entities/device_asset.entity.dart';
 import 'package:immich_mobile/entities/user.entity.dart';
 
 abstract interface class IAssetRepository {
@@ -12,6 +13,7 @@ abstract interface class IAssetRepository {
     bool? remote,
     int limit = 100,
   });
+  Future<List<Asset>> updateAll(List<Asset> assets);
 
   Future<List<Asset>> getMatches({
     required List<Asset> assets,
@@ -19,4 +21,7 @@ abstract interface class IAssetRepository {
     bool? remote,
     int limit = 100,
   });
+
+  Future<List<DeviceAsset?>> getDeviceAssetsById(List<Object> ids);
+  Future<void> upsertDeviceAssets(List<DeviceAsset> deviceAssets);
 }
diff --git a/mobile/lib/interfaces/asset_api.interface.dart b/mobile/lib/interfaces/asset_api.interface.dart
index 201c85cea7..fe3320c9bb 100644
--- a/mobile/lib/interfaces/asset_api.interface.dart
+++ b/mobile/lib/interfaces/asset_api.interface.dart
@@ -13,4 +13,6 @@ abstract interface class IAssetApiRepository {
   });
 
   // Future<void> delete(String id);
+
+  Future<List<Asset>> search({List<String> personIds = const []});
 }
diff --git a/mobile/lib/interfaces/partner_api.interface.dart b/mobile/lib/interfaces/partner_api.interface.dart
new file mode 100644
index 0000000000..bca1baf66d
--- /dev/null
+++ b/mobile/lib/interfaces/partner_api.interface.dart
@@ -0,0 +1,13 @@
+import 'package:immich_mobile/entities/user.entity.dart';
+
+abstract interface class IPartnerApiRepository {
+  Future<List<User>> getAll(Direction direction);
+  Future<User> create(String id);
+  Future<User> update(String id, {required bool inTimeline});
+  Future<void> delete(String id);
+}
+
+enum Direction {
+  sharedWithMe,
+  sharedByMe,
+}
diff --git a/mobile/lib/interfaces/person_api.interface.dart b/mobile/lib/interfaces/person_api.interface.dart
new file mode 100644
index 0000000000..b2fa28df8c
--- /dev/null
+++ b/mobile/lib/interfaces/person_api.interface.dart
@@ -0,0 +1,22 @@
+abstract interface class IPersonApiRepository {
+  Future<List<Person>> getAll();
+  Future<Person> update(String id, {String? name});
+}
+
+class Person {
+  Person({
+    required this.id,
+    required this.isHidden,
+    required this.name,
+    required this.thumbnailPath,
+    this.birthDate,
+    this.updatedAt,
+  });
+
+  final String id;
+  final DateTime? birthDate;
+  final bool isHidden;
+  final String name;
+  final String thumbnailPath;
+  final DateTime? updatedAt;
+}
diff --git a/mobile/lib/interfaces/user.interface.dart b/mobile/lib/interfaces/user.interface.dart
index 4e847ea022..828a7b2398 100644
--- a/mobile/lib/interfaces/user.interface.dart
+++ b/mobile/lib/interfaces/user.interface.dart
@@ -3,4 +3,6 @@ import 'package:immich_mobile/entities/user.entity.dart';
 abstract interface class IUserRepository {
   Future<List<User>> getByIds(List<String> ids);
   Future<User?> get(String id);
+  Future<List<User>> getAll({bool self = true});
+  Future<User> update(User user);
 }
diff --git a/mobile/lib/interfaces/user_api.interface.dart b/mobile/lib/interfaces/user_api.interface.dart
new file mode 100644
index 0000000000..67ac3c0883
--- /dev/null
+++ b/mobile/lib/interfaces/user_api.interface.dart
@@ -0,0 +1,11 @@
+import 'dart:typed_data';
+
+import 'package:immich_mobile/entities/user.entity.dart';
+
+abstract interface class IUserApiRepository {
+  Future<List<User>> getAll();
+  Future<({String profileImagePath})> createProfileImage({
+    required String name,
+    required Uint8List data,
+  });
+}
diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart
index 6a7c612b15..297a819b6a 100644
--- a/mobile/lib/models/search/search_filter.model.dart
+++ b/mobile/lib/models/search/search_filter.model.dart
@@ -2,7 +2,7 @@
 import 'dart:convert';
 
 import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:openapi/api.dart';
+import 'package:immich_mobile/interfaces/person_api.interface.dart';
 
 class SearchLocationFilter {
   String? country;
@@ -235,7 +235,7 @@ class SearchDisplayFilters {
 class SearchFilter {
   String? context;
   String? filename;
-  Set<PersonResponseDto> people;
+  Set<Person> people;
   SearchLocationFilter location;
   SearchCameraFilter camera;
   SearchDateFilter date;
@@ -258,7 +258,7 @@ class SearchFilter {
   SearchFilter copyWith({
     String? context,
     String? filename,
-    Set<PersonResponseDto>? people,
+    Set<Person>? people,
     SearchLocationFilter? location,
     SearchCameraFilter? camera,
     SearchDateFilter? date,
diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart
index d8ea7cd89b..1434d1cca5 100644
--- a/mobile/lib/pages/common/gallery_viewer.page.dart
+++ b/mobile/lib/pages/common/gallery_viewer.page.dart
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/constants/constants.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/pages/common/video_viewer.page.dart';
@@ -30,7 +31,6 @@ import 'package:immich_mobile/widgets/photo_view/photo_view_gallery.dart';
 import 'package:immich_mobile/widgets/photo_view/src/photo_view_computed_scale.dart';
 import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart';
 import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attributes.dart';
-import 'package:isar/isar.dart';
 
 @RoutePage()
 // ignore: must_be_immutable
@@ -73,7 +73,7 @@ class GalleryViewerPage extends HookConsumerWidget {
         : <Asset>[];
     final stackElements = showStack ? [currentAsset, ...stack] : <Asset>[];
     // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
-    final isFromDto = currentAsset.id == Isar.autoIncrement;
+    final isFromDto = currentAsset.id == noDbId;
 
     Asset asset = stackIndex.value == -1
         ? currentAsset
diff --git a/mobile/lib/pages/search/search_input.page.dart b/mobile/lib/pages/search/search_input.page.dart
index acabc75aa4..2ca2a37918 100644
--- a/mobile/lib/pages/search/search_input.page.dart
+++ b/mobile/lib/pages/search/search_input.page.dart
@@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/extensions/theme_extensions.dart';
+import 'package:immich_mobile/interfaces/person_api.interface.dart';
 import 'package:immich_mobile/models/search/search_filter.model.dart';
 import 'package:immich_mobile/providers/search/paginated_search.provider.dart';
 import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
@@ -19,7 +20,6 @@ import 'package:immich_mobile/widgets/search/search_filter/media_type_picker.dar
 import 'package:immich_mobile/widgets/search/search_filter/people_picker.dart';
 import 'package:immich_mobile/widgets/search/search_filter/search_filter_chip.dart';
 import 'package:immich_mobile/widgets/search/search_filter/search_filter_utils.dart';
-import 'package:openapi/api.dart';
 
 @RoutePage()
 class SearchInputPage extends HookConsumerWidget {
@@ -110,7 +110,7 @@ class SearchInputPage extends HookConsumerWidget {
     }
 
     showPeoplePicker() {
-      handleOnSelect(Set<PersonResponseDto> value) {
+      handleOnSelect(Set<Person> value) {
         filter.value = filter.value.copyWith(
           people: value,
         );
diff --git a/mobile/lib/providers/activity_service.provider.g.dart b/mobile/lib/providers/activity_service.provider.g.dart
index 8e5ef43260..d42b2a39e4 100644
--- a/mobile/lib/providers/activity_service.provider.g.dart
+++ b/mobile/lib/providers/activity_service.provider.g.dart
@@ -6,7 +6,7 @@ part of 'activity_service.provider.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$activityServiceHash() => r'5dd4955d14f5bf01c00d7f8750d07e7ace7cc4b0';
+String _$activityServiceHash() => r'23a3ee7db71676d2719daa64217a683cc5c7eab0';
 
 /// See also [activityService].
 @ProviderFor(activityService)
diff --git a/mobile/lib/providers/activity_statistics.provider.g.dart b/mobile/lib/providers/activity_statistics.provider.g.dart
index 79856c525b..16a3c0e81b 100644
--- a/mobile/lib/providers/activity_statistics.provider.g.dart
+++ b/mobile/lib/providers/activity_statistics.provider.g.dart
@@ -7,7 +7,7 @@ part of 'activity_statistics.provider.dart';
 // **************************************************************************
 
 String _$activityStatisticsHash() =>
-    r'a5f7bbee1891c33b72919a34e632ca9ef9cd8dbf';
+    r'1f43f0bcb11c754ca3cb586a13570db25023b9a8';
 
 /// Copied from Dart SDK
 class _SystemHash {
diff --git a/mobile/lib/providers/album/suggested_shared_users.provider.dart b/mobile/lib/providers/album/suggested_shared_users.provider.dart
index 77518f47d0..fe8a1fccce 100644
--- a/mobile/lib/providers/album/suggested_shared_users.provider.dart
+++ b/mobile/lib/providers/album/suggested_shared_users.provider.dart
@@ -5,5 +5,5 @@ import 'package:immich_mobile/services/user.service.dart';
 final otherUsersProvider = FutureProvider.autoDispose<List<User>>((ref) {
   UserService userService = ref.watch(userServiceProvider);
 
-  return userService.getUsersInDb();
+  return userService.getUsers();
 });
diff --git a/mobile/lib/providers/map/map_state.provider.g.dart b/mobile/lib/providers/map/map_state.provider.g.dart
index eff7b4b68e..23a570d1c8 100644
--- a/mobile/lib/providers/map/map_state.provider.g.dart
+++ b/mobile/lib/providers/map/map_state.provider.g.dart
@@ -6,7 +6,7 @@ part of 'map_state.provider.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$mapStateNotifierHash() => r'31fafe17aa85c48379a22ed3db3cc94af59ce5b8';
+String _$mapStateNotifierHash() => r'22e4e571bd0730dbc34b109255a62b920e9c7d66';
 
 /// See also [MapStateNotifier].
 @ProviderFor(MapStateNotifier)
diff --git a/mobile/lib/providers/search/people.provider.dart b/mobile/lib/providers/search/people.provider.dart
index e2c243354b..7c956f0a37 100644
--- a/mobile/lib/providers/search/people.provider.dart
+++ b/mobile/lib/providers/search/people.provider.dart
@@ -1,14 +1,14 @@
+import 'package:immich_mobile/interfaces/person_api.interface.dart';
 import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
 import 'package:immich_mobile/services/person.service.dart';
 import 'package:immich_mobile/providers/app_settings.provider.dart';
 import 'package:immich_mobile/services/app_settings.service.dart';
-import 'package:openapi/api.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
 
 part 'people.provider.g.dart';
 
 @riverpod
-Future<List<PersonResponseDto>> getAllPeople(
+Future<List<Person>> getAllPeople(
   GetAllPeopleRef ref,
 ) async {
   final PersonService personService = ref.read(personServiceProvider);
diff --git a/mobile/lib/providers/search/people.provider.g.dart b/mobile/lib/providers/search/people.provider.g.dart
index db2edfb956..c5ff6287cd 100644
--- a/mobile/lib/providers/search/people.provider.g.dart
+++ b/mobile/lib/providers/search/people.provider.g.dart
@@ -6,12 +6,11 @@ part of 'people.provider.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$getAllPeopleHash() => r'4eff6666be5a74710d1e8587e01d8154310d85bd';
+String _$getAllPeopleHash() => r'3417b7e0c211382d4480a415e352139995d57b6d';
 
 /// See also [getAllPeople].
 @ProviderFor(getAllPeople)
-final getAllPeopleProvider =
-    AutoDisposeFutureProvider<List<PersonResponseDto>>.internal(
+final getAllPeopleProvider = AutoDisposeFutureProvider<List<Person>>.internal(
   getAllPeople,
   name: r'getAllPeopleProvider',
   debugGetCreateSourceHash:
@@ -20,7 +19,7 @@ final getAllPeopleProvider =
   allTransitiveDependencies: null,
 );
 
-typedef GetAllPeopleRef = AutoDisposeFutureProviderRef<List<PersonResponseDto>>;
+typedef GetAllPeopleRef = AutoDisposeFutureProviderRef<List<Person>>;
 String _$personAssetsHash() => r'3dfecb67a54d07e4208bcb9581b2625acd2e1832';
 
 /// Copied from Dart SDK
diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart
index c6012af371..087344302a 100644
--- a/mobile/lib/repositories/asset.repository.dart
+++ b/mobile/lib/repositories/asset.repository.dart
@@ -1,6 +1,11 @@
+import 'dart:io';
+
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
+import 'package:immich_mobile/entities/android_device_asset.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
+import 'package:immich_mobile/entities/device_asset.entity.dart';
+import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
 import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/interfaces/asset.interface.dart';
 import 'package:immich_mobile/providers/db.provider.dart';
@@ -69,6 +74,12 @@ class AssetRepository implements IAssetRepository {
     return query.limit(limit).findAll();
   }
 
+  @override
+  Future<List<Asset>> updateAll(List<Asset> assets) async {
+    await _db.writeTxn(() => _db.assets.putAll(assets));
+    return assets;
+  }
+
   @override
   Future<List<Asset>> getMatches({
     required List<Asset> assets,
@@ -86,6 +97,20 @@ class AssetRepository implements IAssetRepository {
     }
     return _getMatchesImpl(query, ownerId, assets, limit);
   }
+
+  @override
+  Future<List<DeviceAsset?>> getDeviceAssetsById(List<Object> ids) =>
+      Platform.isAndroid
+          ? _db.androidDeviceAssets.getAll(ids.cast())
+          : _db.iOSDeviceAssets.getAllById(ids.cast());
+
+  @override
+  Future<void> upsertDeviceAssets(List<DeviceAsset> deviceAssets) =>
+      _db.writeTxn(
+        () => Platform.isAndroid
+            ? _db.androidDeviceAssets.putAll(deviceAssets.cast())
+            : _db.iOSDeviceAssets.putAll(deviceAssets.cast()),
+      );
 }
 
 Future<List<Asset>> _getMatchesImpl(
diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart
index 3ad0e1cba0..eb796f6c6b 100644
--- a/mobile/lib/repositories/asset_api.repository.dart
+++ b/mobile/lib/repositories/asset_api.repository.dart
@@ -6,14 +6,18 @@ import 'package:immich_mobile/repositories/base_api.repository.dart';
 import 'package:openapi/api.dart';
 
 final assetApiRepositoryProvider = Provider(
-  (ref) => AssetApiRepository(ref.watch(apiServiceProvider).assetsApi),
+  (ref) => AssetApiRepository(
+    ref.watch(apiServiceProvider).assetsApi,
+    ref.watch(apiServiceProvider).searchApi,
+  ),
 );
 
 class AssetApiRepository extends BaseApiRepository
     implements IAssetApiRepository {
   final AssetsApi _api;
+  final SearchApi _searchApi;
 
-  AssetApiRepository(this._api);
+  AssetApiRepository(this._api, this._searchApi);
 
   @override
   Future<Asset> update(String id, {String? description}) async {
@@ -22,4 +26,27 @@ class AssetApiRepository extends BaseApiRepository
     );
     return Asset.remote(response);
   }
+
+  @override
+  Future<List<Asset>> search({List<String> personIds = const []}) async {
+    // TODO this always fetches all assets, change API and usage to actually do pagination
+    final List<Asset> result = [];
+    bool hasNext = true;
+    int currentPage = 1;
+    while (hasNext) {
+      final response = await checkNull(
+        _searchApi.searchMetadata(
+          MetadataSearchDto(
+            personIds: personIds,
+            page: currentPage,
+            size: 1000,
+          ),
+        ),
+      );
+      result.addAll(response.assets.items.map(Asset.remote));
+      hasNext = response.assets.nextPage != null;
+      currentPage++;
+    }
+    return result;
+  }
 }
diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart
new file mode 100644
index 0000000000..3419a2bc77
--- /dev/null
+++ b/mobile/lib/repositories/partner_api.repository.dart
@@ -0,0 +1,51 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/interfaces/partner_api.interface.dart';
+import 'package:immich_mobile/providers/api.provider.dart';
+import 'package:immich_mobile/repositories/base_api.repository.dart';
+import 'package:openapi/api.dart';
+
+final partnerApiRepositoryProvider = Provider(
+  (ref) => PartnerApiRepository(
+    ref.watch(apiServiceProvider).partnersApi,
+  ),
+);
+
+class PartnerApiRepository extends BaseApiRepository
+    implements IPartnerApiRepository {
+  final PartnersApi _api;
+
+  PartnerApiRepository(this._api);
+
+  @override
+  Future<List<User>> getAll(Direction direction) async {
+    final response = await checkNull(
+      _api.getPartners(
+        direction == Direction.sharedByMe
+            ? PartnerDirection.by
+            : PartnerDirection.with_,
+      ),
+    );
+    return response.map(User.fromPartnerDto).toList();
+  }
+
+  @override
+  Future<User> create(String id) async {
+    final dto = await checkNull(_api.createPartner(id));
+    return User.fromPartnerDto(dto);
+  }
+
+  @override
+  Future<void> delete(String id) => checkNull(_api.removePartner(id));
+
+  @override
+  Future<User> update(String id, {required bool inTimeline}) async {
+    final dto = await checkNull(
+      _api.updatePartner(
+        id,
+        UpdatePartnerDto(inTimeline: inTimeline),
+      ),
+    );
+    return User.fromPartnerDto(dto);
+  }
+}
diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart
new file mode 100644
index 0000000000..8071c33dc2
--- /dev/null
+++ b/mobile/lib/repositories/person_api.repository.dart
@@ -0,0 +1,38 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/interfaces/person_api.interface.dart';
+import 'package:immich_mobile/providers/api.provider.dart';
+import 'package:immich_mobile/repositories/base_api.repository.dart';
+import 'package:openapi/api.dart';
+
+final personApiRepositoryProvider = Provider(
+  (ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi),
+);
+
+class PersonApiRepository extends BaseApiRepository
+    implements IPersonApiRepository {
+  final PeopleApi _api;
+
+  PersonApiRepository(this._api);
+
+  @override
+  Future<List<Person>> getAll() async {
+    final dto = await checkNull(_api.getAllPeople());
+    return dto.people.map(_toPerson).toList();
+  }
+
+  @override
+  Future<Person> update(String id, {String? name}) async {
+    final dto = await checkNull(
+      _api.updatePerson(id, PersonUpdateDto(name: name)),
+    );
+    return _toPerson(dto);
+  }
+
+  static Person _toPerson(PersonResponseDto dto) => Person(
+        birthDate: dto.birthDate,
+        id: dto.id,
+        isHidden: dto.isHidden,
+        name: dto.name,
+        thumbnailPath: dto.thumbnailPath,
+      );
+}
diff --git a/mobile/lib/repositories/user.repository.dart b/mobile/lib/repositories/user.repository.dart
index b05af9a57f..796b1f421b 100644
--- a/mobile/lib/repositories/user.repository.dart
+++ b/mobile/lib/repositories/user.repository.dart
@@ -1,4 +1,5 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/entities/store.entity.dart';
 import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/interfaces/user.interface.dart';
 import 'package:immich_mobile/providers/db.provider.dart';
@@ -20,4 +21,19 @@ class UserRepository implements IUserRepository {
 
   @override
   Future<User?> get(String id) => _db.users.getById(id);
+
+  @override
+  Future<List<User>> getAll({bool self = true}) {
+    if (self) {
+      return _db.users.where().findAll();
+    }
+    final int userId = Store.get(StoreKey.currentUser).isarId;
+    return _db.users.where().isarIdNotEqualTo(userId).findAll();
+  }
+
+  @override
+  Future<User> update(User user) async {
+    await _db.writeTxn(() => _db.users.put(user));
+    return user;
+  }
 }
diff --git a/mobile/lib/repositories/user_api.repository.dart b/mobile/lib/repositories/user_api.repository.dart
new file mode 100644
index 0000000000..ffc50ae4c3
--- /dev/null
+++ b/mobile/lib/repositories/user_api.repository.dart
@@ -0,0 +1,41 @@
+import 'dart:typed_data';
+
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:http/http.dart';
+import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/interfaces/user_api.interface.dart';
+import 'package:immich_mobile/providers/api.provider.dart';
+import 'package:immich_mobile/repositories/base_api.repository.dart';
+import 'package:openapi/api.dart';
+
+final userApiRepositoryProvider = Provider(
+  (ref) => UserApiRepository(
+    ref.watch(apiServiceProvider).usersApi,
+  ),
+);
+
+class UserApiRepository extends BaseApiRepository
+    implements IUserApiRepository {
+  final UsersApi _api;
+
+  UserApiRepository(this._api);
+
+  @override
+  Future<List<User>> getAll() async {
+    final dto = await checkNull(_api.searchUsers());
+    return dto.map(User.fromSimpleUserDto).toList();
+  }
+
+  @override
+  Future<({String profileImagePath})> createProfileImage({
+    required String name,
+    required Uint8List data,
+  }) async {
+    final response = await checkNull(
+      _api.createProfileImage(
+        MultipartFile.fromBytes('file', data, filename: name),
+      ),
+    );
+    return (profileImagePath: response.profileImagePath);
+  }
+}
diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart
index 09030a621b..d06bc86d48 100644
--- a/mobile/lib/services/background.service.dart
+++ b/mobile/lib/services/background.service.dart
@@ -18,7 +18,9 @@ import 'package:immich_mobile/repositories/asset.repository.dart';
 import 'package:immich_mobile/repositories/backup.repository.dart';
 import 'package:immich_mobile/repositories/album_media.repository.dart';
 import 'package:immich_mobile/repositories/file_media.repository.dart';
+import 'package:immich_mobile/repositories/partner_api.repository.dart';
 import 'package:immich_mobile/repositories/user.repository.dart';
+import 'package:immich_mobile/repositories/user_api.repository.dart';
 import 'package:immich_mobile/services/album.service.dart';
 import 'package:immich_mobile/services/entity.service.dart';
 import 'package:immich_mobile/services/hash.service.dart';
@@ -30,7 +32,6 @@ import 'package:immich_mobile/services/backup.service.dart';
 import 'package:immich_mobile/services/app_settings.service.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
 import 'package:immich_mobile/services/api.service.dart';
-import 'package:immich_mobile/services/partner.service.dart';
 import 'package:immich_mobile/services/sync.service.dart';
 import 'package:immich_mobile/services/user.service.dart';
 import 'package:immich_mobile/utils/backup_progress.dart';
@@ -362,16 +363,20 @@ class BackgroundService {
     apiService.setAccessToken(Store.get(StoreKey.accessToken));
     AppSettingsService settingService = AppSettingsService();
     AppSettingsService settingsService = AppSettingsService();
-    PartnerService partnerService = PartnerService(apiService, db);
     AlbumRepository albumRepository = AlbumRepository(db);
     AssetRepository assetRepository = AssetRepository(db);
     BackupRepository backupAlbumRepository = BackupRepository(db);
     AlbumMediaRepository albumMediaRepository = AlbumMediaRepository();
     FileMediaRepository fileMediaRepository = FileMediaRepository();
     UserRepository userRepository = UserRepository(db);
+    UserApiRepository userApiRepository =
+        UserApiRepository(apiService.usersApi);
     AlbumApiRepository albumApiRepository =
         AlbumApiRepository(apiService.albumsApi);
-    HashService hashService = HashService(db, this, albumMediaRepository);
+    PartnerApiRepository partnerApiRepository =
+        PartnerApiRepository(apiService.partnersApi);
+    HashService hashService =
+        HashService(assetRepository, this, albumMediaRepository);
     EntityService entityService =
         EntityService(assetRepository, userRepository);
     SyncService syncSerive = SyncService(
@@ -381,8 +386,12 @@ class BackgroundService {
       albumMediaRepository,
       albumApiRepository,
     );
-    UserService userService =
-        UserService(apiService, db, syncSerive, partnerService);
+    UserService userService = UserService(
+      partnerApiRepository,
+      userApiRepository,
+      userRepository,
+      syncSerive,
+    );
     AlbumService albumService = AlbumService(
       userService,
       syncSerive,
diff --git a/mobile/lib/services/hash.service.dart b/mobile/lib/services/hash.service.dart
index 94d680972f..3827e421e6 100644
--- a/mobile/lib/services/hash.service.dart
+++ b/mobile/lib/services/hash.service.dart
@@ -4,20 +4,24 @@ import 'package:flutter/foundation.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/interfaces/album_media.interface.dart';
+import 'package:immich_mobile/interfaces/asset.interface.dart';
 import 'package:immich_mobile/repositories/album_media.repository.dart';
+import 'package:immich_mobile/repositories/asset.repository.dart';
 import 'package:immich_mobile/services/background.service.dart';
 import 'package:immich_mobile/entities/android_device_asset.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/device_asset.entity.dart';
 import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
-import 'package:immich_mobile/providers/db.provider.dart';
 import 'package:immich_mobile/extensions/string_extensions.dart';
-import 'package:isar/isar.dart';
 import 'package:logging/logging.dart';
 
 class HashService {
-  HashService(this._db, this._backgroundService, this._albumMediaRepository);
-  final Isar _db;
+  HashService(
+    this._assetRepository,
+    this._backgroundService,
+    this._albumMediaRepository,
+  );
+  final IAssetRepository _assetRepository;
   final BackgroundService _backgroundService;
   final IAlbumMediaRepository _albumMediaRepository;
   final _log = Logger('HashService');
@@ -55,7 +59,8 @@ class HashService {
     final ids = assets
         .map(Platform.isAndroid ? (a) => a.localId!.toInt() : (a) => a.localId!)
         .toList();
-    final List<DeviceAsset?> hashes = await _lookupHashes(ids);
+    final List<DeviceAsset?> hashes =
+        await _assetRepository.getDeviceAssetsById(ids);
     final List<DeviceAsset> toAdd = [];
     final List<String> toHash = [];
 
@@ -106,12 +111,6 @@ class HashService {
     return _getHashedAssets(assets, hashes);
   }
 
-  /// Lookup hashes of assets by their local ID
-  Future<List<DeviceAsset?>> _lookupHashes(List<Object> ids) =>
-      Platform.isAndroid
-          ? _db.androidDeviceAssets.getAll(ids.cast())
-          : _db.iOSDeviceAssets.getAllById(ids.cast());
-
   /// Processes a batch of files and saves any successfully hashed
   /// values to the DB table.
   Future<void> _processBatch(
@@ -131,11 +130,7 @@ class HashService {
     final validHashes = anyNull
         ? toAdd.where((e) => e.hash.length == 20).toList(growable: false)
         : toAdd;
-    await _db.writeTxn(
-      () => Platform.isAndroid
-          ? _db.androidDeviceAssets.putAll(validHashes.cast())
-          : _db.iOSDeviceAssets.putAll(validHashes.cast()),
-    );
+    await _assetRepository.upsertDeviceAssets(validHashes);
     _log.fine("Hashed ${validHashes.length}/${toHash.length} assets");
   }
 
@@ -168,7 +163,7 @@ class HashService {
 
 final hashServiceProvider = Provider(
   (ref) => HashService(
-    ref.watch(dbProvider),
+    ref.watch(assetRepositoryProvider),
     ref.watch(backgroundServiceProvider),
     ref.watch(albumMediaRepositoryProvider),
   ),
diff --git a/mobile/lib/services/memory.service.dart b/mobile/lib/services/memory.service.dart
index ea07f7c019..b95899df67 100644
--- a/mobile/lib/services/memory.service.dart
+++ b/mobile/lib/services/memory.service.dart
@@ -1,18 +1,17 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
+import 'package:immich_mobile/interfaces/asset.interface.dart';
 import 'package:immich_mobile/models/memories/memory.model.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
-import 'package:immich_mobile/providers/db.provider.dart';
+import 'package:immich_mobile/repositories/asset.repository.dart';
 import 'package:immich_mobile/services/api.service.dart';
-import 'package:isar/isar.dart';
 import 'package:logging/logging.dart';
 import 'package:openapi/api.dart';
 
 final memoryServiceProvider = StateProvider<MemoryService>((ref) {
   return MemoryService(
     ref.watch(apiServiceProvider),
-    ref.watch(dbProvider),
+    ref.watch(assetRepositoryProvider),
   );
 });
 
@@ -20,9 +19,9 @@ class MemoryService {
   final log = Logger("MemoryService");
 
   final ApiService _apiService;
-  final Isar _db;
+  final IAssetRepository _assetRepository;
 
-  MemoryService(this._apiService, this._db);
+  MemoryService(this._apiService, this._assetRepository);
 
   Future<List<Memory>?> getMemoryLane() async {
     try {
@@ -39,7 +38,7 @@ class MemoryService {
       List<Memory> memories = [];
       for (final MemoryLaneResponseDto(:yearsAgo, :assets) in data) {
         final dbAssets =
-            await _db.assets.getAllByRemoteId(assets.map((e) => e.id));
+            await _assetRepository.getAllByRemoteId(assets.map((e) => e.id));
         if (dbAssets.isNotEmpty) {
           final String title = yearsAgo <= 1
               ? 'memories_year_ago'.tr()
diff --git a/mobile/lib/services/partner.service.dart b/mobile/lib/services/partner.service.dart
index 8cd2fe424f..67d7f4e1d1 100644
--- a/mobile/lib/services/partner.service.dart
+++ b/mobile/lib/services/partner.service.dart
@@ -1,43 +1,33 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/entities/user.entity.dart';
-import 'package:immich_mobile/providers/api.provider.dart';
-import 'package:immich_mobile/providers/db.provider.dart';
-import 'package:immich_mobile/services/api.service.dart';
-import 'package:isar/isar.dart';
+import 'package:immich_mobile/interfaces/partner_api.interface.dart';
+import 'package:immich_mobile/interfaces/user.interface.dart';
+import 'package:immich_mobile/repositories/partner_api.repository.dart';
+import 'package:immich_mobile/repositories/user.repository.dart';
 import 'package:logging/logging.dart';
-import 'package:openapi/api.dart';
 
 final partnerServiceProvider = Provider(
   (ref) => PartnerService(
-    ref.watch(apiServiceProvider),
-    ref.watch(dbProvider),
+    ref.watch(partnerApiRepositoryProvider),
+    ref.watch(userRepositoryProvider),
   ),
 );
 
 class PartnerService {
-  final ApiService _apiService;
-  final Isar _db;
+  final IPartnerApiRepository _partnerApiRepository;
+  final IUserRepository _userRepository;
   final Logger _log = Logger("PartnerService");
 
-  PartnerService(this._apiService, this._db);
-
-  Future<List<User>?> getPartners(PartnerDirection direction) async {
-    try {
-      final userDtos = await _apiService.partnersApi.getPartners(direction);
-      if (userDtos != null) {
-        return userDtos.map((u) => User.fromPartnerDto(u)).toList();
-      }
-    } catch (e) {
-      _log.warning("Failed to get partners for direction $direction", e);
-    }
-    return null;
-  }
+  PartnerService(
+    this._partnerApiRepository,
+    this._userRepository,
+  );
 
   Future<bool> removePartner(User partner) async {
     try {
-      await _apiService.partnersApi.removePartner(partner.id);
+      await _partnerApiRepository.delete(partner.id);
       partner.isPartnerSharedBy = false;
-      await _db.writeTxn(() => _db.users.put(partner));
+      await _userRepository.update(partner);
     } catch (e) {
       _log.warning("Failed to remove partner ${partner.id}", e);
       return false;
@@ -47,12 +37,10 @@ class PartnerService {
 
   Future<bool> addPartner(User partner) async {
     try {
-      final dto = await _apiService.partnersApi.createPartner(partner.id);
-      if (dto != null) {
-        partner.isPartnerSharedBy = true;
-        await _db.writeTxn(() => _db.users.put(partner));
-        return true;
-      }
+      await _partnerApiRepository.create(partner.id);
+      partner.isPartnerSharedBy = true;
+      await _userRepository.update(partner);
+      return true;
     } catch (e) {
       _log.warning("Failed to add partner ${partner.id}", e);
     }
@@ -61,13 +49,13 @@ class PartnerService {
 
   Future<bool> updatePartner(User partner, {required bool inTimeline}) async {
     try {
-      final dto = await _apiService.partnersApi
-          .updatePartner(partner.id, UpdatePartnerDto(inTimeline: inTimeline));
-      if (dto != null) {
-        partner.inTimeline = dto.inTimeline ?? partner.inTimeline;
-        await _db.writeTxn(() => _db.users.put(partner));
-        return true;
-      }
+      final dto = await _partnerApiRepository.update(
+        partner.id,
+        inTimeline: inTimeline,
+      );
+      partner.inTimeline = dto.inTimeline;
+      await _userRepository.update(partner);
+      return true;
     } catch (e) {
       _log.warning("Failed to update partner ${partner.id}", e);
     }
diff --git a/mobile/lib/services/person.service.dart b/mobile/lib/services/person.service.dart
index ddb61f5e48..5b325acdc5 100644
--- a/mobile/lib/services/person.service.dart
+++ b/mobile/lib/services/person.service.dart
@@ -1,29 +1,37 @@
 import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/providers/api.provider.dart';
-import 'package:immich_mobile/providers/db.provider.dart';
-import 'package:immich_mobile/services/api.service.dart';
-import 'package:isar/isar.dart';
+import 'package:immich_mobile/interfaces/asset.interface.dart';
+import 'package:immich_mobile/interfaces/asset_api.interface.dart';
+import 'package:immich_mobile/interfaces/person_api.interface.dart';
+import 'package:immich_mobile/repositories/asset.repository.dart';
+import 'package:immich_mobile/repositories/asset_api.repository.dart';
+import 'package:immich_mobile/repositories/person_api.repository.dart';
 import 'package:logging/logging.dart';
-import 'package:openapi/api.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
 
 part 'person.service.g.dart';
 
 @riverpod
-PersonService personService(PersonServiceRef ref) =>
-    PersonService(ref.read(apiServiceProvider), ref.read(dbProvider));
+PersonService personService(PersonServiceRef ref) => PersonService(
+      ref.watch(personApiRepositoryProvider),
+      ref.watch(assetApiRepositoryProvider),
+      ref.read(assetRepositoryProvider),
+    );
 
 class PersonService {
   final Logger _log = Logger("PersonService");
-  final ApiService _apiService;
-  final Isar _db;
+  final IPersonApiRepository _personApiRepository;
+  final IAssetApiRepository _assetApiRepository;
+  final IAssetRepository _assetRepository;
 
-  PersonService(this._apiService, this._db);
+  PersonService(
+    this._personApiRepository,
+    this._assetApiRepository,
+    this._assetRepository,
+  );
 
-  Future<List<PersonResponseDto>> getAllPeople() async {
+  Future<List<Person>> getAllPeople() async {
     try {
-      final peopleResponseDto = await _apiService.peopleApi.getAllPeople();
-      return peopleResponseDto?.people ?? [];
+      return await _personApiRepository.getAll();
     } catch (error, stack) {
       _log.severe("Error while fetching curated people", error, stack);
       return [];
@@ -31,50 +39,19 @@ class PersonService {
   }
 
   Future<List<Asset>> getPersonAssets(String id) async {
-    List<Asset> result = [];
-    var hasNext = true;
-    var currentPage = 1;
-
     try {
-      while (hasNext) {
-        final response = await _apiService.searchApi.searchMetadata(
-          MetadataSearchDto(
-            personIds: [id],
-            page: currentPage,
-            size: 1000,
-          ),
-        );
-
-        if (response == null) {
-          break;
-        }
-
-        if (response.assets.nextPage == null) {
-          hasNext = false;
-        }
-
-        final assets = response.assets.items;
-        final mapAssets =
-            await _db.assets.getAllByRemoteId(assets.map((e) => e.id));
-        result.addAll(mapAssets);
-
-        currentPage++;
-      }
+      final assets = await _assetApiRepository.search(personIds: [id]);
+      return await _assetRepository
+          .getAllByRemoteId(assets.map((a) => a.remoteId!));
     } catch (error, stack) {
       _log.severe("Error while fetching person assets", error, stack);
     }
-
-    return result;
+    return [];
   }
 
-  Future<PersonResponseDto?> updateName(String id, String name) async {
+  Future<Person?> updateName(String id, String name) async {
     try {
-      return await _apiService.peopleApi.updatePerson(
-        id,
-        PersonUpdateDto(
-          name: name,
-        ),
-      );
+      return await _personApiRepository.update(id, name: name);
     } catch (error, stack) {
       _log.severe("Error while updating person name", error, stack);
     }
diff --git a/mobile/lib/services/person.service.g.dart b/mobile/lib/services/person.service.g.dart
index 01a5ed8f30..9a24069fbf 100644
--- a/mobile/lib/services/person.service.g.dart
+++ b/mobile/lib/services/person.service.g.dart
@@ -6,7 +6,7 @@ part of 'person.service.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$personServiceHash() => r'54e6df4b8eea744f6de009f8315c9fe6230f6798';
+String _$personServiceHash() => r'32f28cb5a3de0553c17447e33a0efde7409a43ed';
 
 /// See also [personService].
 @ProviderFor(personService)
diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart
index cf3905e5ca..336fe45010 100644
--- a/mobile/lib/services/search.service.dart
+++ b/mobile/lib/services/search.service.dart
@@ -1,27 +1,27 @@
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/interfaces/asset.interface.dart';
 import 'package:immich_mobile/models/search/search_filter.model.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
-import 'package:immich_mobile/providers/db.provider.dart';
+import 'package:immich_mobile/repositories/asset.repository.dart';
 import 'package:immich_mobile/services/api.service.dart';
-import 'package:isar/isar.dart';
 import 'package:logging/logging.dart';
 import 'package:openapi/api.dart';
 
 final searchServiceProvider = Provider(
   (ref) => SearchService(
     ref.watch(apiServiceProvider),
-    ref.watch(dbProvider),
+    ref.watch(assetRepositoryProvider),
   ),
 );
 
 class SearchService {
   final ApiService _apiService;
-  final Isar _db;
+  final IAssetRepository _assetRepository;
 
   final _log = Logger("SearchService");
-  SearchService(this._apiService, this._db);
+  SearchService(this._apiService, this._assetRepository);
 
   Future<List<String>?> getSearchSuggestions(
     SearchSuggestionType type, {
@@ -103,7 +103,7 @@ class SearchService {
         return null;
       }
 
-      return _db.assets
+      return _assetRepository
           .getAllByRemoteId(response.assets.items.map((e) => e.id));
     } catch (error, stackTrace) {
       _log.severe("Failed to search for assets", error, stackTrace);
diff --git a/mobile/lib/services/stack.service.dart b/mobile/lib/services/stack.service.dart
index 75074101c2..8bff21fef6 100644
--- a/mobile/lib/services/stack.service.dart
+++ b/mobile/lib/services/stack.service.dart
@@ -1,17 +1,17 @@
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
+import 'package:immich_mobile/interfaces/asset.interface.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
-import 'package:immich_mobile/providers/db.provider.dart';
+import 'package:immich_mobile/repositories/asset.repository.dart';
 import 'package:immich_mobile/services/api.service.dart';
-import 'package:isar/isar.dart';
 import 'package:openapi/api.dart';
 
 class StackService {
-  StackService(this._api, this._db);
+  StackService(this._api, this._assetRepository);
 
   final ApiService _api;
-  final Isar _db;
+  final IAssetRepository _assetRepository;
 
   Future<StackResponseDto?> getStack(String stackId) async {
     try {
@@ -61,10 +61,7 @@ class StackService {
 
         removeAssets.add(asset);
       }
-
-      _db.writeTxn(() async {
-        await _db.assets.putAll(removeAssets);
-      });
+      await _assetRepository.updateAll(removeAssets);
     } catch (error) {
       debugPrint("Error while deleting stack: $error");
     }
@@ -74,6 +71,6 @@ class StackService {
 final stackServiceProvider = Provider(
   (ref) => StackService(
     ref.watch(apiServiceProvider),
-    ref.watch(dbProvider),
+    ref.watch(assetRepositoryProvider),
   ),
 );
diff --git a/mobile/lib/services/user.service.dart b/mobile/lib/services/user.service.dart
index 9631141c41..4c2b3cbbd0 100644
--- a/mobile/lib/services/user.service.dart
+++ b/mobile/lib/services/user.service.dart
@@ -1,68 +1,48 @@
 import 'package:collection/collection.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:http/http.dart';
 import 'package:image_picker/image_picker.dart';
-import 'package:immich_mobile/services/partner.service.dart';
-import 'package:immich_mobile/entities/store.entity.dart';
+import 'package:immich_mobile/interfaces/partner_api.interface.dart';
+import 'package:immich_mobile/interfaces/user.interface.dart';
+import 'package:immich_mobile/interfaces/user_api.interface.dart';
+import 'package:immich_mobile/repositories/partner_api.repository.dart';
+import 'package:immich_mobile/repositories/user.repository.dart';
+import 'package:immich_mobile/repositories/user_api.repository.dart';
 import 'package:immich_mobile/entities/user.entity.dart';
-import 'package:immich_mobile/providers/api.provider.dart';
-import 'package:immich_mobile/providers/db.provider.dart';
-import 'package:immich_mobile/services/api.service.dart';
 import 'package:immich_mobile/services/sync.service.dart';
 import 'package:immich_mobile/utils/diff.dart';
-import 'package:isar/isar.dart';
 import 'package:logging/logging.dart';
-import 'package:openapi/api.dart';
 
 final userServiceProvider = Provider(
   (ref) => UserService(
-    ref.watch(apiServiceProvider),
-    ref.watch(dbProvider),
+    ref.watch(partnerApiRepositoryProvider),
+    ref.watch(userApiRepositoryProvider),
+    ref.watch(userRepositoryProvider),
     ref.watch(syncServiceProvider),
-    ref.watch(partnerServiceProvider),
   ),
 );
 
 class UserService {
-  final ApiService _apiService;
-  final Isar _db;
+  final IPartnerApiRepository _partnerApiRepository;
+  final IUserApiRepository _userApiRepository;
+  final IUserRepository _userRepository;
   final SyncService _syncService;
-  final PartnerService _partnerService;
   final Logger _log = Logger("UserService");
 
   UserService(
-    this._apiService,
-    this._db,
+    this._partnerApiRepository,
+    this._userApiRepository,
+    this._userRepository,
     this._syncService,
-    this._partnerService,
   );
 
-  Future<List<User>?> _getAllUsers() async {
-    try {
-      final dto = await _apiService.usersApi.searchUsers();
-      return dto?.map(User.fromSimpleUserDto).toList();
-    } catch (e) {
-      _log.warning("Failed get all users", e);
-      return null;
-    }
-  }
+  Future<List<User>> getUsers({bool self = false}) =>
+      _userRepository.getAll(self: self);
 
-  Future<List<User>> getUsersInDb({bool self = false}) async {
-    if (self) {
-      return _db.users.where().findAll();
-    }
-    final int userId = Store.get(StoreKey.currentUser).isarId;
-    return _db.users.where().isarIdNotEqualTo(userId).findAll();
-  }
-
-  Future<CreateProfileImageResponseDto?> uploadProfileImage(XFile image) async {
+  Future<({String profileImagePath})?> uploadProfileImage(XFile image) async {
     try {
-      return await _apiService.usersApi.createProfileImage(
-        MultipartFile.fromBytes(
-          'file',
-          await image.readAsBytes(),
-          filename: image.name,
-        ),
+      return await _userApiRepository.createProfileImage(
+        name: image.name,
+        data: await image.readAsBytes(),
       );
     } catch (e) {
       _log.warning("Failed to upload profile image", e);
@@ -71,13 +51,19 @@ class UserService {
   }
 
   Future<List<User>?> getUsersFromServer() async {
-    final List<User>? users = await _getAllUsers();
-    final List<User>? sharedBy =
-        await _partnerService.getPartners(PartnerDirection.by);
-    final List<User>? sharedWith =
-        await _partnerService.getPartners(PartnerDirection.with_);
+    List<User>? users;
+    try {
+      users = await _userApiRepository.getAll();
+    } catch (e) {
+      _log.warning("Failed to fetch users", e);
+      users = null;
+    }
+    final List<User> sharedBy =
+        await _partnerApiRepository.getAll(Direction.sharedByMe);
+    final List<User> sharedWith =
+        await _partnerApiRepository.getAll(Direction.sharedWithMe);
 
-    if (users == null || sharedBy == null || sharedWith == null) {
+    if (users == null) {
       _log.warning("Failed to refresh users");
       return null;
     }
diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart
index e7a1b9e39e..9fc7b13eed 100644
--- a/mobile/lib/utils/image_url_builder.dart
+++ b/mobile/lib/utils/image_url_builder.dart
@@ -1,7 +1,7 @@
+import 'package:immich_mobile/constants/constants.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:isar/isar.dart';
 import 'package:openapi/api.dart';
 
 String getThumbnailUrl(
@@ -61,7 +61,7 @@ String getOriginalUrlForRemoteId(final String id) {
 
 String getImageCacheKey(final Asset asset) {
   // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
-  final isFromDto = asset.id == Isar.autoIncrement;
+  final isFromDto = asset.id == noDbId;
   return '${isFromDto ? asset.remoteId : asset.id}_fullStage';
 }
 
diff --git a/mobile/lib/widgets/asset_grid/thumbnail_image.dart b/mobile/lib/widgets/asset_grid/thumbnail_image.dart
index 8e818f64fb..6cadef763d 100644
--- a/mobile/lib/widgets/asset_grid/thumbnail_image.dart
+++ b/mobile/lib/widgets/asset_grid/thumbnail_image.dart
@@ -1,11 +1,11 @@
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/constants/constants.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/theme_extensions.dart';
 import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
 import 'package:immich_mobile/utils/storage_indicator.dart';
-import 'package:isar/isar.dart';
 
 class ThumbnailImage extends ConsumerWidget {
   /// The asset to show the thumbnail image for
@@ -46,7 +46,7 @@ class ThumbnailImage extends ConsumerWidget {
         ? context.primaryColor.darken(amount: 0.6)
         : context.primaryColor.lighten(amount: 0.8);
     // Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
-    final isFromDto = asset.id == Isar.autoIncrement;
+    final isFromDto = asset.id == noDbId;
 
     Widget buildSelectionIcon(Asset asset) {
       if (isSelected) {
diff --git a/mobile/lib/widgets/search/search_filter/people_picker.dart b/mobile/lib/widgets/search/search_filter/people_picker.dart
index d79ae5bd95..dfc435c807 100644
--- a/mobile/lib/widgets/search/search_filter/people_picker.dart
+++ b/mobile/lib/widgets/search/search_filter/people_picker.dart
@@ -3,23 +3,23 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/interfaces/person_api.interface.dart';
 import 'package:immich_mobile/providers/search/people.provider.dart';
 import 'package:immich_mobile/services/api.service.dart';
 import 'package:immich_mobile/utils/image_url_builder.dart';
-import 'package:openapi/api.dart';
 
 class PeoplePicker extends HookConsumerWidget {
   const PeoplePicker({super.key, required this.onSelect, this.filter});
 
-  final Function(Set<PersonResponseDto>) onSelect;
-  final Set<PersonResponseDto>? filter;
+  final Function(Set<Person>) onSelect;
+  final Set<Person>? filter;
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
     var imageSize = 45.0;
     final people = ref.watch(getAllPeopleProvider);
     final headers = ApiService.getRequestHeaders();
-    final selectedPeople = useState<Set<PersonResponseDto>>(filter ?? {});
+    final selectedPeople = useState<Set<Person>>(filter ?? {});
 
     return people.widgetWhen(
       onData: (people) {