From d1c8fe5303da59ad890bc3e7eb2d9823f3624bcb Mon Sep 17 00:00:00 2001
From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
Date: Wed, 12 Mar 2025 19:26:56 +0530
Subject: [PATCH] refactor: user entity (#16655)

* refactor: user entity

* fix: add users to album & user profile url

* chore: rebase fixes

* generate files

* fix(mobile): timeline not reset on login

* fix: test stub

* refactor: rename user model (#16813)

* refactor: rename user model

* simplify import

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>

* chore: generate files

* fix: use getAllAccessible instead of getAll

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
---
 mobile/analysis_options.yaml                  |   3 +-
 .../lib/domain/interfaces/user.interface.dart |  24 ++
 mobile/lib/domain/models/store.model.dart     |   4 +-
 mobile/lib/domain/models/user.model.dart      | 157 +++++++++
 mobile/lib/domain/services/store.service.dart |   4 +-
 mobile/lib/entities/album.entity.dart         |   2 +-
 mobile/lib/entities/user.entity.dart          | 181 ----------
 .../lib/extensions/collection_extensions.dart |   6 +-
 .../infrastructure/entities/user.entity.dart  |  73 +++++
 .../entities/user.entity.g.dart               | 309 ++++--------------
 .../repositories/store.repository.dart        |  10 +-
 .../repositories/user.repository.dart         |  80 +++++
 .../infrastructure/utils/user.converter.dart  |  66 ++++
 mobile/lib/interfaces/album.interface.dart    |   6 +-
 mobile/lib/interfaces/partner.interface.dart  |  10 +-
 .../lib/interfaces/partner_api.interface.dart |   8 +-
 mobile/lib/interfaces/user.interface.dart     |  27 --
 mobile/lib/interfaces/user_api.interface.dart |   4 +-
 .../lib/models/activities/activity.model.dart |   6 +-
 ...additional_shared_user_selection.page.dart |  16 +-
 .../lib/pages/album/album_options.page.dart   |  28 +-
 .../pages/album/album_shared_user_icons.dart  |   6 +-
 .../album_shared_user_selection.page.dart     |  10 +-
 mobile/lib/pages/albums/albums.page.dart      |   2 +-
 mobile/lib/pages/common/activities.page.dart  |   8 +-
 mobile/lib/pages/library/library.page.dart    |   4 +-
 .../pages/library/partner/partner.page.dart   |  12 +-
 .../library/partner/partner_detail.page.dart  |   8 +-
 mobile/lib/pages/photos/photos.page.dart      |   8 +-
 .../lib/providers/album/album.provider.dart   |   8 +-
 .../suggested_shared_users.provider.dart      |  11 +-
 mobile/lib/providers/auth.provider.dart       |  17 +-
 .../infrastructure/store.provider.dart        |   6 +-
 .../infrastructure/store.provider.g.dart      |  22 +-
 .../infrastructure/user.provider.dart         |  11 +
 .../infrastructure/user.provider.g.dart       |  27 ++
 mobile/lib/providers/partner.provider.dart    |  24 +-
 mobile/lib/providers/user.provider.dart       |  11 +-
 .../repositories/activity_api.repository.dart |   4 +-
 mobile/lib/repositories/album.repository.dart |  21 +-
 .../repositories/album_api.repository.dart    |  11 +-
 .../repositories/album_media.repository.dart  |   3 +-
 .../repositories/asset_media.repository.dart  |   2 +-
 mobile/lib/repositories/auth.repository.dart  |   2 +-
 .../lib/repositories/partner.repository.dart  |  50 +--
 .../repositories/partner_api.repository.dart  |  15 +-
 .../lib/repositories/timeline.repository.dart |   2 +-
 mobile/lib/repositories/user.repository.dart  |  73 -----
 .../lib/repositories/user_api.repository.dart |   7 +-
 mobile/lib/routing/router.dart                |   4 +-
 mobile/lib/routing/router.gr.dart             |   4 +-
 .../lib/routing/tab_navigation_observer.dart  |   4 +-
 mobile/lib/services/album.service.dart        |  21 +-
 mobile/lib/services/asset.service.dart        |  40 ++-
 mobile/lib/services/background.service.dart   |  11 +-
 .../services/backup_verification.service.dart |   2 +-
 mobile/lib/services/entity.service.dart       |  12 +-
 mobile/lib/services/partner.service.dart      |  45 +--
 mobile/lib/services/sync.service.dart         | 107 +++---
 mobile/lib/services/timeline.service.dart     |  35 +-
 mobile/lib/services/trash.service.dart        |  25 +-
 mobile/lib/services/user.service.dart         |  70 ++--
 mobile/lib/utils/bootstrap.dart               |   2 +-
 mobile/lib/utils/migration.dart               |   2 +-
 mobile/lib/utils/provider_utils.dart          |   2 +
 .../widgets/album/album_thumbnail_card.dart   |   2 +-
 .../asset_viewer/bottom_gallery_bar.dart      |  20 +-
 .../asset_viewer/description_input.dart       |   2 +-
 .../widgets/asset_viewer/gallery_app_bar.dart |  30 +-
 .../app_bar_dialog/app_bar_profile_info.dart  |   5 +-
 mobile/lib/widgets/common/user_avatar.dart    |   6 +-
 .../widgets/common/user_circle_avatar.dart    |  10 +-
 mobile/test/fixtures/album.stub.dart          |   5 +-
 mobile/test/fixtures/user.stub.dart           |  32 +-
 .../repositories/store_repository_test.dart   |   4 +-
 .../activity/activities_page_test.dart        |   4 +-
 mobile/test/modules/shared/shared_mocks.dart  |   6 +-
 .../modules/shared/sync_service_test.dart     |  31 +-
 mobile/test/repository.mocks.dart             |   5 +-
 mobile/test/services/album.service_test.dart  |   6 +-
 mobile/test/services/entity.service_test.dart |  21 +-
 mobile/test/test_utils.dart                   |   2 +-
 82 files changed, 1039 insertions(+), 947 deletions(-)
 create mode 100644 mobile/lib/domain/interfaces/user.interface.dart
 create mode 100644 mobile/lib/domain/models/user.model.dart
 delete mode 100644 mobile/lib/entities/user.entity.dart
 create mode 100644 mobile/lib/infrastructure/entities/user.entity.dart
 rename mobile/lib/{ => infrastructure}/entities/user.entity.g.dart (86%)
 create mode 100644 mobile/lib/infrastructure/repositories/user.repository.dart
 create mode 100644 mobile/lib/infrastructure/utils/user.converter.dart
 delete mode 100644 mobile/lib/interfaces/user.interface.dart
 create mode 100644 mobile/lib/providers/infrastructure/user.provider.dart
 create mode 100644 mobile/lib/providers/infrastructure/user.provider.g.dart
 delete mode 100644 mobile/lib/repositories/user.repository.dart

diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml
index 4e20acb72a..085449756d 100644
--- a/mobile/analysis_options.yaml
+++ b/mobile/analysis_options.yaml
@@ -67,7 +67,7 @@ custom_lint:
         - lib/entities/*.entity.dart
         - lib/repositories/{album,asset,backup,database,etag,exif_info,user,timeline,partner}.repository.dart
         - lib/infrastructure/entities/*.entity.dart
-        - lib/infrastructure/repositories/{store,db,log,exif}.repository.dart
+        - lib/infrastructure/repositories/*.repository.dart
         - lib/providers/infrastructure/db.provider.dart
         # acceptable exceptions for the time being (until Isar is fully replaced)
         - lib/providers/app_life_cycle.provider.dart
@@ -93,6 +93,7 @@ custom_lint:
         - lib/infrastructure/utils/*.converter.dart
         # acceptable exceptions for the time being
         - lib/entities/{album,asset,exif_info,user}.entity.dart # to convert DTOs to entities
+        - lib/infrastructure/utils/*.converter.dart
         - lib/utils/{image_url_builder,openapi_patching}.dart # utils are fine
         - test/modules/utils/openapi_patching_test.dart # filename is self-explanatory...
         - lib/domain/services/sync_stream.service.dart # Making sure to comply with the type from database
diff --git a/mobile/lib/domain/interfaces/user.interface.dart b/mobile/lib/domain/interfaces/user.interface.dart
new file mode 100644
index 0000000000..03f3ebb63e
--- /dev/null
+++ b/mobile/lib/domain/interfaces/user.interface.dart
@@ -0,0 +1,24 @@
+import 'package:immich_mobile/domain/interfaces/db.interface.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+
+abstract interface class IUserRepository implements IDatabaseRepository {
+  Future<bool> insert(UserDto user);
+
+  Future<UserDto?> get(int id);
+
+  Future<UserDto?> getByUserId(String id);
+
+  Future<List<UserDto?>> getByUserIds(List<String> ids);
+
+  Future<List<UserDto>> getAll({SortUserBy? sortBy});
+
+  Future<bool> updateAll(List<UserDto> users);
+
+  Future<UserDto> update(UserDto user);
+
+  Future<void> delete(List<int> ids);
+
+  Future<void> deleteAll();
+}
+
+enum SortUserBy { id }
diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart
index 06b946b3f6..8a99d68b8f 100644
--- a/mobile/lib/domain/models/store.model.dart
+++ b/mobile/lib/domain/models/store.model.dart
@@ -1,11 +1,11 @@
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 
 /// Key for each possible value in the `Store`.
 /// Defines the data type for each value
 enum StoreKey<T> {
   version<int>._(0),
   assetETag<String>._(1),
-  currentUser<User>._(2),
+  currentUser<UserDto>._(2),
   deviceIdHash<int>._(3),
   deviceId<String>._(4),
   backupFailedSince<DateTime>._(5),
diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart
new file mode 100644
index 0000000000..ceb65f313a
--- /dev/null
+++ b/mobile/lib/domain/models/user.model.dart
@@ -0,0 +1,157 @@
+import 'dart:ui';
+
+import 'package:immich_mobile/utils/hash.dart';
+
+enum AvatarColor {
+  // do not change this order or reuse indices for other purposes, adding is OK
+  primary,
+  pink,
+  red,
+  yellow,
+  blue,
+  green,
+  purple,
+  orange,
+  gray,
+  amber;
+
+  Color toColor({bool isDarkTheme = false}) => switch (this) {
+        AvatarColor.primary =>
+          isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF),
+        AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182),
+        AvatarColor.red => const Color.fromARGB(255, 239, 68, 68),
+        AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8),
+        AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246),
+        AvatarColor.green => const Color.fromARGB(255, 22, 163, 74),
+        AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234),
+        AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12),
+        AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99),
+        AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6),
+      };
+}
+
+// TODO: Rename to User once Isar is removed
+class UserDto {
+  final String uid;
+  final String email;
+  final String name;
+  final bool isAdmin;
+  final DateTime updatedAt;
+
+  final String? profileImagePath;
+  final AvatarColor avatarColor;
+
+  final bool memoryEnabled;
+  final bool inTimeline;
+
+  final bool isPartnerSharedBy;
+  final bool isPartnerSharedWith;
+
+  final int quotaUsageInBytes;
+  final int quotaSizeInBytes;
+
+  int get id => fastHash(uid);
+  bool get hasQuota => quotaSizeInBytes > 0;
+
+  const UserDto({
+    required this.uid,
+    required this.email,
+    required this.name,
+    required this.isAdmin,
+    required this.updatedAt,
+    this.profileImagePath,
+    this.avatarColor = AvatarColor.primary,
+    this.memoryEnabled = true,
+    this.inTimeline = false,
+    this.isPartnerSharedBy = false,
+    this.isPartnerSharedWith = false,
+    this.quotaUsageInBytes = 0,
+    this.quotaSizeInBytes = 0,
+  });
+
+  @override
+  String toString() {
+    return '''User: {
+id: $id,
+uid: $uid,
+email: $email,
+name: $name,
+isAdmin: $isAdmin,
+updatedAt: $updatedAt,
+profileImagePath: ${profileImagePath ?? '<NA>'},
+avatarColor: $avatarColor,
+memoryEnabled: $memoryEnabled,
+inTimeline: $inTimeline,
+isPartnerSharedBy: $isPartnerSharedBy,
+isPartnerSharedWith: $isPartnerSharedWith,
+quotaUsageInBytes: $quotaUsageInBytes,
+quotaSizeInBytes: $quotaSizeInBytes,
+}''';
+  }
+
+  UserDto copyWith({
+    String? uid,
+    String? email,
+    String? name,
+    bool? isAdmin,
+    DateTime? updatedAt,
+    String? profileImagePath,
+    AvatarColor? avatarColor,
+    bool? memoryEnabled,
+    bool? inTimeline,
+    bool? isPartnerSharedBy,
+    bool? isPartnerSharedWith,
+    int? quotaUsageInBytes,
+    int? quotaSizeInBytes,
+  }) =>
+      UserDto(
+        uid: uid ?? this.uid,
+        email: email ?? this.email,
+        name: name ?? this.name,
+        isAdmin: isAdmin ?? this.isAdmin,
+        updatedAt: updatedAt ?? this.updatedAt,
+        profileImagePath: profileImagePath ?? this.profileImagePath,
+        avatarColor: avatarColor ?? this.avatarColor,
+        memoryEnabled: memoryEnabled ?? this.memoryEnabled,
+        inTimeline: inTimeline ?? this.inTimeline,
+        isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy,
+        isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith,
+        quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
+        quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
+      );
+
+  @override
+  bool operator ==(covariant UserDto other) {
+    if (identical(this, other)) return true;
+
+    return other.uid == uid &&
+        other.updatedAt.isAtSameMomentAs(updatedAt) &&
+        other.avatarColor == avatarColor &&
+        other.email == email &&
+        other.name == name &&
+        other.isPartnerSharedBy == isPartnerSharedBy &&
+        other.isPartnerSharedWith == isPartnerSharedWith &&
+        other.profileImagePath == profileImagePath &&
+        other.isAdmin == isAdmin &&
+        other.memoryEnabled == memoryEnabled &&
+        other.inTimeline == inTimeline &&
+        other.quotaUsageInBytes == quotaUsageInBytes &&
+        other.quotaSizeInBytes == quotaSizeInBytes;
+  }
+
+  @override
+  int get hashCode =>
+      uid.hashCode ^
+      name.hashCode ^
+      email.hashCode ^
+      updatedAt.hashCode ^
+      isAdmin.hashCode ^
+      profileImagePath.hashCode ^
+      avatarColor.hashCode ^
+      memoryEnabled.hashCode ^
+      inTimeline.hashCode ^
+      isPartnerSharedBy.hashCode ^
+      isPartnerSharedWith.hashCode ^
+      quotaUsageInBytes.hashCode ^
+      quotaSizeInBytes.hashCode;
+}
diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart
index 70b9f31c00..73426cbf4e 100644
--- a/mobile/lib/domain/services/store.service.dart
+++ b/mobile/lib/domain/services/store.service.dart
@@ -74,7 +74,7 @@ class StoreService {
     return value;
   }
 
-  /// Asynchronously stores the value in the DB and synchronously in the cache
+  /// Asynchronously stores the value in the Store
   Future<void> put<U extends StoreKey<T>, T>(U key, T value) async {
     if (_cache[key.id] == value) return;
     await _storeRepository.insert(key, value);
@@ -84,7 +84,7 @@ class StoreService {
   /// Watches a specific key for changes
   Stream<T?> watch<T>(StoreKey<T> key) => _storeRepository.watch(key);
 
-  /// Removes the value asynchronously from the DB and synchronously from the cache
+  /// Removes the value asynchronously from the Store
   Future<void> delete<T>(StoreKey<T> key) async {
     await _storeRepository.delete(key);
     _cache.remove(key.id);
diff --git a/mobile/lib/entities/album.entity.dart b/mobile/lib/entities/album.entity.dart
index 8caff2255f..8b466da1db 100644
--- a/mobile/lib/entities/album.entity.dart
+++ b/mobile/lib/entities/album.entity.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/foundation.dart';
 import 'package:immich_mobile/constants/enums.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/utils/datetime_comparison.dart';
 import 'package:isar/isar.dart';
 // ignore: implementation_imports
diff --git a/mobile/lib/entities/user.entity.dart b/mobile/lib/entities/user.entity.dart
deleted file mode 100644
index 8fa6e83874..0000000000
--- a/mobile/lib/entities/user.entity.dart
+++ /dev/null
@@ -1,181 +0,0 @@
-import 'dart:ui';
-
-import 'package:immich_mobile/entities/album.entity.dart';
-import 'package:immich_mobile/utils/hash.dart';
-import 'package:isar/isar.dart';
-import 'package:openapi/api.dart';
-
-part 'user.entity.g.dart';
-
-@Collection(inheritance: false)
-class User {
-  User({
-    required this.id,
-    required this.updatedAt,
-    required this.email,
-    required this.name,
-    required this.isAdmin,
-    this.isPartnerSharedBy = false,
-    this.isPartnerSharedWith = false,
-    this.profileImagePath = '',
-    this.avatarColor = AvatarColorEnum.primary,
-    this.memoryEnabled = true,
-    this.inTimeline = false,
-    this.quotaUsageInBytes = 0,
-    this.quotaSizeInBytes = 0,
-  });
-
-  Id get isarId => fastHash(id);
-
-  User.fromUserDto(
-    UserAdminResponseDto dto,
-    UserPreferencesResponseDto? preferences,
-  )   : id = dto.id,
-        updatedAt = dto.updatedAt,
-        email = dto.email,
-        name = dto.name,
-        isPartnerSharedBy = false,
-        isPartnerSharedWith = false,
-        profileImagePath = dto.profileImagePath,
-        isAdmin = dto.isAdmin,
-        memoryEnabled = preferences?.memories.enabled ?? false,
-        avatarColor = dto.avatarColor.toAvatarColor(),
-        inTimeline = false,
-        quotaUsageInBytes = dto.quotaUsageInBytes ?? 0,
-        quotaSizeInBytes = dto.quotaSizeInBytes ?? 0;
-
-  User.fromPartnerDto(PartnerResponseDto dto)
-      : id = dto.id,
-        updatedAt = DateTime.now(),
-        email = dto.email,
-        name = dto.name,
-        isPartnerSharedBy = false,
-        isPartnerSharedWith = false,
-        profileImagePath = dto.profileImagePath,
-        isAdmin = false,
-        memoryEnabled = false,
-        avatarColor = dto.avatarColor.toAvatarColor(),
-        inTimeline = dto.inTimeline ?? false,
-        quotaUsageInBytes = 0,
-        quotaSizeInBytes = 0;
-
-  /// Base user dto used where the complete user object is not required
-  User.fromSimpleUserDto(UserResponseDto dto)
-      : id = dto.id,
-        email = dto.email,
-        name = dto.name,
-        profileImagePath = dto.profileImagePath,
-        avatarColor = dto.avatarColor.toAvatarColor(),
-        // Fill the remaining fields with placeholders
-        isAdmin = false,
-        inTimeline = false,
-        memoryEnabled = false,
-        isPartnerSharedBy = false,
-        isPartnerSharedWith = false,
-        updatedAt = DateTime.now(),
-        quotaUsageInBytes = 0,
-        quotaSizeInBytes = 0;
-
-  @Index(unique: true, replace: false, type: IndexType.hash)
-  String id;
-  DateTime updatedAt;
-  String email;
-  String name;
-  bool isPartnerSharedBy;
-  bool isPartnerSharedWith;
-  bool isAdmin;
-  String profileImagePath;
-  @Enumerated(EnumType.ordinal)
-  AvatarColorEnum avatarColor;
-  bool memoryEnabled;
-  bool inTimeline;
-  int quotaUsageInBytes;
-  int quotaSizeInBytes;
-
-  bool get hasQuota => quotaSizeInBytes > 0;
-  @Backlink(to: 'owner')
-  final IsarLinks<Album> albums = IsarLinks<Album>();
-  @Backlink(to: 'sharedUsers')
-  final IsarLinks<Album> sharedAlbums = IsarLinks<Album>();
-
-  @override
-  bool operator ==(other) {
-    if (other is! User) return false;
-    return id == other.id &&
-        updatedAt.isAtSameMomentAs(other.updatedAt) &&
-        avatarColor == other.avatarColor &&
-        email == other.email &&
-        name == other.name &&
-        isPartnerSharedBy == other.isPartnerSharedBy &&
-        isPartnerSharedWith == other.isPartnerSharedWith &&
-        profileImagePath == other.profileImagePath &&
-        isAdmin == other.isAdmin &&
-        memoryEnabled == other.memoryEnabled &&
-        inTimeline == other.inTimeline &&
-        quotaUsageInBytes == other.quotaUsageInBytes &&
-        quotaSizeInBytes == other.quotaSizeInBytes;
-  }
-
-  @override
-  @ignore
-  int get hashCode =>
-      id.hashCode ^
-      updatedAt.hashCode ^
-      email.hashCode ^
-      name.hashCode ^
-      isPartnerSharedBy.hashCode ^
-      isPartnerSharedWith.hashCode ^
-      profileImagePath.hashCode ^
-      avatarColor.hashCode ^
-      isAdmin.hashCode ^
-      memoryEnabled.hashCode ^
-      inTimeline.hashCode ^
-      quotaUsageInBytes.hashCode ^
-      quotaSizeInBytes.hashCode;
-}
-
-enum AvatarColorEnum {
-  // do not change this order or reuse indices for other purposes, adding is OK
-  primary,
-  pink,
-  red,
-  yellow,
-  blue,
-  green,
-  purple,
-  orange,
-  gray,
-  amber,
-}
-
-extension AvatarColorEnumHelper on UserAvatarColor {
-  AvatarColorEnum toAvatarColor() => switch (this) {
-        UserAvatarColor.primary => AvatarColorEnum.primary,
-        UserAvatarColor.pink => AvatarColorEnum.pink,
-        UserAvatarColor.red => AvatarColorEnum.red,
-        UserAvatarColor.yellow => AvatarColorEnum.yellow,
-        UserAvatarColor.blue => AvatarColorEnum.blue,
-        UserAvatarColor.green => AvatarColorEnum.green,
-        UserAvatarColor.purple => AvatarColorEnum.purple,
-        UserAvatarColor.orange => AvatarColorEnum.orange,
-        UserAvatarColor.gray => AvatarColorEnum.gray,
-        UserAvatarColor.amber => AvatarColorEnum.amber,
-        _ => AvatarColorEnum.primary,
-      };
-}
-
-extension AvatarColorToColorHelper on AvatarColorEnum {
-  Color toColor([bool isDarkTheme = false]) => switch (this) {
-        AvatarColorEnum.primary =>
-          isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF),
-        AvatarColorEnum.pink => const Color.fromARGB(255, 244, 114, 182),
-        AvatarColorEnum.red => const Color.fromARGB(255, 239, 68, 68),
-        AvatarColorEnum.yellow => const Color.fromARGB(255, 234, 179, 8),
-        AvatarColorEnum.blue => const Color.fromARGB(255, 59, 130, 246),
-        AvatarColorEnum.green => const Color.fromARGB(255, 22, 163, 74),
-        AvatarColorEnum.purple => const Color.fromARGB(255, 147, 51, 234),
-        AvatarColorEnum.orange => const Color.fromARGB(255, 234, 88, 12),
-        AvatarColorEnum.gray => const Color.fromARGB(255, 75, 85, 99),
-        AvatarColorEnum.amber => const Color.fromARGB(255, 217, 119, 6),
-      };
-}
diff --git a/mobile/lib/extensions/collection_extensions.dart b/mobile/lib/extensions/collection_extensions.dart
index d27c9e9500..e02582588b 100644
--- a/mobile/lib/extensions/collection_extensions.dart
+++ b/mobile/lib/extensions/collection_extensions.dart
@@ -1,8 +1,8 @@
 import 'dart:typed_data';
 
 import 'package:collection/collection.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 
 extension ListExtension<E> on List<E> {
   List<E> uniqueConsecutive({
@@ -58,11 +58,11 @@ extension AssetListExtension on Iterable<Asset> {
   /// Returns the assets that are owned by the user passed to the [owner] param
   /// If [owner] is null, an empty list is returned
   Iterable<Asset> ownedOnly(
-    User? owner, {
+    UserDto? owner, {
     void Function()? errorCallback,
   }) {
     if (owner == null) return [];
-    final userId = owner.isarId;
+    final userId = owner.id;
     final bool onlyOwned = every((e) => e.ownerId == userId);
     if (!onlyOwned) {
       if (errorCallback != null) errorCallback();
diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart
new file mode 100644
index 0000000000..834559b936
--- /dev/null
+++ b/mobile/lib/infrastructure/entities/user.entity.dart
@@ -0,0 +1,73 @@
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/utils/hash.dart';
+import 'package:isar/isar.dart';
+
+part 'user.entity.g.dart';
+
+@Collection(inheritance: false)
+class User {
+  Id get isarId => fastHash(id);
+  @Index(unique: true, replace: false, type: IndexType.hash)
+  final String id;
+  final DateTime updatedAt;
+  final String email;
+  final String name;
+  final bool isPartnerSharedBy;
+  final bool isPartnerSharedWith;
+  final bool isAdmin;
+  final String profileImagePath;
+  @Enumerated(EnumType.ordinal)
+  final AvatarColor avatarColor;
+  final bool memoryEnabled;
+  final bool inTimeline;
+  final int quotaUsageInBytes;
+  final int quotaSizeInBytes;
+
+  const User({
+    required this.id,
+    required this.updatedAt,
+    required this.email,
+    required this.name,
+    required this.isAdmin,
+    this.isPartnerSharedBy = false,
+    this.isPartnerSharedWith = false,
+    this.profileImagePath = '',
+    this.avatarColor = AvatarColor.primary,
+    this.memoryEnabled = true,
+    this.inTimeline = false,
+    this.quotaUsageInBytes = 0,
+    this.quotaSizeInBytes = 0,
+  });
+
+  static User fromDto(UserDto dto) => User(
+        id: dto.uid,
+        updatedAt: dto.updatedAt,
+        email: dto.email,
+        name: dto.name,
+        isAdmin: dto.isAdmin,
+        isPartnerSharedBy: dto.isPartnerSharedBy,
+        isPartnerSharedWith: dto.isPartnerSharedWith,
+        profileImagePath: dto.profileImagePath ?? "",
+        avatarColor: dto.avatarColor,
+        memoryEnabled: dto.memoryEnabled,
+        inTimeline: dto.inTimeline,
+        quotaUsageInBytes: dto.quotaUsageInBytes,
+        quotaSizeInBytes: dto.quotaSizeInBytes,
+      );
+
+  UserDto toDto() => UserDto(
+        uid: id,
+        email: email,
+        name: name,
+        isAdmin: isAdmin,
+        updatedAt: updatedAt,
+        profileImagePath: profileImagePath.isEmpty ? null : profileImagePath,
+        avatarColor: avatarColor,
+        memoryEnabled: memoryEnabled,
+        inTimeline: inTimeline,
+        isPartnerSharedBy: isPartnerSharedBy,
+        isPartnerSharedWith: isPartnerSharedWith,
+        quotaUsageInBytes: quotaUsageInBytes,
+        quotaSizeInBytes: quotaSizeInBytes,
+      );
+}
diff --git a/mobile/lib/entities/user.entity.g.dart b/mobile/lib/infrastructure/entities/user.entity.g.dart
similarity index 86%
rename from mobile/lib/entities/user.entity.g.dart
rename to mobile/lib/infrastructure/entities/user.entity.g.dart
index a7aaee44bf..37a793b2c3 100644
--- a/mobile/lib/entities/user.entity.g.dart
+++ b/mobile/lib/infrastructure/entities/user.entity.g.dart
@@ -28,63 +28,58 @@ const UserSchema = CollectionSchema(
       name: r'email',
       type: IsarType.string,
     ),
-    r'hasQuota': PropertySchema(
-      id: 2,
-      name: r'hasQuota',
-      type: IsarType.bool,
-    ),
     r'id': PropertySchema(
-      id: 3,
+      id: 2,
       name: r'id',
       type: IsarType.string,
     ),
     r'inTimeline': PropertySchema(
-      id: 4,
+      id: 3,
       name: r'inTimeline',
       type: IsarType.bool,
     ),
     r'isAdmin': PropertySchema(
-      id: 5,
+      id: 4,
       name: r'isAdmin',
       type: IsarType.bool,
     ),
     r'isPartnerSharedBy': PropertySchema(
-      id: 6,
+      id: 5,
       name: r'isPartnerSharedBy',
       type: IsarType.bool,
     ),
     r'isPartnerSharedWith': PropertySchema(
-      id: 7,
+      id: 6,
       name: r'isPartnerSharedWith',
       type: IsarType.bool,
     ),
     r'memoryEnabled': PropertySchema(
-      id: 8,
+      id: 7,
       name: r'memoryEnabled',
       type: IsarType.bool,
     ),
     r'name': PropertySchema(
-      id: 9,
+      id: 8,
       name: r'name',
       type: IsarType.string,
     ),
     r'profileImagePath': PropertySchema(
-      id: 10,
+      id: 9,
       name: r'profileImagePath',
       type: IsarType.string,
     ),
     r'quotaSizeInBytes': PropertySchema(
-      id: 11,
+      id: 10,
       name: r'quotaSizeInBytes',
       type: IsarType.long,
     ),
     r'quotaUsageInBytes': PropertySchema(
-      id: 12,
+      id: 11,
       name: r'quotaUsageInBytes',
       type: IsarType.long,
     ),
     r'updatedAt': PropertySchema(
-      id: 13,
+      id: 12,
       name: r'updatedAt',
       type: IsarType.dateTime,
     )
@@ -109,22 +104,7 @@ const UserSchema = CollectionSchema(
       ],
     )
   },
-  links: {
-    r'albums': LinkSchema(
-      id: -8764917375410137318,
-      name: r'albums',
-      target: r'Album',
-      single: false,
-      linkName: r'owner',
-    ),
-    r'sharedAlbums': LinkSchema(
-      id: -7037628715076287024,
-      name: r'sharedAlbums',
-      target: r'Album',
-      single: false,
-      linkName: r'sharedUsers',
-    )
-  },
+  links: {},
   embeddedSchemas: {},
   getId: _userGetId,
   getLinks: _userGetLinks,
@@ -153,18 +133,17 @@ void _userSerialize(
 ) {
   writer.writeByte(offsets[0], object.avatarColor.index);
   writer.writeString(offsets[1], object.email);
-  writer.writeBool(offsets[2], object.hasQuota);
-  writer.writeString(offsets[3], object.id);
-  writer.writeBool(offsets[4], object.inTimeline);
-  writer.writeBool(offsets[5], object.isAdmin);
-  writer.writeBool(offsets[6], object.isPartnerSharedBy);
-  writer.writeBool(offsets[7], object.isPartnerSharedWith);
-  writer.writeBool(offsets[8], object.memoryEnabled);
-  writer.writeString(offsets[9], object.name);
-  writer.writeString(offsets[10], object.profileImagePath);
-  writer.writeLong(offsets[11], object.quotaSizeInBytes);
-  writer.writeLong(offsets[12], object.quotaUsageInBytes);
-  writer.writeDateTime(offsets[13], object.updatedAt);
+  writer.writeString(offsets[2], object.id);
+  writer.writeBool(offsets[3], object.inTimeline);
+  writer.writeBool(offsets[4], object.isAdmin);
+  writer.writeBool(offsets[5], object.isPartnerSharedBy);
+  writer.writeBool(offsets[6], object.isPartnerSharedWith);
+  writer.writeBool(offsets[7], object.memoryEnabled);
+  writer.writeString(offsets[8], object.name);
+  writer.writeString(offsets[9], object.profileImagePath);
+  writer.writeLong(offsets[10], object.quotaSizeInBytes);
+  writer.writeLong(offsets[11], object.quotaUsageInBytes);
+  writer.writeDateTime(offsets[12], object.updatedAt);
 }
 
 User _userDeserialize(
@@ -176,19 +155,19 @@ User _userDeserialize(
   final object = User(
     avatarColor:
         _UseravatarColorValueEnumMap[reader.readByteOrNull(offsets[0])] ??
-            AvatarColorEnum.primary,
+            AvatarColor.primary,
     email: reader.readString(offsets[1]),
-    id: reader.readString(offsets[3]),
-    inTimeline: reader.readBoolOrNull(offsets[4]) ?? false,
-    isAdmin: reader.readBool(offsets[5]),
-    isPartnerSharedBy: reader.readBoolOrNull(offsets[6]) ?? false,
-    isPartnerSharedWith: reader.readBoolOrNull(offsets[7]) ?? false,
-    memoryEnabled: reader.readBoolOrNull(offsets[8]) ?? true,
-    name: reader.readString(offsets[9]),
-    profileImagePath: reader.readStringOrNull(offsets[10]) ?? '',
-    quotaSizeInBytes: reader.readLongOrNull(offsets[11]) ?? 0,
-    quotaUsageInBytes: reader.readLongOrNull(offsets[12]) ?? 0,
-    updatedAt: reader.readDateTime(offsets[13]),
+    id: reader.readString(offsets[2]),
+    inTimeline: reader.readBoolOrNull(offsets[3]) ?? false,
+    isAdmin: reader.readBool(offsets[4]),
+    isPartnerSharedBy: reader.readBoolOrNull(offsets[5]) ?? false,
+    isPartnerSharedWith: reader.readBoolOrNull(offsets[6]) ?? false,
+    memoryEnabled: reader.readBoolOrNull(offsets[7]) ?? true,
+    name: reader.readString(offsets[8]),
+    profileImagePath: reader.readStringOrNull(offsets[9]) ?? '',
+    quotaSizeInBytes: reader.readLongOrNull(offsets[10]) ?? 0,
+    quotaUsageInBytes: reader.readLongOrNull(offsets[11]) ?? 0,
+    updatedAt: reader.readDateTime(offsets[12]),
   );
   return object;
 }
@@ -202,32 +181,30 @@ P _userDeserializeProp<P>(
   switch (propertyId) {
     case 0:
       return (_UseravatarColorValueEnumMap[reader.readByteOrNull(offset)] ??
-          AvatarColorEnum.primary) as P;
+          AvatarColor.primary) as P;
     case 1:
       return (reader.readString(offset)) as P;
     case 2:
-      return (reader.readBool(offset)) as P;
-    case 3:
       return (reader.readString(offset)) as P;
-    case 4:
+    case 3:
       return (reader.readBoolOrNull(offset) ?? false) as P;
-    case 5:
+    case 4:
       return (reader.readBool(offset)) as P;
+    case 5:
+      return (reader.readBoolOrNull(offset) ?? false) as P;
     case 6:
       return (reader.readBoolOrNull(offset) ?? false) as P;
     case 7:
-      return (reader.readBoolOrNull(offset) ?? false) as P;
-    case 8:
       return (reader.readBoolOrNull(offset) ?? true) as P;
-    case 9:
+    case 8:
       return (reader.readString(offset)) as P;
-    case 10:
+    case 9:
       return (reader.readStringOrNull(offset) ?? '') as P;
+    case 10:
+      return (reader.readLongOrNull(offset) ?? 0) as P;
     case 11:
       return (reader.readLongOrNull(offset) ?? 0) as P;
     case 12:
-      return (reader.readLongOrNull(offset) ?? 0) as P;
-    case 13:
       return (reader.readDateTime(offset)) as P;
     default:
       throw IsarError('Unknown property with id $propertyId');
@@ -247,16 +224,16 @@ const _UseravatarColorEnumValueMap = {
   'amber': 9,
 };
 const _UseravatarColorValueEnumMap = {
-  0: AvatarColorEnum.primary,
-  1: AvatarColorEnum.pink,
-  2: AvatarColorEnum.red,
-  3: AvatarColorEnum.yellow,
-  4: AvatarColorEnum.blue,
-  5: AvatarColorEnum.green,
-  6: AvatarColorEnum.purple,
-  7: AvatarColorEnum.orange,
-  8: AvatarColorEnum.gray,
-  9: AvatarColorEnum.amber,
+  0: AvatarColor.primary,
+  1: AvatarColor.pink,
+  2: AvatarColor.red,
+  3: AvatarColor.yellow,
+  4: AvatarColor.blue,
+  5: AvatarColor.green,
+  6: AvatarColor.purple,
+  7: AvatarColor.orange,
+  8: AvatarColor.gray,
+  9: AvatarColor.amber,
 };
 
 Id _userGetId(User object) {
@@ -264,14 +241,10 @@ Id _userGetId(User object) {
 }
 
 List<IsarLinkBase<dynamic>> _userGetLinks(User object) {
-  return [object.albums, object.sharedAlbums];
+  return [];
 }
 
-void _userAttach(IsarCollection<dynamic> col, Id id, User object) {
-  object.albums.attach(col, col.isar.collection<Album>(), r'albums', id);
-  object.sharedAlbums
-      .attach(col, col.isar.collection<Album>(), r'sharedAlbums', id);
-}
+void _userAttach(IsarCollection<dynamic> col, Id id, User object) {}
 
 extension UserByIndex on IsarCollection<User> {
   Future<User?> getById(String id) {
@@ -447,7 +420,7 @@ extension UserQueryWhere on QueryBuilder<User, User, QWhereClause> {
 
 extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
   QueryBuilder<User, User, QAfterFilterCondition> avatarColorEqualTo(
-      AvatarColorEnum value) {
+      AvatarColor value) {
     return QueryBuilder.apply(this, (query) {
       return query.addFilterCondition(FilterCondition.equalTo(
         property: r'avatarColor',
@@ -457,7 +430,7 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
   }
 
   QueryBuilder<User, User, QAfterFilterCondition> avatarColorGreaterThan(
-    AvatarColorEnum value, {
+    AvatarColor value, {
     bool include = false,
   }) {
     return QueryBuilder.apply(this, (query) {
@@ -470,7 +443,7 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
   }
 
   QueryBuilder<User, User, QAfterFilterCondition> avatarColorLessThan(
-    AvatarColorEnum value, {
+    AvatarColor value, {
     bool include = false,
   }) {
     return QueryBuilder.apply(this, (query) {
@@ -483,8 +456,8 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
   }
 
   QueryBuilder<User, User, QAfterFilterCondition> avatarColorBetween(
-    AvatarColorEnum lower,
-    AvatarColorEnum upper, {
+    AvatarColor lower,
+    AvatarColor upper, {
     bool includeLower = true,
     bool includeUpper = true,
   }) {
@@ -627,15 +600,6 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
     });
   }
 
-  QueryBuilder<User, User, QAfterFilterCondition> hasQuotaEqualTo(bool value) {
-    return QueryBuilder.apply(this, (query) {
-      return query.addFilterCondition(FilterCondition.equalTo(
-        property: r'hasQuota',
-        value: value,
-      ));
-    });
-  }
-
   QueryBuilder<User, User, QAfterFilterCondition> idEqualTo(
     String value, {
     bool caseSensitive = true,
@@ -1285,118 +1249,7 @@ extension UserQueryFilter on QueryBuilder<User, User, QFilterCondition> {
 
 extension UserQueryObject on QueryBuilder<User, User, QFilterCondition> {}
 
-extension UserQueryLinks on QueryBuilder<User, User, QFilterCondition> {
-  QueryBuilder<User, User, QAfterFilterCondition> albums(FilterQuery<Album> q) {
-    return QueryBuilder.apply(this, (query) {
-      return query.link(q, r'albums');
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> albumsLengthEqualTo(
-      int length) {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'albums', length, true, length, true);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> albumsIsEmpty() {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'albums', 0, true, 0, true);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> albumsIsNotEmpty() {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'albums', 0, false, 999999, true);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> albumsLengthLessThan(
-    int length, {
-    bool include = false,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'albums', 0, true, length, include);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> albumsLengthGreaterThan(
-    int length, {
-    bool include = false,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'albums', length, include, 999999, true);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> albumsLengthBetween(
-    int lower,
-    int upper, {
-    bool includeLower = true,
-    bool includeUpper = true,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(
-          r'albums', lower, includeLower, upper, includeUpper);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> sharedAlbums(
-      FilterQuery<Album> q) {
-    return QueryBuilder.apply(this, (query) {
-      return query.link(q, r'sharedAlbums');
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> sharedAlbumsLengthEqualTo(
-      int length) {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'sharedAlbums', length, true, length, true);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> sharedAlbumsIsEmpty() {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'sharedAlbums', 0, true, 0, true);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> sharedAlbumsIsNotEmpty() {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'sharedAlbums', 0, false, 999999, true);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> sharedAlbumsLengthLessThan(
-    int length, {
-    bool include = false,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'sharedAlbums', 0, true, length, include);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> sharedAlbumsLengthGreaterThan(
-    int length, {
-    bool include = false,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(r'sharedAlbums', length, include, 999999, true);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterFilterCondition> sharedAlbumsLengthBetween(
-    int lower,
-    int upper, {
-    bool includeLower = true,
-    bool includeUpper = true,
-  }) {
-    return QueryBuilder.apply(this, (query) {
-      return query.linkLength(
-          r'sharedAlbums', lower, includeLower, upper, includeUpper);
-    });
-  }
-}
+extension UserQueryLinks on QueryBuilder<User, User, QFilterCondition> {}
 
 extension UserQuerySortBy on QueryBuilder<User, User, QSortBy> {
   QueryBuilder<User, User, QAfterSortBy> sortByAvatarColor() {
@@ -1423,18 +1276,6 @@ extension UserQuerySortBy on QueryBuilder<User, User, QSortBy> {
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> sortByHasQuota() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'hasQuota', Sort.asc);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterSortBy> sortByHasQuotaDesc() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'hasQuota', Sort.desc);
-    });
-  }
-
   QueryBuilder<User, User, QAfterSortBy> sortById() {
     return QueryBuilder.apply(this, (query) {
       return query.addSortBy(r'id', Sort.asc);
@@ -1593,18 +1434,6 @@ extension UserQuerySortThenBy on QueryBuilder<User, User, QSortThenBy> {
     });
   }
 
-  QueryBuilder<User, User, QAfterSortBy> thenByHasQuota() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'hasQuota', Sort.asc);
-    });
-  }
-
-  QueryBuilder<User, User, QAfterSortBy> thenByHasQuotaDesc() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addSortBy(r'hasQuota', Sort.desc);
-    });
-  }
-
   QueryBuilder<User, User, QAfterSortBy> thenById() {
     return QueryBuilder.apply(this, (query) {
       return query.addSortBy(r'id', Sort.asc);
@@ -1764,12 +1593,6 @@ extension UserQueryWhereDistinct on QueryBuilder<User, User, QDistinct> {
     });
   }
 
-  QueryBuilder<User, User, QDistinct> distinctByHasQuota() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addDistinctBy(r'hasQuota');
-    });
-  }
-
   QueryBuilder<User, User, QDistinct> distinctById(
       {bool caseSensitive = true}) {
     return QueryBuilder.apply(this, (query) {
@@ -1848,7 +1671,7 @@ extension UserQueryProperty on QueryBuilder<User, User, QQueryProperty> {
     });
   }
 
-  QueryBuilder<User, AvatarColorEnum, QQueryOperations> avatarColorProperty() {
+  QueryBuilder<User, AvatarColor, QQueryOperations> avatarColorProperty() {
     return QueryBuilder.apply(this, (query) {
       return query.addPropertyName(r'avatarColor');
     });
@@ -1860,12 +1683,6 @@ extension UserQueryProperty on QueryBuilder<User, User, QQueryProperty> {
     });
   }
 
-  QueryBuilder<User, bool, QQueryOperations> hasQuotaProperty() {
-    return QueryBuilder.apply(this, (query) {
-      return query.addPropertyName(r'hasQuota');
-    });
-  }
-
   QueryBuilder<User, String, QQueryOperations> idProperty() {
     return QueryBuilder.apply(this, (query) {
       return query.addPropertyName(r'id');
diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart
index 5cf6838ee1..1e5a5335d5 100644
--- a/mobile/lib/infrastructure/repositories/store.repository.dart
+++ b/mobile/lib/infrastructure/repositories/store.repository.dart
@@ -1,9 +1,9 @@
 import 'package:immich_mobile/domain/interfaces/store.interface.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
 import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
-import 'package:immich_mobile/repositories/user.repository.dart';
+import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
 import 'package:isar/isar.dart';
 
 class IsarStoreRepository extends IsarDatabaseRepository
@@ -78,7 +78,7 @@ class IsarStoreRepository extends IsarDatabaseRepository
         const (DateTime) => entity.intValue == null
             ? null
             : DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
-        const (User) => await UserRepository(_db).getByDbId(entity.intValue!),
+        const (UserDto) => await IsarUserRepository(_db).get(entity.intValue!),
         _ => null,
       } as T?;
 
@@ -88,8 +88,8 @@ class IsarStoreRepository extends IsarDatabaseRepository
       const (String) => (null, value as String),
       const (bool) => ((value as bool) ? 1 : 0, null),
       const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null),
-      const (User) => (
-          (await UserRepository(_db).update(value as User)).isarId,
+      const (UserDto) => (
+          (await IsarUserRepository(_db).update(value as UserDto)).id,
           null,
         ),
       _ => throw UnsupportedError(
diff --git a/mobile/lib/infrastructure/repositories/user.repository.dart b/mobile/lib/infrastructure/repositories/user.repository.dart
new file mode 100644
index 0000000000..2cb8023b2a
--- /dev/null
+++ b/mobile/lib/infrastructure/repositories/user.repository.dart
@@ -0,0 +1,80 @@
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
+    as entity;
+import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
+import 'package:isar/isar.dart';
+
+class IsarUserRepository extends IsarDatabaseRepository
+    implements IUserRepository {
+  final Isar _db;
+  const IsarUserRepository(super.db) : _db = db;
+
+  @override
+  Future<void> delete(List<int> ids) async {
+    await transaction(() async {
+      await _db.users.deleteAll(ids);
+    });
+  }
+
+  @override
+  Future<void> deleteAll() async {
+    await transaction(() async {
+      await _db.users.clear();
+    });
+  }
+
+  @override
+  Future<UserDto?> get(int id) async {
+    return (await _db.users.get(id))?.toDto();
+  }
+
+  @override
+  Future<List<UserDto>> getAll({SortUserBy? sortBy}) async {
+    return (await _db.users
+            .where()
+            .optional(
+              sortBy != null,
+              (query) => switch (sortBy!) {
+                SortUserBy.id => query.sortById(),
+              },
+            )
+            .findAll())
+        .map((u) => u.toDto())
+        .toList();
+  }
+
+  @override
+  Future<UserDto?> getByUserId(String id) async {
+    return (await _db.users.getById(id))?.toDto();
+  }
+
+  @override
+  Future<List<UserDto?>> getByUserIds(List<String> ids) async {
+    return (await _db.users.getAllById(ids)).map((u) => u?.toDto()).toList();
+  }
+
+  @override
+  Future<bool> insert(UserDto user) async {
+    await transaction(() async {
+      await _db.users.put(entity.User.fromDto(user));
+    });
+    return true;
+  }
+
+  @override
+  Future<UserDto> update(UserDto user) async {
+    await transaction(() async {
+      await _db.users.put(entity.User.fromDto(user));
+    });
+    return user;
+  }
+
+  @override
+  Future<bool> updateAll(List<UserDto> users) async {
+    await transaction(() async {
+      await _db.users.putAll(users.map(entity.User.fromDto).toList());
+    });
+    return true;
+  }
+}
diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart
new file mode 100644
index 0000000000..11f9ddec33
--- /dev/null
+++ b/mobile/lib/infrastructure/utils/user.converter.dart
@@ -0,0 +1,66 @@
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:openapi/api.dart';
+
+abstract final class UserConverter {
+  /// Base user dto used where the complete user object is not required
+  static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto(
+        uid: dto.id,
+        email: dto.email,
+        name: dto.name,
+        isAdmin: false,
+        updatedAt: DateTime.now(),
+        profileImagePath: dto.profileImagePath,
+        avatarColor: dto.avatarColor.toAvatarColor(),
+      );
+
+  static UserDto fromAdminDto(
+    UserAdminResponseDto adminDto, [
+    UserPreferencesResponseDto? preferenceDto,
+  ]) =>
+      UserDto(
+        uid: adminDto.id,
+        email: adminDto.email,
+        name: adminDto.name,
+        isAdmin: adminDto.isAdmin,
+        updatedAt: adminDto.updatedAt,
+        profileImagePath: adminDto.profileImagePath,
+        avatarColor: adminDto.avatarColor.toAvatarColor(),
+        memoryEnabled: preferenceDto?.memories.enabled ?? true,
+        inTimeline: false,
+        isPartnerSharedBy: false,
+        isPartnerSharedWith: false,
+        quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0,
+        quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0,
+      );
+
+  static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto(
+        uid: dto.id,
+        email: dto.email,
+        name: dto.name,
+        isAdmin: false,
+        updatedAt: DateTime.now(),
+        profileImagePath: dto.profileImagePath,
+        avatarColor: dto.avatarColor.toAvatarColor(),
+        memoryEnabled: false,
+        inTimeline: dto.inTimeline ?? false,
+        isPartnerSharedBy: false,
+        isPartnerSharedWith: false,
+        quotaUsageInBytes: 0,
+        quotaSizeInBytes: 0,
+      );
+}
+
+extension on UserAvatarColor {
+  AvatarColor toAvatarColor() => switch (this) {
+        UserAvatarColor.red => AvatarColor.red,
+        UserAvatarColor.green => AvatarColor.green,
+        UserAvatarColor.blue => AvatarColor.blue,
+        UserAvatarColor.purple => AvatarColor.purple,
+        UserAvatarColor.orange => AvatarColor.orange,
+        UserAvatarColor.pink => AvatarColor.pink,
+        UserAvatarColor.amber => AvatarColor.amber,
+        UserAvatarColor.yellow => AvatarColor.yellow,
+        UserAvatarColor.gray => AvatarColor.gray,
+        UserAvatarColor.primary || _ => AvatarColor.primary,
+      };
+}
diff --git a/mobile/lib/interfaces/album.interface.dart b/mobile/lib/interfaces/album.interface.dart
index 3a83a8feb7..c1696eda80 100644
--- a/mobile/lib/interfaces/album.interface.dart
+++ b/mobile/lib/interfaces/album.interface.dart
@@ -1,6 +1,6 @@
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/interfaces/database.interface.dart';
 import 'package:immich_mobile/models/albums/album_search.model.dart';
 
@@ -31,9 +31,9 @@ abstract interface class IAlbumRepository implements IDatabaseRepository {
 
   Future<int> count({bool? local});
 
-  Future<void> addUsers(Album album, List<User> users);
+  Future<void> addUsers(Album album, List<UserDto> users);
 
-  Future<void> removeUsers(Album album, List<User> users);
+  Future<void> removeUsers(Album album, List<UserDto> users);
 
   Future<void> addAssets(Album album, List<Asset> assets);
 
diff --git a/mobile/lib/interfaces/partner.interface.dart b/mobile/lib/interfaces/partner.interface.dart
index 995e07c392..8e5fcb7a97 100644
--- a/mobile/lib/interfaces/partner.interface.dart
+++ b/mobile/lib/interfaces/partner.interface.dart
@@ -1,8 +1,8 @@
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 
 abstract class IPartnerRepository {
-  Future<List<User>> getSharedWith();
-  Future<List<User>> getSharedBy();
-  Stream<List<User>> watchSharedWith();
-  Stream<List<User>> watchSharedBy();
+  Future<List<UserDto>> getSharedWith();
+  Future<List<UserDto>> getSharedBy();
+  Stream<List<UserDto>> watchSharedWith();
+  Stream<List<UserDto>> watchSharedBy();
 }
diff --git a/mobile/lib/interfaces/partner_api.interface.dart b/mobile/lib/interfaces/partner_api.interface.dart
index bca1baf66d..01149f473c 100644
--- a/mobile/lib/interfaces/partner_api.interface.dart
+++ b/mobile/lib/interfaces/partner_api.interface.dart
@@ -1,9 +1,9 @@
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.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<List<UserDto>> getAll(Direction direction);
+  Future<UserDto> create(String id);
+  Future<UserDto> update(String id, {required bool inTimeline});
   Future<void> delete(String id);
 }
 
diff --git a/mobile/lib/interfaces/user.interface.dart b/mobile/lib/interfaces/user.interface.dart
deleted file mode 100644
index d099e0e50b..0000000000
--- a/mobile/lib/interfaces/user.interface.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-import 'package:immich_mobile/entities/user.entity.dart';
-import 'package:immich_mobile/interfaces/database.interface.dart';
-
-abstract interface class IUserRepository implements IDatabaseRepository {
-  Future<User?> get(String id);
-
-  Future<User?> getByDbId(int id);
-
-  Future<List<User>> getByIds(List<String> ids);
-
-  Future<List<User>> getAll({bool self = true, UserSort? sortBy});
-
-  /// Returns all users whose assets can be accessed (self+partners)
-  Future<List<User>> getAllAccessible();
-
-  Future<List<User>> upsertAll(List<User> users);
-
-  Future<User> update(User user);
-
-  Future<void> deleteById(List<int> ids);
-
-  Future<User> me();
-
-  Future<void> clearTable();
-}
-
-enum UserSort { id }
diff --git a/mobile/lib/interfaces/user_api.interface.dart b/mobile/lib/interfaces/user_api.interface.dart
index 67ac3c0883..ebbbac8291 100644
--- a/mobile/lib/interfaces/user_api.interface.dart
+++ b/mobile/lib/interfaces/user_api.interface.dart
@@ -1,9 +1,9 @@
 import 'dart:typed_data';
 
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 
 abstract interface class IUserApiRepository {
-  Future<List<User>> getAll();
+  Future<List<UserDto>> getAll();
   Future<({String profileImagePath})> createProfileImage({
     required String name,
     required Uint8List data,
diff --git a/mobile/lib/models/activities/activity.model.dart b/mobile/lib/models/activities/activity.model.dart
index 4702753f41..17f70d5d62 100644
--- a/mobile/lib/models/activities/activity.model.dart
+++ b/mobile/lib/models/activities/activity.model.dart
@@ -1,4 +1,4 @@
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 
 enum ActivityType { comment, like }
 
@@ -8,7 +8,7 @@ class Activity {
   final String? comment;
   final DateTime createdAt;
   final ActivityType type;
-  final User user;
+  final UserDto user;
 
   const Activity({
     required this.id,
@@ -25,7 +25,7 @@ class Activity {
     String? comment,
     DateTime? createdAt,
     ActivityType? type,
-    User? user,
+    UserDto? user,
   }) {
     return Activity(
       id: id ?? this.id,
diff --git a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart
index 5096a8a249..2dc41b396d 100644
--- a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart
+++ b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart
@@ -3,11 +3,11 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/providers/album/suggested_shared_users.provider.dart';
-import 'package:immich_mobile/entities/album.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
 
 @RoutePage()
@@ -21,15 +21,15 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    final AsyncValue<List<User>> suggestedShareUsers =
+    final AsyncValue<List<UserDto>> suggestedShareUsers =
         ref.watch(otherUsersProvider);
-    final sharedUsersList = useState<Set<User>>({});
+    final sharedUsersList = useState<Set<UserDto>>({});
 
     addNewUsersHandler() {
-      context.maybePop(sharedUsersList.value.map((e) => e.id).toList());
+      context.maybePop(sharedUsersList.value.map((e) => e.uid).toList());
     }
 
-    buildTileIcon(User user) {
+    buildTileIcon(UserDto user) {
       if (sharedUsersList.value.contains(user)) {
         return CircleAvatar(
           backgroundColor: context.primaryColor,
@@ -45,7 +45,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
       }
     }
 
-    buildUserList(List<User> users) {
+    buildUserList(List<UserDto> users) {
       List<Widget> usersChip = [];
 
       for (var user in sharedUsersList.value) {
@@ -151,7 +151,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget {
         onData: (users) {
           for (var sharedUsers in album.sharedUsers) {
             users.removeWhere(
-              (u) => u.id == sharedUsers.id || u.id == album.ownerId,
+              (u) => u.uid == sharedUsers.id || u.uid == album.ownerId,
             );
           }
 
diff --git a/mobile/lib/pages/album/album_options.page.dart b/mobile/lib/pages/album/album_options.page.dart
index 0e9bfeb2ce..a765be50b3 100644
--- a/mobile/lib/pages/album/album_options.page.dart
+++ b/mobile/lib/pages/album/album_options.page.dart
@@ -4,14 +4,16 @@ import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/extensions/theme_extensions.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
+    as entity;
 import 'package:immich_mobile/providers/album/album.provider.dart';
 import 'package:immich_mobile/providers/album/current_album.provider.dart';
 import 'package:immich_mobile/providers/auth.provider.dart';
-import 'package:immich_mobile/utils/immich_loading_overlay.dart';
 import 'package:immich_mobile/routing/router.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/utils/immich_loading_overlay.dart';
 import 'package:immich_mobile/widgets/common/immich_toast.dart';
 import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
 
@@ -26,7 +28,8 @@ class AlbumOptionsPage extends HookConsumerWidget {
       return const SizedBox();
     }
 
-    final sharedUsers = useState(album.sharedUsers.toList());
+    final sharedUsers =
+        useState(album.sharedUsers.map((u) => u.toDto()).toList());
     final owner = album.owner.value;
     final userId = ref.watch(authProvider).userId;
     final activityEnabled = useState(album.activityEnabled);
@@ -64,13 +67,13 @@ class AlbumOptionsPage extends HookConsumerWidget {
       isProcessing.value = false;
     }
 
-    void removeUserFromAlbum(User user) async {
+    void removeUserFromAlbum(UserDto user) async {
       isProcessing.value = true;
 
       try {
         await ref.read(albumProvider.notifier).removeUser(album, user);
-        album.sharedUsers.remove(user);
-        sharedUsers.value = album.sharedUsers.toList();
+        album.sharedUsers.remove(entity.User.fromDto(user));
+        sharedUsers.value = album.sharedUsers.map((u) => u.toDto()).toList();
       } catch (error) {
         showErrorMessage();
       }
@@ -79,10 +82,10 @@ class AlbumOptionsPage extends HookConsumerWidget {
       isProcessing.value = false;
     }
 
-    void handleUserClick(User user) {
+    void handleUserClick(UserDto user) {
       var actions = [];
 
-      if (user.id == userId) {
+      if (user.uid == userId) {
         actions = [
           ListTile(
             leading: const Icon(Icons.exit_to_app_rounded),
@@ -123,8 +126,9 @@ class AlbumOptionsPage extends HookConsumerWidget {
 
     buildOwnerInfo() {
       return ListTile(
-        leading:
-            owner != null ? UserCircleAvatar(user: owner) : const SizedBox(),
+        leading: owner != null
+            ? UserCircleAvatar(user: owner.toDto())
+            : const SizedBox(),
         title: Text(
           album.owner.value?.name ?? "",
           style: const TextStyle(
@@ -166,10 +170,10 @@ class AlbumOptionsPage extends HookConsumerWidget {
                 color: context.colorScheme.onSurfaceSecondary,
               ),
             ),
-            trailing: userId == user.id || isOwner
+            trailing: userId == user.uid || isOwner
                 ? const Icon(Icons.more_horiz_rounded)
                 : const SizedBox(),
-            onTap: userId == user.id || isOwner
+            onTap: userId == user.uid || isOwner
                 ? () => handleUserClick(user)
                 : null,
           );
diff --git a/mobile/lib/pages/album/album_shared_user_icons.dart b/mobile/lib/pages/album/album_shared_user_icons.dart
index f417f9fb38..47ea476028 100644
--- a/mobile/lib/pages/album/album_shared_user_icons.dart
+++ b/mobile/lib/pages/album/album_shared_user_icons.dart
@@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/providers/album/current_album.provider.dart';
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
@@ -12,7 +12,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    final sharedUsers = useRef<List<User>>(const []);
+    final sharedUsers = useRef<List<UserDto>>(const []);
     sharedUsers.value = ref.watch(
       currentAlbumProvider.select((album) {
         if (album == null) {
@@ -23,7 +23,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget {
           return sharedUsers.value;
         }
 
-        return album.sharedUsers.toList(growable: false);
+        return album.sharedUsers.map((u) => u.toDto()).toList(growable: false);
       }),
     );
 
diff --git a/mobile/lib/pages/album/album_shared_user_selection.page.dart b/mobile/lib/pages/album/album_shared_user_selection.page.dart
index 61ef267b99..1c797aa449 100644
--- a/mobile/lib/pages/album/album_shared_user_selection.page.dart
+++ b/mobile/lib/pages/album/album_shared_user_selection.page.dart
@@ -3,14 +3,14 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/providers/album/album.provider.dart';
 import 'package:immich_mobile/providers/album/album_title.provider.dart';
 import 'package:immich_mobile/providers/album/suggested_shared_users.provider.dart';
 import 'package:immich_mobile/routing/router.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
 
 @RoutePage()
@@ -21,7 +21,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    final sharedUsersList = useState<Set<User>>({});
+    final sharedUsersList = useState<Set<UserDto>>({});
     final suggestedShareUsers = ref.watch(otherUsersProvider);
 
     createSharedAlbum() async {
@@ -48,7 +48,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
       );
     }
 
-    buildTileIcon(User user) {
+    buildTileIcon(UserDto user) {
       if (sharedUsersList.value.contains(user)) {
         return CircleAvatar(
           backgroundColor: context.primaryColor,
@@ -64,7 +64,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget {
       }
     }
 
-    buildUserList(List<User> users) {
+    buildUserList(List<UserDto> users) {
       List<Widget> usersChip = [];
 
       for (var user in sharedUsersList.value) {
diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart
index e5758c959c..acdfbf385f 100644
--- a/mobile/lib/pages/albums/albums.page.dart
+++ b/mobile/lib/pages/albums/albums.page.dart
@@ -33,7 +33,7 @@ class AlbumsPage extends HookConsumerWidget {
     final searchController = useTextEditingController();
     final debounceTimer = useRef<Timer?>(null);
     final filterMode = useState(QuickFilterMode.all);
-    final userId = ref.watch(currentUserProvider)?.id;
+    final userId = ref.watch(currentUserProvider)?.uid;
     final searchFocusNode = useFocusNode();
 
     toggleViewMode() {
diff --git a/mobile/lib/pages/common/activities.page.dart b/mobile/lib/pages/common/activities.page.dart
index 9678058111..84a8622fa5 100644
--- a/mobile/lib/pages/common/activities.page.dart
+++ b/mobile/lib/pages/common/activities.page.dart
@@ -7,12 +7,12 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/models/activities/activity.model.dart';
 import 'package:immich_mobile/providers/activity.provider.dart';
-import 'package:immich_mobile/widgets/activities/activity_text_field.dart';
-import 'package:immich_mobile/widgets/activities/activity_tile.dart';
-import 'package:immich_mobile/widgets/activities/dismissible_activity.dart';
 import 'package:immich_mobile/providers/album/current_album.provider.dart';
 import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
 import 'package:immich_mobile/providers/user.provider.dart';
+import 'package:immich_mobile/widgets/activities/activity_text_field.dart';
+import 'package:immich_mobile/widgets/activities/activity_tile.dart';
+import 'package:immich_mobile/widgets/activities/dismissible_activity.dart';
 
 @RoutePage()
 class ActivitiesPage extends HookConsumerWidget {
@@ -72,7 +72,7 @@ class ActivitiesPage extends HookConsumerWidget {
 
                     final activity = data[index];
                     final canDelete = activity.user.id == user?.id ||
-                        album.ownerId == user?.id;
+                        album.ownerId == user?.uid;
 
                     return Padding(
                       padding: const EdgeInsets.all(5),
diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart
index 31b465ead7..86c411395b 100644
--- a/mobile/lib/pages/library/library.page.dart
+++ b/mobile/lib/pages/library/library.page.dart
@@ -2,7 +2,7 @@ import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/providers/album/album.provider.dart';
@@ -163,7 +163,7 @@ class QuickAccessButtons extends ConsumerWidget {
 class PartnerList extends ConsumerWidget {
   const PartnerList({super.key, required this.partners});
 
-  final List<User> partners;
+  final List<UserDto> partners;
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
diff --git a/mobile/lib/pages/library/partner/partner.page.dart b/mobile/lib/pages/library/partner/partner.page.dart
index 1e9e801210..faea069c50 100644
--- a/mobile/lib/pages/library/partner/partner.page.dart
+++ b/mobile/lib/pages/library/partner/partner.page.dart
@@ -2,10 +2,10 @@ import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/providers/partner.provider.dart';
 import 'package:immich_mobile/services/partner.service.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/widgets/common/confirm_dialog.dart';
 import 'package:immich_mobile/widgets/common/immich_toast.dart';
 import 'package:immich_mobile/widgets/common/user_avatar.dart';
@@ -16,7 +16,7 @@ class PartnerPage extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    final List<User> partners = ref.watch(partnerSharedByProvider);
+    final List<UserDto> partners = ref.watch(partnerSharedByProvider);
     final availableUsers = ref.watch(partnerAvailableProvider);
 
     addNewUsersHandler() async {
@@ -29,13 +29,13 @@ class PartnerPage extends HookConsumerWidget {
         return;
       }
 
-      final selectedUser = await showDialog<User>(
+      final selectedUser = await showDialog<UserDto>(
         context: context,
         builder: (context) {
           return SimpleDialog(
             title: const Text("partner_page_select_partner").tr(),
             children: [
-              for (User u in users)
+              for (UserDto u in users)
                 SimpleDialogOption(
                   onPressed: () => context.pop(u),
                   child: Row(
@@ -67,7 +67,7 @@ class PartnerPage extends HookConsumerWidget {
       }
     }
 
-    onDeleteUser(User u) {
+    onDeleteUser(UserDto u) {
       return showDialog(
         context: context,
         builder: (BuildContext context) {
@@ -80,7 +80,7 @@ class PartnerPage extends HookConsumerWidget {
       );
     }
 
-    buildUserList(List<User> users) {
+    buildUserList(List<UserDto> users) {
       return Column(
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
diff --git a/mobile/lib/pages/library/partner/partner_detail.page.dart b/mobile/lib/pages/library/partner/partner_detail.page.dart
index f018726fe2..618d31affa 100644
--- a/mobile/lib/pages/library/partner/partner_detail.page.dart
+++ b/mobile/lib/pages/library/partner/partner_detail.page.dart
@@ -2,11 +2,11 @@ import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/providers/asset.provider.dart';
 import 'package:immich_mobile/providers/multiselect.provider.dart';
 import 'package:immich_mobile/providers/partner.provider.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
-import 'package:immich_mobile/providers/asset.provider.dart';
 import 'package:immich_mobile/providers/timeline.provider.dart';
 import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
 import 'package:immich_mobile/widgets/common/immich_toast.dart';
@@ -15,7 +15,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart';
 class PartnerDetailPage extends HookConsumerWidget {
   const PartnerDetailPage({super.key, required this.partner});
 
-  final User partner;
+  final UserDto partner;
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
@@ -111,7 +111,7 @@ class PartnerDetailPage extends HookConsumerWidget {
             ),
           ),
         ),
-        renderListProvider: singleUserTimelineProvider(partner.isarId),
+        renderListProvider: singleUserTimelineProvider(partner.id),
         onRefresh: () => ref.read(assetProvider.notifier).getAllAsset(),
         deleteEnabled: false,
         favoriteEnabled: false,
diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart
index b3bfa366f2..c9211e984d 100644
--- a/mobile/lib/pages/photos/photos.page.dart
+++ b/mobile/lib/pages/photos/photos.page.dart
@@ -7,16 +7,16 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/providers/album/album.provider.dart';
-import 'package:immich_mobile/providers/multiselect.provider.dart';
-import 'package:immich_mobile/providers/timeline.provider.dart';
-import 'package:immich_mobile/widgets/memories/memory_lane.dart';
 import 'package:immich_mobile/providers/asset.provider.dart';
+import 'package:immich_mobile/providers/multiselect.provider.dart';
 import 'package:immich_mobile/providers/server_info.provider.dart';
+import 'package:immich_mobile/providers/timeline.provider.dart';
 import 'package:immich_mobile/providers/user.provider.dart';
 import 'package:immich_mobile/providers/websocket.provider.dart';
 import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
 import 'package:immich_mobile/widgets/common/immich_app_bar.dart';
 import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
+import 'package:immich_mobile/widgets/memories/memory_lane.dart';
 
 @RoutePage()
 class PhotosPage extends HookConsumerWidget {
@@ -110,7 +110,7 @@ class PhotosPage extends HookConsumerWidget {
               : const SizedBox(),
           renderListProvider: timelineUsers.length > 1
               ? multiUsersTimelineProvider(timelineUsers)
-              : singleUserTimelineProvider(currentUser?.isarId),
+              : singleUserTimelineProvider(currentUser?.id),
           buildLoadingIndicator: buildLoadingIndicator,
           onRefresh: refreshAssets,
           stackEnabled: true,
diff --git a/mobile/lib/providers/album/album.provider.dart b/mobile/lib/providers/album/album.provider.dart
index a2d7db68ec..39f5af7344 100644
--- a/mobile/lib/providers/album/album.provider.dart
+++ b/mobile/lib/providers/album/album.provider.dart
@@ -2,11 +2,11 @@ import 'dart:async';
 
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/enums.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/entities/album.entity.dart';
+import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/models/albums/album_search.model.dart';
 import 'package:immich_mobile/services/album.service.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/album.entity.dart';
 
 final isRefreshingRemoteAlbumProvider = StateProvider<bool>((ref) => false);
 
@@ -88,7 +88,7 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
     await albumService.addUsers(album, userIds);
   }
 
-  Future<bool> removeUser(Album album, User user) async {
+  Future<bool> removeUser(Album album, UserDto user) async {
     final isRemoved = await albumService.removeUser(album, user);
 
     if (isRemoved && album.sharedUsers.isEmpty) {
diff --git a/mobile/lib/providers/album/suggested_shared_users.provider.dart b/mobile/lib/providers/album/suggested_shared_users.provider.dart
index fe8a1fccce..75b33a9fbe 100644
--- a/mobile/lib/providers/album/suggested_shared_users.provider.dart
+++ b/mobile/lib/providers/album/suggested_shared_users.provider.dart
@@ -1,9 +1,14 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/providers/user.provider.dart';
 import 'package:immich_mobile/services/user.service.dart';
 
-final otherUsersProvider = FutureProvider.autoDispose<List<User>>((ref) {
+final otherUsersProvider =
+    FutureProvider.autoDispose<List<UserDto>>((ref) async {
   UserService userService = ref.watch(userServiceProvider);
+  final currentUser = ref.watch(currentUserProvider);
 
-  return userService.getUsers();
+  final allUsers = await userService.getAll();
+  allUsers.removeWhere((u) => currentUser?.id == u.id);
+  return allUsers;
 });
diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart
index e2939e89ce..2a140911b0 100644
--- a/mobile/lib/providers/auth.provider.dart
+++ b/mobile/lib/providers/auth.provider.dart
@@ -2,8 +2,9 @@ import 'package:flutter/foundation.dart';
 import 'package:flutter_udid/flutter_udid.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
 import 'package:immich_mobile/models/auth/auth_state.model.dart';
 import 'package:immich_mobile/models/auth/login_response.model.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
@@ -105,7 +106,7 @@ class AuthNotifier extends StateNotifier<AuthState> {
     String deviceId =
         Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid;
 
-    User? user = Store.tryGet(StoreKey.currentUser);
+    UserDto? user = Store.tryGet(StoreKey.currentUser);
 
     UserAdminResponseDto? userResponse;
     UserPreferencesResponseDto? userPreferences;
@@ -141,18 +142,18 @@ class AuthNotifier extends StateNotifier<AuthState> {
 
     // If the user information is successfully retrieved, update the store
     // Due to the flow of the code, this will always happen on first login
-    if (userResponse != null) {
+    if (userResponse == null) {
+      _log.severe("Unable to get user information from the server.");
+    } else {
       await Store.put(StoreKey.deviceId, deviceId);
       await Store.put(StoreKey.deviceIdHash, fastHash(deviceId));
       await Store.put(
         StoreKey.currentUser,
-        User.fromUserDto(userResponse, userPreferences),
+        UserConverter.fromAdminDto(userResponse, userPreferences),
       );
       await Store.put(StoreKey.accessToken, accessToken);
 
-      user = User.fromUserDto(userResponse, userPreferences);
-    } else {
-      _log.severe("Unable to get user information from the server.");
+      user = UserConverter.fromAdminDto(userResponse, userPreferences);
     }
 
     // If the user is null, the login was not successful
@@ -163,7 +164,7 @@ class AuthNotifier extends StateNotifier<AuthState> {
 
     state = state.copyWith(
       isAuthenticated: true,
-      userId: user.id,
+      userId: user.uid,
       userEmail: user.email,
       name: user.name,
       profileImagePath: user.profileImagePath,
diff --git a/mobile/lib/providers/infrastructure/store.provider.dart b/mobile/lib/providers/infrastructure/store.provider.dart
index 2712208e76..c7f0c04a4f 100644
--- a/mobile/lib/providers/infrastructure/store.provider.dart
+++ b/mobile/lib/providers/infrastructure/store.provider.dart
@@ -1,11 +1,15 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/interfaces/store.interface.dart';
+import 'package:immich_mobile/domain/services/store.service.dart';
 import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
 import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
 import 'package:riverpod_annotation/riverpod_annotation.dart';
 
 part 'store.provider.g.dart';
 
-@riverpod
+@Riverpod(keepAlive: true)
 IStoreRepository storeRepository(Ref ref) =>
     IsarStoreRepository(ref.watch(isarProvider));
+
+@Riverpod(keepAlive: true)
+StoreService storeService(Ref _) => StoreService.I;
diff --git a/mobile/lib/providers/infrastructure/store.provider.g.dart b/mobile/lib/providers/infrastructure/store.provider.g.dart
index ebf1804704..ffdcd291b6 100644
--- a/mobile/lib/providers/infrastructure/store.provider.g.dart
+++ b/mobile/lib/providers/infrastructure/store.provider.g.dart
@@ -6,11 +6,11 @@ part of 'store.provider.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$storeRepositoryHash() => r'9f378b96e552151fa14a8c8ce2c30a5f38f436ed';
+String _$storeRepositoryHash() => r'99d24875d30c5e86b1c6caa352a0026167114e62';
 
 /// See also [storeRepository].
 @ProviderFor(storeRepository)
-final storeRepositoryProvider = AutoDisposeProvider<IStoreRepository>.internal(
+final storeRepositoryProvider = Provider<IStoreRepository>.internal(
   storeRepository,
   name: r'storeRepositoryProvider',
   debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
@@ -22,6 +22,22 @@ final storeRepositoryProvider = AutoDisposeProvider<IStoreRepository>.internal(
 
 @Deprecated('Will be removed in 3.0. Use Ref instead')
 // ignore: unused_element
-typedef StoreRepositoryRef = AutoDisposeProviderRef<IStoreRepository>;
+typedef StoreRepositoryRef = ProviderRef<IStoreRepository>;
+String _$storeServiceHash() => r'250e10497c42df360e9e1f9a618d0b19c1b5b0a0';
+
+/// See also [storeService].
+@ProviderFor(storeService)
+final storeServiceProvider = Provider<StoreService>.internal(
+  storeService,
+  name: r'storeServiceProvider',
+  debugGetCreateSourceHash:
+      const bool.fromEnvironment('dart.vm.product') ? null : _$storeServiceHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+@Deprecated('Will be removed in 3.0. Use Ref instead')
+// ignore: unused_element
+typedef StoreServiceRef = ProviderRef<StoreService>;
 // ignore_for_file: type=lint
 // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
diff --git a/mobile/lib/providers/infrastructure/user.provider.dart b/mobile/lib/providers/infrastructure/user.provider.dart
new file mode 100644
index 0000000000..6070cc6c35
--- /dev/null
+++ b/mobile/lib/providers/infrastructure/user.provider.dart
@@ -0,0 +1,11 @@
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
+import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
+import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'user.provider.g.dart';
+
+@Riverpod(keepAlive: true)
+IUserRepository userRepository(Ref ref) =>
+    IsarUserRepository(ref.watch(isarProvider));
diff --git a/mobile/lib/providers/infrastructure/user.provider.g.dart b/mobile/lib/providers/infrastructure/user.provider.g.dart
new file mode 100644
index 0000000000..4ff3481412
--- /dev/null
+++ b/mobile/lib/providers/infrastructure/user.provider.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'user.provider.dart';
+
+// **************************************************************************
+// RiverpodGenerator
+// **************************************************************************
+
+String _$userRepositoryHash() => r'1a2ac726bcc44397dcaecf449084fefd336696d4';
+
+/// See also [userRepository].
+@ProviderFor(userRepository)
+final userRepositoryProvider = Provider<IUserRepository>.internal(
+  userRepository,
+  name: r'userRepositoryProvider',
+  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
+      ? null
+      : _$userRepositoryHash,
+  dependencies: null,
+  allTransitiveDependencies: null,
+);
+
+@Deprecated('Will be removed in 3.0. Use Ref instead')
+// ignore: unused_element
+typedef UserRepositoryRef = ProviderRef<IUserRepository>;
+// ignore_for_file: type=lint
+// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
diff --git a/mobile/lib/providers/partner.provider.dart b/mobile/lib/providers/partner.provider.dart
index 282e779432..f210c7fe3f 100644
--- a/mobile/lib/providers/partner.provider.dart
+++ b/mobile/lib/providers/partner.provider.dart
@@ -2,16 +2,16 @@ import 'dart:async';
 
 import 'package:collection/collection.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/providers/album/suggested_shared_users.provider.dart';
 import 'package:immich_mobile/services/partner.service.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 
-class PartnerSharedWithNotifier extends StateNotifier<List<User>> {
+class PartnerSharedWithNotifier extends StateNotifier<List<UserDto>> {
   final PartnerService _partnerService;
-  late final StreamSubscription<List<User>> streamSub;
+  late final StreamSubscription<List<UserDto>> streamSub;
 
   PartnerSharedWithNotifier(this._partnerService) : super([]) {
-    Function eq = const ListEquality<User>().equals;
+    Function eq = const ListEquality<UserDto>().equals;
     _partnerService.getSharedWith().then((partners) {
       if (!eq(state, partners)) {
         state = partners;
@@ -25,7 +25,7 @@ class PartnerSharedWithNotifier extends StateNotifier<List<User>> {
     });
   }
 
-  Future<bool> updatePartner(User partner, {required bool inTimeline}) {
+  Future<bool> updatePartner(UserDto partner, {required bool inTimeline}) {
     return _partnerService.updatePartner(partner, inTimeline: inTimeline);
   }
 
@@ -39,18 +39,18 @@ class PartnerSharedWithNotifier extends StateNotifier<List<User>> {
 }
 
 final partnerSharedWithProvider =
-    StateNotifierProvider<PartnerSharedWithNotifier, List<User>>((ref) {
+    StateNotifierProvider<PartnerSharedWithNotifier, List<UserDto>>((ref) {
   return PartnerSharedWithNotifier(
     ref.watch(partnerServiceProvider),
   );
 });
 
-class PartnerSharedByNotifier extends StateNotifier<List<User>> {
+class PartnerSharedByNotifier extends StateNotifier<List<UserDto>> {
   final PartnerService _partnerService;
-  late final StreamSubscription<List<User>> streamSub;
+  late final StreamSubscription<List<UserDto>> streamSub;
 
   PartnerSharedByNotifier(this._partnerService) : super([]) {
-    Function eq = const ListEquality<User>().equals;
+    Function eq = const ListEquality<UserDto>().equals;
     _partnerService.getSharedBy().then((partners) {
       if (!eq(state, partners)) {
         state = partners;
@@ -74,15 +74,15 @@ class PartnerSharedByNotifier extends StateNotifier<List<User>> {
 }
 
 final partnerSharedByProvider =
-    StateNotifierProvider<PartnerSharedByNotifier, List<User>>((ref) {
+    StateNotifierProvider<PartnerSharedByNotifier, List<UserDto>>((ref) {
   return PartnerSharedByNotifier(ref.watch(partnerServiceProvider));
 });
 
 final partnerAvailableProvider =
-    FutureProvider.autoDispose<List<User>>((ref) async {
+    FutureProvider.autoDispose<List<UserDto>>((ref) async {
   final otherUsers = await ref.watch(otherUsersProvider.future);
   final currentPartners = ref.watch(partnerSharedByProvider);
-  final available = Set<User>.of(otherUsers);
+  final available = Set<UserDto>.of(otherUsers);
   available.removeAll(currentPartners);
   return available.toList();
 });
diff --git a/mobile/lib/providers/user.provider.dart b/mobile/lib/providers/user.provider.dart
index 0a1bc0275a..fb574fa99a 100644
--- a/mobile/lib/providers/user.provider.dart
+++ b/mobile/lib/providers/user.provider.dart
@@ -2,13 +2,14 @@ import 'dart:async';
 
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
 import 'package:immich_mobile/services/api.service.dart';
 import 'package:immich_mobile/services/timeline.service.dart';
 
-class CurrentUserProvider extends StateNotifier<User?> {
+class CurrentUserProvider extends StateNotifier<UserDto?> {
   CurrentUserProvider(this._apiService) : super(null) {
     state = Store.tryGet(StoreKey.currentUser);
     streamSub =
@@ -16,7 +17,7 @@ class CurrentUserProvider extends StateNotifier<User?> {
   }
 
   final ApiService _apiService;
-  late final StreamSubscription<User?> streamSub;
+  late final StreamSubscription<UserDto?> streamSub;
 
   refresh() async {
     try {
@@ -25,7 +26,7 @@ class CurrentUserProvider extends StateNotifier<User?> {
       if (user != null) {
         await Store.put(
           StoreKey.currentUser,
-          User.fromUserDto(user, userPreferences),
+          UserConverter.fromAdminDto(user, userPreferences),
         );
       }
     } catch (_) {}
@@ -39,7 +40,7 @@ class CurrentUserProvider extends StateNotifier<User?> {
 }
 
 final currentUserProvider =
-    StateNotifierProvider<CurrentUserProvider, User?>((ref) {
+    StateNotifierProvider<CurrentUserProvider, UserDto?>((ref) {
   return CurrentUserProvider(
     ref.watch(apiServiceProvider),
   );
diff --git a/mobile/lib/repositories/activity_api.repository.dart b/mobile/lib/repositories/activity_api.repository.dart
index 8da3759709..868415caf9 100644
--- a/mobile/lib/repositories/activity_api.repository.dart
+++ b/mobile/lib/repositories/activity_api.repository.dart
@@ -1,5 +1,5 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
 import 'package:immich_mobile/interfaces/activity_api.interface.dart';
 import 'package:immich_mobile/models/activities/activity.model.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
@@ -60,7 +60,7 @@ class ActivityApiRepository extends ApiRepository
         type: dto.type == ReactionType.comment
             ? ActivityType.comment
             : ActivityType.like,
-        user: User.fromSimpleUserDto(dto.user),
+        user: UserConverter.fromSimpleUserDto(dto.user),
         assetId: dto.assetId,
         comment: dto.comment,
       );
diff --git a/mobile/lib/repositories/album.repository.dart b/mobile/lib/repositories/album.repository.dart
index 1d2df89579..a6657f7637 100644
--- a/mobile/lib/repositories/album.repository.dart
+++ b/mobile/lib/repositories/album.repository.dart
@@ -1,9 +1,11 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.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:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
+    as entity;
 import 'package:immich_mobile/interfaces/album.interface.dart';
 import 'package:immich_mobile/models/albums/album_search.model.dart';
 import 'package:immich_mobile/providers/db.provider.dart';
@@ -43,11 +45,11 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
     }
     if (owner == true) {
       query = query.owner(
-        (q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).isarId),
+        (q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).id),
       );
     } else if (owner == false) {
       query = query.owner(
-        (q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).isarId),
+        (q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).id),
       );
     }
     if (remote == true) {
@@ -100,8 +102,9 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
   Future<Album?> get(int id) => db.albums.get(id);
 
   @override
-  Future<void> removeUsers(Album album, List<User> users) =>
-      txn(() => album.sharedUsers.update(unlink: users));
+  Future<void> removeUsers(Album album, List<UserDto> users) => txn(
+        () => album.sharedUsers.update(unlink: users.map(entity.User.fromDto)),
+      );
 
   @override
   Future<void> addAssets(Album album, List<Asset> assets) =>
@@ -121,8 +124,8 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
   }
 
   @override
-  Future<void> addUsers(Album album, List<User> users) =>
-      txn(() => album.sharedUsers.update(link: users));
+  Future<void> addUsers(Album album, List<UserDto> users) =>
+      txn(() => album.sharedUsers.update(link: users.map(entity.User.fromDto)));
 
   @override
   Future<void> deleteAllLocal() =>
@@ -141,11 +144,11 @@ class AlbumRepository extends DatabaseRepository implements IAlbumRepository {
     switch (filterMode) {
       case QuickFilterMode.sharedWithMe:
         query = query.owner(
-          (q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).isarId),
+          (q) => q.not().isarIdEqualTo(Store.get(StoreKey.currentUser).id),
         );
       case QuickFilterMode.myAlbums:
         query = query.owner(
-          (q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).isarId),
+          (q) => q.isarIdEqualTo(Store.get(StoreKey.currentUser).id),
         );
       case QuickFilterMode.all:
         break;
diff --git a/mobile/lib/repositories/album_api.repository.dart b/mobile/lib/repositories/album_api.repository.dart
index 2438304158..a7bbe452e6 100644
--- a/mobile/lib/repositories/album_api.repository.dart
+++ b/mobile/lib/repositories/album_api.repository.dart
@@ -2,7 +2,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/enums.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
+    as entity;
+import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
 import 'package:immich_mobile/interfaces/album_api.interface.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
 import 'package:immich_mobile/repositories/api.repository.dart';
@@ -164,11 +166,12 @@ class AlbumApiRepository extends ApiRepository implements IAlbumApiRepository {
       sortOrder: dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc,
     );
     album.remoteAssetCount = dto.assetCount;
-    album.owner.value = User.fromSimpleUserDto(dto.owner);
+    album.owner.value =
+        entity.User.fromDto(UserConverter.fromSimpleUserDto(dto.owner));
     album.remoteThumbnailAssetId = dto.albumThumbnailAssetId;
     final users = dto.albumUsers
-        .map((albumUser) => User.fromSimpleUserDto(albumUser.user));
-    album.sharedUsers.addAll(users);
+        .map((albumUser) => UserConverter.fromSimpleUserDto(albumUser.user));
+    album.sharedUsers.addAll(users.map(entity.User.fromDto));
     final assets = dto.assets.map(Asset.remote).toList();
     album.assets.addAll(assets);
     return album;
diff --git a/mobile/lib/repositories/album_media.repository.dart b/mobile/lib/repositories/album_media.repository.dart
index f4f31cf14e..f08322e20a 100644
--- a/mobile/lib/repositories/album_media.repository.dart
+++ b/mobile/lib/repositories/album_media.repository.dart
@@ -3,6 +3,7 @@ import 'package:immich_mobile/domain/models/store.model.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:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/interfaces/album_media.interface.dart';
 import 'package:immich_mobile/repositories/asset_media.repository.dart';
 import 'package:photo_manager/photo_manager.dart' hide AssetType;
@@ -86,7 +87,7 @@ class AlbumMediaRepository implements IAlbumMediaRepository {
       shared: false,
       activityEnabled: false,
     );
-    album.owner.value = Store.get(StoreKey.currentUser);
+    album.owner.value = User.fromDto(Store.get(StoreKey.currentUser));
     album.localId = assetPathEntity.id;
     album.isAll = assetPathEntity.isAll;
     return album;
diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart
index 97d22f3600..0149a8d6c6 100644
--- a/mobile/lib/repositories/asset_media.repository.dart
+++ b/mobile/lib/repositories/asset_media.repository.dart
@@ -24,7 +24,7 @@ class AssetMediaRepository implements IAssetMediaRepository {
     final Asset asset = Asset(
       checksum: "",
       localId: local.id,
-      ownerId: Store.get(StoreKey.currentUser).isarId,
+      ownerId: Store.get(StoreKey.currentUser).id,
       fileCreatedAt: local.createDateTime,
       fileModifiedAt: local.modifiedDateTime,
       updatedAt: local.modifiedDateTime,
diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart
index 18e14865aa..f9e82e1635 100644
--- a/mobile/lib/repositories/auth.repository.dart
+++ b/mobile/lib/repositories/auth.repository.dart
@@ -6,8 +6,8 @@ import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/etag.entity.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/interfaces/auth.interface.dart';
 import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
 import 'package:immich_mobile/providers/db.provider.dart';
diff --git a/mobile/lib/repositories/partner.repository.dart b/mobile/lib/repositories/partner.repository.dart
index cae49fee39..5ea10f98c8 100644
--- a/mobile/lib/repositories/partner.repository.dart
+++ b/mobile/lib/repositories/partner.repository.dart
@@ -1,5 +1,7 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
+    as entity;
 import 'package:immich_mobile/interfaces/partner.interface.dart';
 import 'package:immich_mobile/providers/db.provider.dart';
 import 'package:immich_mobile/repositories/database.repository.dart';
@@ -14,34 +16,40 @@ class PartnerRepository extends DatabaseRepository
   PartnerRepository(super.db);
 
   @override
-  Future<List<User>> getSharedBy() {
-    return db.users
-        .filter()
-        .isPartnerSharedByEqualTo(true)
-        .sortById()
-        .findAll();
+  Future<List<UserDto>> getSharedBy() async {
+    return (await db.users
+            .filter()
+            .isPartnerSharedByEqualTo(true)
+            .sortById()
+            .findAll())
+        .map((u) => u.toDto())
+        .toList();
   }
 
   @override
-  Future<List<User>> getSharedWith() {
-    return db.users
-        .filter()
-        .isPartnerSharedWithEqualTo(true)
-        .sortById()
-        .findAll();
+  Future<List<UserDto>> getSharedWith() async {
+    return (await db.users
+            .filter()
+            .isPartnerSharedWithEqualTo(true)
+            .sortById()
+            .findAll())
+        .map((u) => u.toDto())
+        .toList();
   }
 
   @override
-  Stream<List<User>> watchSharedBy() {
-    return db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch();
+  Stream<List<UserDto>> watchSharedBy() {
+    return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch())
+        .map((users) => users.map((u) => u.toDto()).toList());
   }
 
   @override
-  Stream<List<User>> watchSharedWith() {
-    return db.users
-        .filter()
-        .isPartnerSharedWithEqualTo(true)
-        .sortById()
-        .watch();
+  Stream<List<UserDto>> watchSharedWith() {
+    return (db.users
+            .filter()
+            .isPartnerSharedWithEqualTo(true)
+            .sortById()
+            .watch())
+        .map((users) => users.map((u) => u.toDto()).toList());
   }
 }
diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart
index 1ae16d9d52..367e2a58d7 100644
--- a/mobile/lib/repositories/partner_api.repository.dart
+++ b/mobile/lib/repositories/partner_api.repository.dart
@@ -1,5 +1,6 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
 import 'package:immich_mobile/interfaces/partner_api.interface.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
 import 'package:immich_mobile/repositories/api.repository.dart';
@@ -18,7 +19,7 @@ class PartnerApiRepository extends ApiRepository
   PartnerApiRepository(this._api);
 
   @override
-  Future<List<User>> getAll(Direction direction) async {
+  Future<List<UserDto>> getAll(Direction direction) async {
     final response = await checkNull(
       _api.getPartners(
         direction == Direction.sharedByMe
@@ -26,26 +27,26 @@ class PartnerApiRepository extends ApiRepository
             : PartnerDirection.with_,
       ),
     );
-    return response.map(User.fromPartnerDto).toList();
+    return response.map(UserConverter.fromPartnerDto).toList();
   }
 
   @override
-  Future<User> create(String id) async {
+  Future<UserDto> create(String id) async {
     final dto = await checkNull(_api.createPartner(id));
-    return User.fromPartnerDto(dto);
+    return UserConverter.fromPartnerDto(dto);
   }
 
   @override
   Future<void> delete(String id) => _api.removePartner(id);
 
   @override
-  Future<User> update(String id, {required bool inTimeline}) async {
+  Future<UserDto> update(String id, {required bool inTimeline}) async {
     final dto = await checkNull(
       _api.updatePartner(
         id,
         UpdatePartnerDto(inTimeline: inTimeline),
       ),
     );
-    return User.fromPartnerDto(dto);
+    return UserConverter.fromPartnerDto(dto);
   }
 }
diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart
index 1b9ee8ad37..1b0471059f 100644
--- a/mobile/lib/repositories/timeline.repository.dart
+++ b/mobile/lib/repositories/timeline.repository.dart
@@ -2,7 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/enums.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/interfaces/timeline.interface.dart';
 import 'package:immich_mobile/providers/db.provider.dart';
 import 'package:immich_mobile/repositories/database.repository.dart';
diff --git a/mobile/lib/repositories/user.repository.dart b/mobile/lib/repositories/user.repository.dart
deleted file mode 100644
index ea67b30e0d..0000000000
--- a/mobile/lib/repositories/user.repository.dart
+++ /dev/null
@@ -1,73 +0,0 @@
-import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/domain/models/store.model.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';
-import 'package:immich_mobile/repositories/database.repository.dart';
-import 'package:isar/isar.dart';
-
-final userRepositoryProvider =
-    Provider((ref) => UserRepository(ref.watch(dbProvider)));
-
-class UserRepository extends DatabaseRepository implements IUserRepository {
-  UserRepository(super.db);
-
-  @override
-  Future<List<User>> getByIds(List<String> ids) async =>
-      (await db.users.getAllById(ids)).nonNulls.toList();
-
-  @override
-  Future<User?> get(String id) => db.users.getById(id);
-
-  @override
-  Future<List<User>> getAll({bool self = true, UserSort? sortBy}) {
-    final baseQuery = db.users.where();
-    final int userId = Store.get(StoreKey.currentUser).isarId;
-    final QueryBuilder<User, User, QAfterWhereClause> afterWhere =
-        self ? baseQuery.noOp() : baseQuery.isarIdNotEqualTo(userId);
-    final QueryBuilder<User, User, QAfterSortBy> query = switch (sortBy) {
-      null => afterWhere.noOp(),
-      UserSort.id => afterWhere.sortById(),
-    };
-    return query.findAll();
-  }
-
-  @override
-  Future<User> update(User user) async {
-    await txn(() => db.users.put(user));
-    return user;
-  }
-
-  @override
-  Future<User> me() => Future.value(Store.get(StoreKey.currentUser));
-
-  @override
-  Future<void> deleteById(List<int> ids) => txn(() => db.users.deleteAll(ids));
-
-  @override
-  Future<List<User>> upsertAll(List<User> users) async {
-    await txn(() => db.users.putAll(users));
-    return users;
-  }
-
-  @override
-  Future<List<User>> getAllAccessible() => db.users
-      .filter()
-      .isPartnerSharedWithEqualTo(true)
-      .or()
-      .isarIdEqualTo(Store.get(StoreKey.currentUser).isarId)
-      .findAll();
-
-  @override
-  Future<User?> getByDbId(int id) async {
-    return await db.users.get(id);
-  }
-
-  @override
-  Future<void> clearTable() async {
-    await txn(() async {
-      await db.users.clear();
-    });
-  }
-}
diff --git a/mobile/lib/repositories/user_api.repository.dart b/mobile/lib/repositories/user_api.repository.dart
index 9641c4e0e6..1b72186c7e 100644
--- a/mobile/lib/repositories/user_api.repository.dart
+++ b/mobile/lib/repositories/user_api.repository.dart
@@ -2,7 +2,8 @@ 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/domain/models/user.model.dart';
+import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
 import 'package:immich_mobile/interfaces/user_api.interface.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
 import 'package:immich_mobile/repositories/api.repository.dart';
@@ -20,9 +21,9 @@ class UserApiRepository extends ApiRepository implements IUserApiRepository {
   UserApiRepository(this._api);
 
   @override
-  Future<List<User>> getAll() async {
+  Future<List<UserDto>> getAll() async {
     final dto = await checkNull(_api.searchUsers());
-    return dto.map(User.fromSimpleUserDto).toList();
+    return dto.map(UserConverter.fromSimpleUserDto).toList();
   }
 
   @override
diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart
index cd7a6f6b98..d7edc6fd28 100644
--- a/mobile/lib/routing/router.dart
+++ b/mobile/lib/routing/router.dart
@@ -2,11 +2,10 @@ import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/models/log.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/models/folder/recursive_folder.model.dart';
-import 'package:immich_mobile/pages/library/folder/folder.page.dart';
 import 'package:immich_mobile/models/memories/memory.model.dart';
 import 'package:immich_mobile/models/search/search_filter.model.dart';
 import 'package:immich_mobile/models/shared_link/shared_link.model.dart';
@@ -37,6 +36,7 @@ import 'package:immich_mobile/pages/editing/edit.page.dart';
 import 'package:immich_mobile/pages/editing/filter.page.dart';
 import 'package:immich_mobile/pages/library/archive.page.dart';
 import 'package:immich_mobile/pages/library/favorite.page.dart';
+import 'package:immich_mobile/pages/library/folder/folder.page.dart';
 import 'package:immich_mobile/pages/library/library.page.dart';
 import 'package:immich_mobile/pages/library/local_albums.page.dart';
 import 'package:immich_mobile/pages/library/partner/partner.page.dart';
diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart
index f2f169247e..a78371e05e 100644
--- a/mobile/lib/routing/router.gr.dart
+++ b/mobile/lib/routing/router.gr.dart
@@ -1162,7 +1162,7 @@ class NativeVideoViewerRouteArgs {
 class PartnerDetailRoute extends PageRouteInfo<PartnerDetailRouteArgs> {
   PartnerDetailRoute({
     Key? key,
-    required User partner,
+    required UserDto partner,
     List<PageRouteInfo>? children,
   }) : super(
           PartnerDetailRoute.name,
@@ -1195,7 +1195,7 @@ class PartnerDetailRouteArgs {
 
   final Key? key;
 
-  final User partner;
+  final UserDto partner;
 
   @override
   String toString() {
diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart
index b6a845a0b3..edbfe6da4c 100644
--- a/mobile/lib/routing/tab_navigation_observer.dart
+++ b/mobile/lib/routing/tab_navigation_observer.dart
@@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/utils/user.converter.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
 import 'package:immich_mobile/providers/asset.provider.dart';
 import 'package:immich_mobile/providers/memory.provider.dart';
@@ -39,7 +39,7 @@ class TabNavigationObserver extends AutoRouterObserver {
 
         await Store.put(
           StoreKey.currentUser,
-          User.fromUserDto(userResponseDto, userPreferences),
+          UserConverter.fromAdminDto(userResponseDto, userPreferences),
         );
         ref.read(serverInfoProvider.notifier).getServerVersion();
       } catch (e) {
diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart
index 3a44ca7286..057bec307c 100644
--- a/mobile/lib/services/album.service.dart
+++ b/mobile/lib/services/album.service.dart
@@ -7,11 +7,13 @@ import 'package:flutter/foundation.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/constants/enums.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/backup_album.entity.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart'
+    as entity;
 import 'package:immich_mobile/interfaces/album.interface.dart';
 import 'package:immich_mobile/interfaces/album_api.interface.dart';
 import 'package:immich_mobile/interfaces/album_media.interface.dart';
@@ -202,12 +204,12 @@ class AlbumService {
   Future<Album?> createAlbum(
     String albumName,
     Iterable<Asset> assets, [
-    Iterable<User> sharedUsers = const [],
+    Iterable<UserDto> sharedUsers = const [],
   ]) async {
     final Album album = await _albumApiRepository.create(
       albumName,
       assetIds: assets.map((asset) => asset.remoteId!),
-      sharedUserIds: sharedUsers.map((user) => user.id),
+      sharedUserIds: sharedUsers.map((user) => user.uid),
     );
     await _entityService.fillAlbumWithDatabaseEntities(album);
     return _albumRepository.create(album);
@@ -294,7 +296,7 @@ class AlbumService {
 
   Future<bool> deleteAlbum(Album album) async {
     try {
-      final userId = Store.get(StoreKey.currentUser).isarId;
+      final userId = Store.get(StoreKey.currentUser).id;
       if (album.owner.value?.isarId == userId) {
         await _albumApiRepository.delete(album.remoteId!);
       }
@@ -356,15 +358,15 @@ class AlbumService {
 
   Future<bool> removeUser(
     Album album,
-    User user,
+    UserDto user,
   ) async {
     try {
       await _albumApiRepository.removeUser(
         album.remoteId!,
-        userId: user.id,
+        userId: user.uid,
       );
 
-      album.sharedUsers.remove(user);
+      album.sharedUsers.remove(entity.User.fromDto(user));
       await _albumRepository.removeUsers(album, [user]);
       final a = await _albumRepository.get(album.id);
       // trigger watcher
@@ -388,7 +390,10 @@ class AlbumService {
       album.sharedUsers.addAll(updatedAlbum.remoteUsers);
       album.shared = true;
 
-      await _albumRepository.addUsers(album, album.sharedUsers.toList());
+      await _albumRepository.addUsers(
+        album,
+        album.sharedUsers.map((u) => u.toDto()).toList(),
+      );
       await _albumRepository.update(album);
 
       return true;
diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart
index 815962efac..f61fdfbaeb 100644
--- a/mobile/lib/services/asset.service.dart
+++ b/mobile/lib/services/asset.service.dart
@@ -5,24 +5,27 @@ import 'package:collection/collection.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
+import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/domain/services/store.service.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/backup_album.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/interfaces/asset.interface.dart';
 import 'package:immich_mobile/interfaces/asset_api.interface.dart';
 import 'package:immich_mobile/interfaces/asset_media.interface.dart';
 import 'package:immich_mobile/interfaces/backup_album.interface.dart';
 import 'package:immich_mobile/interfaces/etag.interface.dart';
-import 'package:immich_mobile/interfaces/user.interface.dart';
 import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
 import 'package:immich_mobile/providers/api.provider.dart';
 import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
+import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
+import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
 import 'package:immich_mobile/repositories/asset.repository.dart';
 import 'package:immich_mobile/repositories/asset_api.repository.dart';
 import 'package:immich_mobile/repositories/asset_media.repository.dart';
 import 'package:immich_mobile/repositories/backup.repository.dart';
 import 'package:immich_mobile/repositories/etag.repository.dart';
-import 'package:immich_mobile/repositories/user.repository.dart';
 import 'package:immich_mobile/services/album.service.dart';
 import 'package:immich_mobile/services/api.service.dart';
 import 'package:immich_mobile/services/backup.service.dart';
@@ -45,6 +48,7 @@ final assetServiceProvider = Provider(
     ref.watch(userServiceProvider),
     ref.watch(backupServiceProvider),
     ref.watch(albumServiceProvider),
+    ref.watch(storeServiceProvider),
     ref.watch(assetMediaRepositoryProvider),
   ),
 );
@@ -61,6 +65,7 @@ class AssetService {
   final UserService _userService;
   final BackupService _backupService;
   final AlbumService _albumService;
+  final StoreService _storeService;
   final IAssetMediaRepository _assetMediaRepository;
   final log = Logger('AssetService');
 
@@ -76,6 +81,7 @@ class AssetService {
     this._userService,
     this._backupService,
     this._albumService,
+    this._storeService,
     this._assetMediaRepository,
   );
 
@@ -83,9 +89,9 @@ class AssetService {
   /// required. Returns `true` if there were any changes.
   Future<bool> refreshRemoteAssets() async {
     final syncedUserIds = await _etagRepository.getAllIds();
-    final List<User> syncedUsers = syncedUserIds.isEmpty
+    final List<UserDto> syncedUsers = syncedUserIds.isEmpty
         ? []
-        : await _userRepository.getByIds(syncedUserIds);
+        : (await _userRepository.getByUserIds(syncedUserIds)).nonNulls.toList();
     final Stopwatch sw = Stopwatch()..start();
     final bool changes = await _syncService.syncRemoteAssetsToDb(
       users: syncedUsers,
@@ -99,10 +105,10 @@ class AssetService {
 
   /// Returns `(null, null)` if changes are invalid -> requires full sync
   Future<(List<Asset>? toUpsert, List<String>? toDelete)>
-      _getRemoteAssetChanges(List<User> users, DateTime since) async {
+      _getRemoteAssetChanges(List<UserDto> users, DateTime since) async {
     final dto = AssetDeltaSyncDto(
       updatedAfter: since,
-      userIds: users.map((e) => e.id).toList(),
+      userIds: users.map((e) => e.uid).toList(),
     );
     final changes = await _apiService.syncApi.getDeltaSync(dto);
     return changes == null || changes.needsFullSync
@@ -132,7 +138,7 @@ class AssetService {
   }
 
   /// Returns `null` if the server state did not change, else list of assets
-  Future<List<Asset>?> _getRemoteAssets(User user, DateTime until) async {
+  Future<List<Asset>?> _getRemoteAssets(UserDto user, DateTime until) async {
     const int chunkSize = 10000;
     try {
       final List<Asset> allAssets = [];
@@ -143,7 +149,7 @@ class AssetService {
           limit: chunkSize,
           updatedUntil: until,
           lastId: lastId,
-          userId: user.id,
+          userId: user.uid,
         );
         log.fine("Requesting $chunkSize assets from $lastId");
         final List<AssetResponseDto>? assets =
@@ -314,9 +320,9 @@ class AssetService {
       );
 
       await refreshRemoteAssets();
-      final owner = await _userRepository.me();
+      final owner = _storeService.get(StoreKey.currentUser);
       final remoteAssets = await _assetRepository.getAll(
-        ownerId: owner.isarId,
+        ownerId: owner.id,
         state: AssetState.merged,
       );
 
@@ -519,13 +525,13 @@ class AssetService {
     return _assetRepository.watchAsset(id, fireImmediately: fireImmediately);
   }
 
-  Future<List<Asset>> getRecentlyAddedAssets() async {
-    final me = await _userRepository.me();
-    return _assetRepository.getRecentlyAddedAssets(me.isarId);
+  Future<List<Asset>> getRecentlyAddedAssets() {
+    final me = _storeService.get(StoreKey.currentUser);
+    return _assetRepository.getRecentlyAddedAssets(me.id);
   }
 
-  Future<List<Asset>> getMotionAssets() async {
-    final me = await _userRepository.me();
-    return _assetRepository.getMotionAssets(me.isarId);
+  Future<List<Asset>> getMotionAssets() {
+    final me = _storeService.get(StoreKey.currentUser);
+    return _assetRepository.getMotionAssets(me.id);
   }
 }
diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart
index fe483384e5..ad281e4a0b 100644
--- a/mobile/lib/services/background.service.dart
+++ b/mobile/lib/services/background.service.dart
@@ -12,11 +12,15 @@ import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/services/store.service.dart';
 import 'package:immich_mobile/entities/backup_album.entity.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
 import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart';
+import 'package:immich_mobile/infrastructure/repositories/user.repository.dart';
 import 'package:immich_mobile/interfaces/backup_album.interface.dart';
+import 'package:immich_mobile/interfaces/partner.interface.dart';
 import 'package:immich_mobile/models/backup/backup_candidate.model.dart';
 import 'package:immich_mobile/models/backup/current_upload_asset.model.dart';
 import 'package:immich_mobile/models/backup/error_upload_asset.model.dart';
@@ -32,9 +36,9 @@ import 'package:immich_mobile/repositories/backup.repository.dart';
 import 'package:immich_mobile/repositories/etag.repository.dart';
 import 'package:immich_mobile/repositories/file_media.repository.dart';
 import 'package:immich_mobile/repositories/network.repository.dart';
+import 'package:immich_mobile/repositories/partner.repository.dart';
 import 'package:immich_mobile/repositories/partner_api.repository.dart';
 import 'package:immich_mobile/repositories/permission.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/api.service.dart';
@@ -385,7 +389,7 @@ class BackgroundService {
     AlbumMediaRepository albumMediaRepository = AlbumMediaRepository();
     FileMediaRepository fileMediaRepository = FileMediaRepository();
     AssetMediaRepository assetMediaRepository = AssetMediaRepository();
-    UserRepository userRepository = UserRepository(db);
+    IUserRepository userRepository = IsarUserRepository(db);
     UserApiRepository userApiRepository =
         UserApiRepository(apiService.usersApi);
     AlbumApiRepository albumApiRepository =
@@ -396,6 +400,7 @@ class BackgroundService {
         HashService(assetRepository, this, albumMediaRepository);
     EntityService entityService =
         EntityService(assetRepository, userRepository);
+    IPartnerRepository partnerRepository = PartnerRepository(db);
     SyncService syncSerive = SyncService(
       hashService,
       entityService,
@@ -404,7 +409,9 @@ class BackgroundService {
       albumRepository,
       assetRepository,
       exifInfoRepository,
+      partnerRepository,
       userRepository,
+      StoreService.I,
       eTagRepository,
     );
     UserService userService = UserService(
diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart
index c2e93a678a..e4d5ab4afd 100644
--- a/mobile/lib/services/backup_verification.service.dart
+++ b/mobile/lib/services/backup_verification.service.dart
@@ -34,7 +34,7 @@ class BackupVerificationService {
 
   /// Returns at most [limit] assets that were backed up without exif
   Future<List<Asset>> findWronglyBackedUpAssets({int limit = 100}) async {
-    final owner = Store.get(StoreKey.currentUser).isarId;
+    final owner = Store.get(StoreKey.currentUser).id;
     final List<Asset> onlyLocal = await _assetRepository.getAll(
       ownerId: owner,
       state: AssetState.local,
diff --git a/mobile/lib/services/entity.service.dart b/mobile/lib/services/entity.service.dart
index ddbe77f8c9..9e61366b94 100644
--- a/mobile/lib/services/entity.service.dart
+++ b/mobile/lib/services/entity.service.dart
@@ -1,9 +1,10 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/interfaces/asset.interface.dart';
-import 'package:immich_mobile/interfaces/user.interface.dart';
+import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
 import 'package:immich_mobile/repositories/asset.repository.dart';
-import 'package:immich_mobile/repositories/user.repository.dart';
 
 class EntityService {
   final IAssetRepository _assetRepository;
@@ -17,7 +18,8 @@ class EntityService {
     final ownerId = album.ownerId;
     if (ownerId != null) {
       // replace owner with user from database
-      album.owner.value = await _userRepository.get(ownerId);
+      final user = await _userRepository.getByUserId(ownerId);
+      album.owner.value = user == null ? null : User.fromDto(user);
     }
     final thumbnailAssetId =
         album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId;
@@ -29,9 +31,9 @@ class EntityService {
     if (album.remoteUsers.isNotEmpty) {
       // replace all users with users from database
       final users = await _userRepository
-          .getByIds(album.remoteUsers.map((user) => user.id).toList());
+          .getByUserIds(album.remoteUsers.map((user) => user.id).toList());
       album.sharedUsers.clear();
-      album.sharedUsers.addAll(users);
+      album.sharedUsers.addAll(users.nonNulls.map(User.fromDto));
       album.shared = true;
     }
     if (album.remoteAssets.isNotEmpty) {
diff --git a/mobile/lib/services/partner.service.dart b/mobile/lib/services/partner.service.dart
index 6bd429b51d..cc3631a0b1 100644
--- a/mobile/lib/services/partner.service.dart
+++ b/mobile/lib/services/partner.service.dart
@@ -1,11 +1,11 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/interfaces/partner.interface.dart';
 import 'package:immich_mobile/interfaces/partner_api.interface.dart';
-import 'package:immich_mobile/interfaces/user.interface.dart';
+import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
 import 'package:immich_mobile/repositories/partner.repository.dart';
 import 'package:immich_mobile/repositories/partner_api.repository.dart';
-import 'package:immich_mobile/repositories/user.repository.dart';
 import 'package:logging/logging.dart';
 
 final partnerServiceProvider = Provider(
@@ -28,57 +28,58 @@ class PartnerService {
     this._partnerRepository,
   );
 
-  Future<List<User>> getSharedWith() async {
+  Future<List<UserDto>> getSharedWith() async {
     return _partnerRepository.getSharedWith();
   }
 
-  Future<List<User>> getSharedBy() async {
+  Future<List<UserDto>> getSharedBy() async {
     return _partnerRepository.getSharedBy();
   }
 
-  Stream<List<User>> watchSharedWith() {
+  Stream<List<UserDto>> watchSharedWith() {
     return _partnerRepository.watchSharedWith();
   }
 
-  Stream<List<User>> watchSharedBy() {
+  Stream<List<UserDto>> watchSharedBy() {
     return _partnerRepository.watchSharedBy();
   }
 
-  Future<bool> removePartner(User partner) async {
+  Future<bool> removePartner(UserDto partner) async {
     try {
-      await _partnerApiRepository.delete(partner.id);
-      partner.isPartnerSharedBy = false;
-      await _userRepository.update(partner);
+      await _partnerApiRepository.delete(partner.uid);
+      await _userRepository.update(partner.copyWith(isPartnerSharedBy: false));
     } catch (e) {
-      _log.warning("Failed to remove partner ${partner.id}", e);
+      _log.warning("Failed to remove partner ${partner.uid}", e);
       return false;
     }
     return true;
   }
 
-  Future<bool> addPartner(User partner) async {
+  Future<bool> addPartner(UserDto partner) async {
     try {
-      await _partnerApiRepository.create(partner.id);
-      partner.isPartnerSharedBy = true;
-      await _userRepository.update(partner);
+      await _partnerApiRepository.create(partner.uid);
+      await _userRepository.update(partner.copyWith(isPartnerSharedBy: true));
       return true;
     } catch (e) {
-      _log.warning("Failed to add partner ${partner.id}", e);
+      _log.warning("Failed to add partner ${partner.uid}", e);
     }
     return false;
   }
 
-  Future<bool> updatePartner(User partner, {required bool inTimeline}) async {
+  Future<bool> updatePartner(
+    UserDto partner, {
+    required bool inTimeline,
+  }) async {
     try {
       final dto = await _partnerApiRepository.update(
-        partner.id,
+        partner.uid,
         inTimeline: inTimeline,
       );
-      partner.inTimeline = dto.inTimeline;
-      await _userRepository.update(partner);
+      await _userRepository
+          .update(partner.copyWith(inTimeline: dto.inTimeline));
       return true;
     } catch (e) {
-      _log.warning("Failed to update partner ${partner.id}", e);
+      _log.warning("Failed to update partner ${partner.uid}", e);
     }
     return false;
   }
diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart
index b937dde320..b3edb17c89 100644
--- a/mobile/lib/services/sync.service.dart
+++ b/mobile/lib/services/sync.service.dart
@@ -3,24 +3,29 @@ import 'dart:async';
 import 'package:collection/collection.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
+import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
+import 'package:immich_mobile/domain/services/store.service.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/etag.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/extensions/collection_extensions.dart';
 import 'package:immich_mobile/interfaces/album.interface.dart';
 import 'package:immich_mobile/interfaces/album_api.interface.dart';
 import 'package:immich_mobile/interfaces/album_media.interface.dart';
 import 'package:immich_mobile/interfaces/asset.interface.dart';
 import 'package:immich_mobile/interfaces/etag.interface.dart';
-import 'package:immich_mobile/interfaces/user.interface.dart';
+import 'package:immich_mobile/interfaces/partner.interface.dart';
 import 'package:immich_mobile/providers/infrastructure/exif.provider.dart';
+import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
+import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
 import 'package:immich_mobile/repositories/album.repository.dart';
 import 'package:immich_mobile/repositories/album_api.repository.dart';
 import 'package:immich_mobile/repositories/album_media.repository.dart';
 import 'package:immich_mobile/repositories/asset.repository.dart';
 import 'package:immich_mobile/repositories/etag.repository.dart';
-import 'package:immich_mobile/repositories/user.repository.dart';
+import 'package:immich_mobile/repositories/partner.repository.dart';
 import 'package:immich_mobile/services/entity.service.dart';
 import 'package:immich_mobile/services/hash.service.dart';
 import 'package:immich_mobile/utils/async_mutex.dart';
@@ -37,7 +42,9 @@ final syncServiceProvider = Provider(
     ref.watch(albumRepositoryProvider),
     ref.watch(assetRepositoryProvider),
     ref.watch(exifRepositoryProvider),
+    ref.watch(partnerRepositoryProvider),
     ref.watch(userRepositoryProvider),
+    ref.watch(storeServiceProvider),
     ref.watch(etagRepositoryProvider),
   ),
 );
@@ -51,6 +58,8 @@ class SyncService {
   final IAssetRepository _assetRepository;
   final IExifInfoRepository _exifInfoRepository;
   final IUserRepository _userRepository;
+  final IPartnerRepository _partnerRepository;
+  final StoreService _storeService;
   final IETagRepository _eTagRepository;
   final AsyncMutex _lock = AsyncMutex();
   final Logger _log = Logger('SyncService');
@@ -63,7 +72,9 @@ class SyncService {
     this._albumRepository,
     this._assetRepository,
     this._exifInfoRepository,
+    this._partnerRepository,
     this._userRepository,
+    this._storeService,
     this._eTagRepository,
   );
 
@@ -71,20 +82,20 @@ class SyncService {
 
   /// Syncs users from the server to the local database
   /// Returns `true`if there were any changes
-  Future<bool> syncUsersFromServer(List<User> users) =>
+  Future<bool> syncUsersFromServer(List<UserDto> users) =>
       _lock.run(() => _syncUsersFromServer(users));
 
   /// Syncs remote assets owned by the logged-in user to the DB
   /// Returns `true` if there were any changes
   Future<bool> syncRemoteAssetsToDb({
-    required List<User> users,
+    required List<UserDto> users,
     required Future<(List<Asset>? toUpsert, List<String>? toDelete)> Function(
-      List<User> users,
+      List<UserDto> users,
       DateTime since,
     ) getChangedAssets,
-    required FutureOr<List<Asset>?> Function(User user, DateTime until)
+    required FutureOr<List<Asset>?> Function(UserDto user, DateTime until)
         loadAssets,
-    required FutureOr<List<User>?> Function() refreshUsers,
+    required FutureOr<List<UserDto>?> Function() refreshUsers,
   }) =>
       _lock.run(
         () async =>
@@ -134,16 +145,16 @@ class SyncService {
 
   /// Syncs users from the server to the local database
   /// Returns `true`if there were any changes
-  Future<bool> _syncUsersFromServer(List<User> users) async {
-    users.sortBy((u) => u.id);
-    final dbUsers = await _userRepository.getAll(sortBy: UserSort.id);
+  Future<bool> _syncUsersFromServer(List<UserDto> users) async {
+    users.sortBy((u) => u.uid);
+    final dbUsers = await _userRepository.getAll(sortBy: SortUserBy.id);
     final List<int> toDelete = [];
-    final List<User> toUpsert = [];
+    final List<UserDto> toUpsert = [];
     final changes = diffSortedListsSync(
       users,
       dbUsers,
-      compare: (User a, User b) => a.id.compareTo(b.id),
-      both: (User a, User b) {
+      compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
+      both: (UserDto a, UserDto b) {
         if (!a.updatedAt.isAtSameMomentAs(b.updatedAt) ||
             a.isPartnerSharedBy != b.isPartnerSharedBy ||
             a.isPartnerSharedWith != b.isPartnerSharedWith ||
@@ -153,13 +164,13 @@ class SyncService {
         }
         return false;
       },
-      onlyFirst: (User a) => toUpsert.add(a),
-      onlySecond: (User b) => toDelete.add(b.isarId),
+      onlyFirst: (UserDto a) => toUpsert.add(a),
+      onlySecond: (UserDto b) => toDelete.add(b.id),
     );
     if (changes) {
       await _userRepository.transaction(() async {
-        await _userRepository.deleteById(toDelete);
-        await _userRepository.upsertAll(toUpsert);
+        await _userRepository.delete(toDelete);
+        await _userRepository.updateAll(toUpsert);
       });
     }
     return changes;
@@ -185,15 +196,15 @@ class SyncService {
 
   /// Efficiently syncs assets via changes. Returns `null` when a full sync is required.
   Future<bool?> _syncRemoteAssetChanges(
-    List<User> users,
+    List<UserDto> users,
     Future<(List<Asset>? toUpsert, List<String>? toDelete)> Function(
-      List<User> users,
+      List<UserDto> users,
       DateTime since,
     ) getChangedAssets,
   ) async {
-    final currentUser = await _userRepository.me();
+    final currentUser = _storeService.get(StoreKey.currentUser);
     final DateTime? since =
-        (await _eTagRepository.get(currentUser.isarId))?.time?.toUtc();
+        (await _eTagRepository.get(currentUser.id))?.time?.toUtc();
     if (since == null) return null;
     final DateTime now = DateTime.now();
     final (toUpsert, toDelete) = await getChangedAssets(users, since);
@@ -240,10 +251,16 @@ class SyncService {
     });
   }
 
+  Future<List<UserDto>> _getAllAccessibleUsers() async {
+    final sharedWith = (await _partnerRepository.getSharedWith()).toSet();
+    sharedWith.add(_storeService.get(StoreKey.currentUser));
+    return sharedWith.toList();
+  }
+
   /// Syncs assets by loading and comparing all assets from the server.
   Future<bool> _syncRemoteAssetsFull(
-    FutureOr<List<User>?> Function() refreshUsers,
-    FutureOr<List<Asset>?> Function(User user, DateTime until) loadAssets,
+    FutureOr<List<UserDto>?> Function() refreshUsers,
+    FutureOr<List<Asset>?> Function(UserDto user, DateTime until) loadAssets,
   ) async {
     final serverUsers = await refreshUsers();
     if (serverUsers == null) {
@@ -251,17 +268,17 @@ class SyncService {
       return false;
     }
     await _syncUsersFromServer(serverUsers);
-    final List<User> users = await _userRepository.getAllAccessible();
+    final List<UserDto> users = await _getAllAccessibleUsers();
     bool changes = false;
-    for (User u in users) {
+    for (UserDto u in users) {
       changes |= await _syncRemoteAssetsForUser(u, loadAssets);
     }
     return changes;
   }
 
   Future<bool> _syncRemoteAssetsForUser(
-    User user,
-    FutureOr<List<Asset>?> Function(User user, DateTime until) loadAssets,
+    UserDto user,
+    FutureOr<List<Asset>?> Function(UserDto user, DateTime until) loadAssets,
   ) async {
     final DateTime now = DateTime.now().toUtc();
     final List<Asset>? remote = await loadAssets(user, now);
@@ -269,7 +286,7 @@ class SyncService {
       return false;
     }
     final List<Asset> inDb = await _assetRepository.getAll(
-      ownerId: user.isarId,
+      ownerId: user.id,
       sortBy: AssetSort.checksum,
     );
     assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!");
@@ -295,13 +312,13 @@ class SyncService {
     return true;
   }
 
-  Future<void> _updateUserAssetsETag(List<User> users, DateTime time) {
-    final etags = users.map((u) => ETag(id: u.id, time: time)).toList();
+  Future<void> _updateUserAssetsETag(List<UserDto> users, DateTime time) {
+    final etags = users.map((u) => ETag(id: u.uid, time: time)).toList();
     return _eTagRepository.upsertAll(etags);
   }
 
-  Future<void> _clearUserAssetsETag(List<User> users) {
-    final ids = users.map((u) => u.id).toList();
+  Future<void> _clearUserAssetsETag(List<UserDto> users) {
+    final ids = users.map((u) => u.uid).toList();
     return _eTagRepository.deleteByIds(ids);
   }
 
@@ -373,26 +390,27 @@ class SyncService {
     );
 
     // update shared users
-    final List<User> sharedUsers = album.sharedUsers.toList(growable: false);
+    final List<UserDto> sharedUsers =
+        album.sharedUsers.map((u) => u.toDto()).toList(growable: false);
     sharedUsers.sort((a, b) => a.id.compareTo(b.id));
-    final List<User> users = dto.remoteUsers.toList()
+    final List<UserDto> users = dto.remoteUsers.map((u) => u.toDto()).toList()
       ..sort((a, b) => a.id.compareTo(b.id));
     final List<String> userIdsToAdd = [];
-    final List<User> usersToUnlink = [];
+    final List<UserDto> usersToUnlink = [];
     diffSortedListsSync(
       users,
       sharedUsers,
-      compare: (User a, User b) => a.id.compareTo(b.id),
+      compare: (UserDto a, UserDto b) => a.id.compareTo(b.id),
       both: (a, b) => false,
-      onlyFirst: (User a) => userIdsToAdd.add(a.id),
-      onlySecond: (User a) => usersToUnlink.add(a),
+      onlyFirst: (UserDto a) => userIdsToAdd.add(a.uid),
+      onlySecond: (UserDto a) => usersToUnlink.add(a),
     );
 
     // for shared album: put missing album assets into local DB
     final (existingInDb, updated) = await _linkWithExistingFromDb(toAdd);
     await upsertAssetsWithExif(updated);
     final assetsToLink = existingInDb + updated;
-    final usersToLink = await _userRepository.getByIds(userIdsToAdd);
+    final usersToLink = await _userRepository.getByUserIds(userIdsToAdd);
 
     album.name = dto.name;
     album.shared = dto.shared;
@@ -416,7 +434,7 @@ class SyncService {
     try {
       await _assetRepository.transaction(() async {
         await _assetRepository.updateAll(toUpdate);
-        await _albumRepository.addUsers(album, usersToLink);
+        await _albumRepository.addUsers(album, usersToLink.nonNulls.toList());
         await _albumRepository.removeUsers(album, usersToUnlink);
         await _albumRepository.addAssets(album, assetsToLink);
         await _albumRepository.removeAssets(album, toUnlink);
@@ -429,7 +447,7 @@ class SyncService {
     }
 
     if (album.shared || dto.shared) {
-      final userId = (await _userRepository.me()).isarId;
+      final userId = (_storeService.get(StoreKey.currentUser)).id;
       final foreign =
           await _assetRepository.getByAlbum(album, notOwnedBy: [userId]);
       existing.addAll(foreign);
@@ -482,8 +500,7 @@ class SyncService {
       );
     } else if (album.shared) {
       // delete assets in DB unless they belong to this user or are part of some other shared album or belong to a partner
-      final userIds =
-          (await _userRepository.getAllAccessible()).map((user) => user.isarId);
+      final userIds = (await _getAllAccessibleUsers()).map((user) => user.id);
       final orphanedAssets =
           await _assetRepository.getByAlbum(album, notOwnedBy: userIds);
       deleteCandidates.addAll(orphanedAssets);
@@ -566,7 +583,7 @@ class SyncService {
     // general case, e.g. some assets have been deleted or there are excluded albums on iOS
     final inDb = await _assetRepository.getByAlbum(
       dbAlbum,
-      ownerId: (await _userRepository.me()).isarId,
+      ownerId: (_storeService.get(StoreKey.currentUser)).id,
       sortBy: AssetSort.checksum,
     );
 
diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart
index db85230662..03042e266b 100644
--- a/mobile/lib/services/timeline.service.dart
+++ b/mobile/lib/services/timeline.service.dart
@@ -1,41 +1,42 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/services/store.service.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/interfaces/timeline.interface.dart';
-import 'package:immich_mobile/interfaces/user.interface.dart';
 import 'package:immich_mobile/providers/app_settings.provider.dart';
+import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
 import 'package:immich_mobile/repositories/timeline.repository.dart';
-import 'package:immich_mobile/repositories/user.repository.dart';
 import 'package:immich_mobile/services/app_settings.service.dart';
 import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
 
 final timelineServiceProvider = Provider<TimelineService>((ref) {
   return TimelineService(
     ref.watch(timelineRepositoryProvider),
-    ref.watch(userRepositoryProvider),
     ref.watch(appSettingsServiceProvider),
+    ref.watch(storeServiceProvider),
   );
 });
 
 class TimelineService {
   final ITimelineRepository _timelineRepository;
-  final IUserRepository _userRepository;
   final AppSettingsService _appSettingsService;
+  final StoreService _storeService;
 
   const TimelineService(
     this._timelineRepository,
-    this._userRepository,
     this._appSettingsService,
+    this._storeService,
   );
 
   Future<List<int>> getTimelineUserIds() async {
-    final me = await _userRepository.me();
-    return _timelineRepository.getTimelineUserIds(me.isarId);
+    final me = _storeService.get(StoreKey.currentUser);
+    return _timelineRepository.getTimelineUserIds(me.id);
   }
 
   Stream<List<int>> watchTimelineUserIds() async* {
-    final me = await _userRepository.me();
-    yield* _timelineRepository.watchTimelineUsers(me.isarId);
+    final me = _storeService.get(StoreKey.currentUser);
+    yield* _timelineRepository.watchTimelineUsers(me.id);
   }
 
   Stream<RenderList> watchHomeTimeline(int userId) {
@@ -50,15 +51,15 @@ class TimelineService {
   }
 
   Stream<RenderList> watchArchiveTimeline() async* {
-    final user = await _userRepository.me();
+    final user = _storeService.get(StoreKey.currentUser);
 
-    yield* _timelineRepository.watchArchiveTimeline(user.isarId);
+    yield* _timelineRepository.watchArchiveTimeline(user.id);
   }
 
   Stream<RenderList> watchFavoriteTimeline() async* {
-    final user = await _userRepository.me();
+    final user = _storeService.get(StoreKey.currentUser);
 
-    yield* _timelineRepository.watchFavoriteTimeline(user.isarId);
+    yield* _timelineRepository.watchFavoriteTimeline(user.id);
   }
 
   Stream<RenderList> watchAlbumTimeline(Album album) async* {
@@ -69,9 +70,9 @@ class TimelineService {
   }
 
   Stream<RenderList> watchTrashTimeline() async* {
-    final user = await _userRepository.me();
+    final user = _storeService.get(StoreKey.currentUser);
 
-    yield* _timelineRepository.watchTrashTimeline(user.isarId);
+    yield* _timelineRepository.watchTrashTimeline(user.id);
   }
 
   Stream<RenderList> watchAllVideosTimeline() {
@@ -96,9 +97,9 @@ class TimelineService {
   }
 
   Stream<RenderList> watchAssetSelectionTimeline() async* {
-    final user = await _userRepository.me();
+    final user = _storeService.get(StoreKey.currentUser);
 
-    yield* _timelineRepository.watchAssetSelectionTimeline(user.isarId);
+    yield* _timelineRepository.watchAssetSelectionTimeline(user.id);
   }
 
   GroupAssetsBy _getGroupByOption() {
diff --git a/mobile/lib/services/trash.service.dart b/mobile/lib/services/trash.service.dart
index 8d6cdd8bab..338f063fd3 100644
--- a/mobile/lib/services/trash.service.dart
+++ b/mobile/lib/services/trash.service.dart
@@ -1,12 +1,11 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/services/store.service.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/interfaces/asset.interface.dart';
-import 'package:immich_mobile/interfaces/user.interface.dart';
-
 import 'package:immich_mobile/providers/api.provider.dart';
+import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
 import 'package:immich_mobile/repositories/asset.repository.dart';
-import 'package:immich_mobile/repositories/user.repository.dart';
-
 import 'package:immich_mobile/services/api.service.dart';
 import 'package:openapi/api.dart';
 
@@ -14,16 +13,20 @@ final trashServiceProvider = Provider<TrashService>((ref) {
   return TrashService(
     ref.watch(apiServiceProvider),
     ref.watch(assetRepositoryProvider),
-    ref.watch(userRepositoryProvider),
+    ref.watch(storeServiceProvider),
   );
 });
 
 class TrashService {
   final ApiService _apiService;
   final IAssetRepository _assetRepository;
-  final IUserRepository _userRepository;
+  final StoreService _storeService;
 
-  TrashService(this._apiService, this._assetRepository, this._userRepository);
+  TrashService(
+    this._apiService,
+    this._assetRepository,
+    this._storeService,
+  );
 
   Future<void> restoreAssets(Iterable<Asset> assetList) async {
     final remoteAssets = assetList.where((a) => a.isRemote);
@@ -40,11 +43,11 @@ class TrashService {
   }
 
   Future<void> emptyTrash() async {
-    final user = await _userRepository.me();
+    final user = _storeService.get(StoreKey.currentUser);
 
     await _apiService.trashApi.emptyTrash();
 
-    final trashedAssets = await _assetRepository.getTrashAssets(user.isarId);
+    final trashedAssets = await _assetRepository.getTrashAssets(user.id);
     final ids = trashedAssets.map((e) => e.remoteId!).toList();
 
     await _assetRepository.transaction(() async {
@@ -71,11 +74,11 @@ class TrashService {
   }
 
   Future<void> restoreTrash() async {
-    final user = await _userRepository.me();
+    final user = _storeService.get(StoreKey.currentUser);
 
     await _apiService.trashApi.restoreTrash();
 
-    final trashedAssets = await _assetRepository.getTrashAssets(user.isarId);
+    final trashedAssets = await _assetRepository.getTrashAssets(user.id);
     final updatedAssets = trashedAssets.map((asset) {
       asset.isTrashed = false;
       return asset;
diff --git a/mobile/lib/services/user.service.dart b/mobile/lib/services/user.service.dart
index 921202ec59..3cb2022aa3 100644
--- a/mobile/lib/services/user.service.dart
+++ b/mobile/lib/services/user.service.dart
@@ -1,12 +1,12 @@
 import 'package:collection/collection.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:image_picker/image_picker.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
+import 'package:immich_mobile/domain/models/user.model.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/providers/infrastructure/user.provider.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/utils/diff.dart';
 import 'package:logging/logging.dart';
@@ -31,10 +31,6 @@ class UserService {
     this._userRepository,
   );
 
-  Future<List<User>> getUsers({bool self = false}) {
-    return _userRepository.getAll(self: self);
-  }
-
   Future<({String profileImagePath})?> uploadProfileImage(XFile image) async {
     try {
       return await _userApiRepository.createProfileImage(
@@ -47,17 +43,21 @@ class UserService {
     }
   }
 
-  Future<List<User>?> getUsersFromServer() async {
-    List<User>? users;
+  Future<List<UserDto>> getAll() async {
+    return await _userRepository.getAll();
+  }
+
+  Future<List<UserDto>?> getUsersFromServer() async {
+    List<UserDto>? users;
     try {
       users = await _userApiRepository.getAll();
     } catch (e) {
       _log.warning("Failed to fetch users", e);
       users = null;
     }
-    final List<User> sharedBy =
+    final List<UserDto> sharedBy =
         await _partnerApiRepository.getAll(Direction.sharedByMe);
-    final List<User> sharedWith =
+    final List<UserDto> sharedWith =
         await _partnerApiRepository.getAll(Direction.sharedWithMe);
 
     if (users == null) {
@@ -65,36 +65,44 @@ class UserService {
       return null;
     }
 
-    users.sortBy((u) => u.id);
-    sharedBy.sortBy((u) => u.id);
-    sharedWith.sortBy((u) => u.id);
+    users.sortBy((u) => u.uid);
+    sharedBy.sortBy((u) => u.uid);
+    sharedWith.sortBy((u) => u.uid);
+
+    final updatedSharedBy = <UserDto>[];
 
     diffSortedListsSync(
       users,
       sharedBy,
-      compare: (User a, User b) => a.id.compareTo(b.id),
-      both: (User a, User b) => a.isPartnerSharedBy = true,
-      onlyFirst: (_) {},
-      onlySecond: (_) {},
-    );
-
-    diffSortedListsSync(
-      users,
-      sharedWith,
-      compare: (User a, User b) => a.id.compareTo(b.id),
-      both: (User a, User b) {
-        a.isPartnerSharedWith = true;
-        a.inTimeline = b.inTimeline;
+      compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
+      both: (UserDto a, UserDto b) {
+        updatedSharedBy.add(a.copyWith(isPartnerSharedBy: true));
         return true;
       },
-      onlyFirst: (_) {},
-      onlySecond: (_) {},
+      onlyFirst: (UserDto a) => updatedSharedBy.add(a),
+      onlySecond: (UserDto b) => updatedSharedBy.add(b),
     );
 
-    return users;
+    final updatedSharedWith = <UserDto>[];
+
+    diffSortedListsSync(
+      updatedSharedBy,
+      sharedWith,
+      compare: (UserDto a, UserDto b) => a.uid.compareTo(b.uid),
+      both: (UserDto a, UserDto b) {
+        updatedSharedWith.add(
+          a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true),
+        );
+        return true;
+      },
+      onlyFirst: (UserDto a) => updatedSharedWith.add(a),
+      onlySecond: (UserDto b) => updatedSharedWith.add(b),
+    );
+
+    return updatedSharedWith;
   }
 
   Future<void> clearTable() {
-    return _userRepository.clearTable();
+    return _userRepository.deleteAll();
   }
 }
diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart
index db019798a3..21231becf6 100644
--- a/mobile/lib/utils/bootstrap.dart
+++ b/mobile/lib/utils/bootstrap.dart
@@ -10,10 +10,10 @@ import 'package:immich_mobile/entities/backup_album.entity.dart';
 import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
 import 'package:immich_mobile/entities/etag.entity.dart';
 import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
 import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
 import 'package:isar/isar.dart';
diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart
index 990fc082d5..d2f0a2ac9d 100644
--- a/mobile/lib/utils/migration.dart
+++ b/mobile/lib/utils/migration.dart
@@ -5,8 +5,8 @@ import 'package:immich_mobile/entities/album.entity.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/etag.entity.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:isar/isar.dart';
 
 const int targetVersion = 8;
diff --git a/mobile/lib/utils/provider_utils.dart b/mobile/lib/utils/provider_utils.dart
index 3eac55089d..9a2a441a3e 100644
--- a/mobile/lib/utils/provider_utils.dart
+++ b/mobile/lib/utils/provider_utils.dart
@@ -1,4 +1,5 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:immich_mobile/providers/user.provider.dart';
 import 'package:immich_mobile/repositories/activity_api.repository.dart';
 import 'package:immich_mobile/repositories/album_api.repository.dart';
 import 'package:immich_mobile/repositories/asset_api.repository.dart';
@@ -13,4 +14,5 @@ void invalidateAllApiRepositoryProviders(WidgetRef ref) {
   ref.invalidate(albumApiRepositoryProvider);
   ref.invalidate(personApiRepositoryProvider);
   ref.invalidate(assetApiRepositoryProvider);
+  ref.invalidate(timelineUsersIdsProvider);
 }
diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart
index ac62ecee03..ec984d1017 100644
--- a/mobile/lib/widgets/album/album_thumbnail_card.dart
+++ b/mobile/lib/widgets/album/album_thumbnail_card.dart
@@ -58,7 +58,7 @@ class AlbumThumbnailCard extends StatelessWidget {
           // Add the owner name to the subtitle
           String? owner;
           if (showOwner) {
-            if (album.ownerId == Store.get(StoreKey.currentUser).id) {
+            if (album.ownerId == Store.get(StoreKey.currentUser).uid) {
               owner = 'album_thumbnail_owned'.tr();
             } else if (album.ownerName != null) {
               owner = 'album_thumbnail_shared_by'.tr(args: [album.ownerName!]);
diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart
index 256141dc7d..1b225f106f 100644
--- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart
+++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart
@@ -5,25 +5,25 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 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/pages/editing/edit.page.dart';
 import 'package:immich_mobile/providers/album/album.provider.dart';
 import 'package:immich_mobile/providers/album/current_album.provider.dart';
+import 'package:immich_mobile/providers/asset.provider.dart';
 import 'package:immich_mobile/providers/asset_viewer/asset_stack.provider.dart';
 import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
 import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
 import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
-import 'package:immich_mobile/services/stack.service.dart';
-import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
-import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart';
-import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
-import 'package:immich_mobile/routing/router.dart';
-import 'package:immich_mobile/widgets/common/immich_image.dart';
-import 'package:immich_mobile/entities/asset.entity.dart';
-import 'package:immich_mobile/providers/asset.provider.dart';
 import 'package:immich_mobile/providers/server_info.provider.dart';
 import 'package:immich_mobile/providers/user.provider.dart';
+import 'package:immich_mobile/routing/router.dart';
+import 'package:immich_mobile/services/stack.service.dart';
+import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
+import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
+import 'package:immich_mobile/widgets/asset_viewer/video_controls.dart';
+import 'package:immich_mobile/widgets/common/immich_image.dart';
 import 'package:immich_mobile/widgets/common/immich_toast.dart';
-import 'package:immich_mobile/pages/editing/edit.page.dart';
 
 class BottomGalleryBar extends ConsumerWidget {
   final ValueNotifier<int> assetIndex;
@@ -49,7 +49,7 @@ class BottomGalleryBar extends ConsumerWidget {
     if (asset == null) {
       return const SizedBox();
     }
-    final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.isarId;
+    final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.id;
     final showControls = ref.watch(showControlsProvider);
     final stackId = asset.stackId;
 
diff --git a/mobile/lib/widgets/asset_viewer/description_input.dart b/mobile/lib/widgets/asset_viewer/description_input.dart
index 2d9d71ac9e..844e4744b3 100644
--- a/mobile/lib/widgets/asset_viewer/description_input.dart
+++ b/mobile/lib/widgets/asset_viewer/description_input.dart
@@ -81,7 +81,7 @@ class DescriptionInput extends HookConsumerWidget {
     }
 
     return TextField(
-      enabled: owner?.isarId == asset.ownerId,
+      enabled: owner?.id == asset.ownerId,
       focusNode: focusNode,
       onTap: () => isFocus.value = true,
       onChanged: (value) {
diff --git a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart
index 80be3e46cc..dcef6c79d4 100644
--- a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart
+++ b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart
@@ -3,22 +3,22 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/providers/album/current_album.provider.dart';
-import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
-import 'package:immich_mobile/providers/asset_viewer/scroll_to_date_notifier.provider.dart';
-import 'package:immich_mobile/providers/tab.provider.dart';
-import 'package:immich_mobile/widgets/album/add_to_album_bottom_sheet.dart';
-import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
-import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
-import 'package:immich_mobile/widgets/asset_viewer/top_control_app_bar.dart';
-import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
-import 'package:immich_mobile/providers/trash.provider.dart';
-import 'package:immich_mobile/widgets/asset_grid/upload_dialog.dart';
-import 'package:immich_mobile/providers/partner.provider.dart';
-import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
+import 'package:immich_mobile/providers/album/current_album.provider.dart';
 import 'package:immich_mobile/providers/asset.provider.dart';
+import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
+import 'package:immich_mobile/providers/asset_viewer/download.provider.dart';
+import 'package:immich_mobile/providers/asset_viewer/scroll_to_date_notifier.provider.dart';
+import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
+import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
+import 'package:immich_mobile/providers/partner.provider.dart';
+import 'package:immich_mobile/providers/tab.provider.dart';
+import 'package:immich_mobile/providers/trash.provider.dart';
 import 'package:immich_mobile/providers/user.provider.dart';
+import 'package:immich_mobile/routing/router.dart';
+import 'package:immich_mobile/widgets/album/add_to_album_bottom_sheet.dart';
+import 'package:immich_mobile/widgets/asset_grid/upload_dialog.dart';
+import 'package:immich_mobile/widgets/asset_viewer/top_control_app_bar.dart';
 import 'package:immich_mobile/widgets/common/immich_toast.dart';
 
 class GalleryAppBar extends ConsumerWidget {
@@ -33,12 +33,12 @@ class GalleryAppBar extends ConsumerWidget {
       return const SizedBox();
     }
     final album = ref.watch(currentAlbumProvider);
-    final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.isarId;
+    final isOwner = asset.ownerId == ref.watch(currentUserProvider)?.id;
     final showControls = ref.watch(showControlsProvider);
 
     final isPartner = ref
         .watch(partnerSharedWithProvider)
-        .map((e) => e.isarId)
+        .map((e) => e.id)
         .contains(asset.ownerId);
 
     toggleFavorite(Asset asset) =>
diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart
index d51e122954..4d4376b71d 100644
--- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart
+++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart
@@ -67,8 +67,9 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
                 profileImagePath,
               );
           if (user != null) {
-            user.profileImagePath = profileImagePath;
-            await Store.put(StoreKey.currentUser, user);
+            final updatedUser =
+                user.copyWith(profileImagePath: profileImagePath);
+            await Store.put(StoreKey.currentUser, updatedUser);
             ref.read(currentUserProvider.notifier).refresh();
           }
         }
diff --git a/mobile/lib/widgets/common/user_avatar.dart b/mobile/lib/widgets/common/user_avatar.dart
index 62491210c9..753a1f5d37 100644
--- a/mobile/lib/widgets/common/user_avatar.dart
+++ b/mobile/lib/widgets/common/user_avatar.dart
@@ -1,14 +1,14 @@
 import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/material.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/services/api.service.dart';
 
-Widget userAvatar(BuildContext context, User u, {double? radius}) {
+Widget userAvatar(BuildContext context, UserDto u, {double? radius}) {
   final url =
-      "${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image";
+      "${Store.get(StoreKey.serverEndpoint)}/users/${u.uid}/profile-image";
   final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : "";
   return CircleAvatar(
     radius: radius,
diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart
index 2b7eadf04b..4bc7bfa0f4 100644
--- a/mobile/lib/widgets/common/user_circle_avatar.dart
+++ b/mobile/lib/widgets/common/user_circle_avatar.dart
@@ -4,15 +4,15 @@ import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
 import 'package:immich_mobile/services/api.service.dart';
 import 'package:immich_mobile/widgets/common/transparent_image.dart';
 
 // ignore: must_be_immutable
 class UserCircleAvatar extends ConsumerWidget {
-  final User user;
+  final UserDto user;
   double radius;
   double size;
 
@@ -27,13 +27,13 @@ class UserCircleAvatar extends ConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     bool isDarkTheme = context.themeData.brightness == Brightness.dark;
     final profileImageUrl =
-        '${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}';
+        '${Store.get(StoreKey.serverEndpoint)}/users/${user.uid}/profile-image?d=${Random().nextInt(1024)}';
 
     final textIcon = DefaultTextStyle(
       style: TextStyle(
         fontWeight: FontWeight.bold,
         fontSize: 12,
-        color: isDarkTheme && user.avatarColor == AvatarColorEnum.primary
+        color: isDarkTheme && user.avatarColor == AvatarColor.primary
             ? Colors.black
             : Colors.white,
       ),
@@ -42,7 +42,7 @@ class UserCircleAvatar extends ConsumerWidget {
     return CircleAvatar(
       backgroundColor: user.avatarColor.toColor(),
       radius: radius,
-      child: user.profileImagePath.isEmpty
+      child: user.profileImagePath == null
           ? textIcon
           : ClipRRect(
               borderRadius: const BorderRadius.all(Radius.circular(50)),
diff --git a/mobile/test/fixtures/album.stub.dart b/mobile/test/fixtures/album.stub.dart
index e820f193d5..c6ea199c0f 100644
--- a/mobile/test/fixtures/album.stub.dart
+++ b/mobile/test/fixtures/album.stub.dart
@@ -1,4 +1,5 @@
 import 'package:immich_mobile/entities/album.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 
 import 'asset.stub.dart';
 import 'user.stub.dart';
@@ -26,7 +27,7 @@ final class AlbumStub {
     shared: true,
     activityEnabled: false,
     endDate: DateTime(2020),
-  )..sharedUsers.addAll([UserStub.admin]);
+  )..sharedUsers.addAll([User.fromDto(UserStub.admin)]);
 
   static final oneAsset = Album(
     name: "album-with-single-asset",
@@ -53,7 +54,7 @@ final class AlbumStub {
   )
     ..assets.addAll([AssetStub.image1, AssetStub.image2])
     ..activityEnabled = true
-    ..owner.value = UserStub.admin;
+    ..owner.value = User.fromDto(UserStub.admin);
 
   static final create2020end2020Album = Album(
     name: "create2020update2020Album",
diff --git a/mobile/test/fixtures/user.stub.dart b/mobile/test/fixtures/user.stub.dart
index 92efc93683..88b14dc02e 100644
--- a/mobile/test/fixtures/user.stub.dart
+++ b/mobile/test/fixtures/user.stub.dart
@@ -1,35 +1,35 @@
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 
 abstract final class UserStub {
   const UserStub._();
 
-  static final admin = User(
-    id: "admin",
-    updatedAt: DateTime(2021),
+  static final admin = UserDto(
+    uid: "admin",
     email: "admin@test.com",
     name: "admin",
     isAdmin: true,
-    profileImagePath: '',
-    avatarColor: AvatarColorEnum.green,
+    updatedAt: DateTime(2021),
+    profileImagePath: null,
+    avatarColor: AvatarColor.green,
   );
 
-  static final user1 = User(
-    id: "user1",
-    updatedAt: DateTime(2022),
+  static final user1 = UserDto(
+    uid: "user1",
     email: "user1@test.com",
     name: "user1",
     isAdmin: false,
-    profileImagePath: '',
-    avatarColor: AvatarColorEnum.red,
+    updatedAt: DateTime(2022),
+    profileImagePath: null,
+    avatarColor: AvatarColor.red,
   );
 
-  static final user2 = User(
-    id: "user2",
-    updatedAt: DateTime(2023),
+  static final user2 = UserDto(
+    uid: "user2",
     email: "user2@test.com",
     name: "user2",
     isAdmin: false,
-    profileImagePath: '',
-    avatarColor: AvatarColorEnum.primary,
+    updatedAt: DateTime(2023),
+    profileImagePath: null,
+    avatarColor: AvatarColor.primary,
   );
 }
diff --git a/mobile/test/infrastructure/repositories/store_repository_test.dart b/mobile/test/infrastructure/repositories/store_repository_test.dart
index 6fd3d3963a..16e0632d83 100644
--- a/mobile/test/infrastructure/repositories/store_repository_test.dart
+++ b/mobile/test/infrastructure/repositories/store_repository_test.dart
@@ -3,7 +3,7 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:immich_mobile/domain/interfaces/store.interface.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
 import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
 import 'package:isar/isar.dart';
@@ -86,7 +86,7 @@ void main() {
     });
 
     test('converts user', () async {
-      User? user = await sut.tryGet(StoreKey.currentUser);
+      UserDto? user = await sut.tryGet(StoreKey.currentUser);
       expect(user, isNull);
       await sut.insert(StoreKey.currentUser, _kTestUser);
       user = await sut.tryGet(StoreKey.currentUser);
diff --git a/mobile/test/modules/activity/activities_page_test.dart b/mobile/test/modules/activity/activities_page_test.dart
index 6b20692bcd..cf9238d205 100644
--- a/mobile/test/modules/activity/activities_page_test.dart
+++ b/mobile/test/modules/activity/activities_page_test.dart
@@ -9,7 +9,7 @@ import 'package:immich_mobile/domain/services/store.service.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:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
 import 'package:immich_mobile/models/activities/activity.model.dart';
 import 'package:immich_mobile/pages/common/activities.page.dart';
@@ -96,7 +96,7 @@ void main() {
     await db.writeTxn(() async {
       await db.clear();
       // Save all assets
-      await db.users.put(UserStub.admin);
+      await db.users.put(User.fromDto(UserStub.admin));
       await db.assets.putAll([AssetStub.image1, AssetStub.image2]);
       await db.albums.put(AlbumStub.twoAsset);
       await AlbumStub.twoAsset.owner.save();
diff --git a/mobile/test/modules/shared/shared_mocks.dart b/mobile/test/modules/shared/shared_mocks.dart
index 013232da3e..f50fde7040 100644
--- a/mobile/test/modules/shared/shared_mocks.dart
+++ b/mobile/test/modules/shared/shared_mocks.dart
@@ -1,13 +1,13 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/providers/user.provider.dart';
 import 'package:mocktail/mocktail.dart';
 
-class MockCurrentUserProvider extends StateNotifier<User?>
+class MockCurrentUserProvider extends StateNotifier<UserDto?>
     with Mock
     implements CurrentUserProvider {
   MockCurrentUserProvider() : super(null);
 
   @override
-  set state(User? user) => super.state = user;
+  set state(UserDto? user) => super.state = user;
 }
diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart
index a58de21613..577b5a0389 100644
--- a/mobile/test/modules/shared/sync_service_test.dart
+++ b/mobile/test/modules/shared/sync_service_test.dart
@@ -1,16 +1,16 @@
 import 'package:flutter/widgets.dart';
 import 'package:flutter_test/flutter_test.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
 import 'package:immich_mobile/domain/models/store.model.dart';
+import 'package:immich_mobile/domain/models/user.model.dart';
 import 'package:immich_mobile/domain/services/log.service.dart';
 import 'package:immich_mobile/domain/services/store.service.dart';
 import 'package:immich_mobile/entities/asset.entity.dart';
 import 'package:immich_mobile/entities/etag.entity.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
 import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
 import 'package:immich_mobile/interfaces/asset.interface.dart';
-import 'package:immich_mobile/interfaces/user.interface.dart';
 import 'package:immich_mobile/services/sync.service.dart';
 import 'package:mocktail/mocktail.dart';
 
@@ -56,8 +56,10 @@ void main() {
     final MockAlbumMediaRepository albumMediaRepository =
         MockAlbumMediaRepository();
     final MockAlbumApiRepository albumApiRepository = MockAlbumApiRepository();
-    final owner = User(
-      id: "1",
+    final MockPartnerRepository partnerRepository = MockPartnerRepository();
+
+    final owner = UserDto(
+      uid: "1",
       updatedAt: DateTime.now(),
       email: "a@b.c",
       name: "first last",
@@ -92,21 +94,22 @@ void main() {
         albumRepository,
         assetRepository,
         exifInfoRepository,
+        partnerRepository,
         userRepository,
+        StoreService.I,
         eTagRepository,
       );
-      when(() => eTagRepository.get(owner.isarId))
-          .thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now()));
+      when(() => eTagRepository.get(owner.id))
+          .thenAnswer((_) async => ETag(id: owner.uid, time: DateTime.now()));
       when(() => eTagRepository.deleteByIds(["1"])).thenAnswer((_) async {});
       when(() => eTagRepository.upsertAll(any())).thenAnswer((_) async {});
-      when(() => userRepository.me()).thenAnswer((_) async => owner);
-      when(() => userRepository.getAll(sortBy: UserSort.id))
-          .thenAnswer((_) async => [owner]);
-      when(() => userRepository.getAllAccessible())
+      when(() => partnerRepository.getSharedWith()).thenAnswer((_) async => []);
+      when(() => userRepository.getAll(sortBy: SortUserBy.id))
           .thenAnswer((_) async => [owner]);
+      when(() => userRepository.getAll()).thenAnswer((_) async => [owner]);
       when(
         () => assetRepository.getAll(
-          ownerId: owner.isarId,
+          ownerId: owner.id,
           sortBy: AssetSort.checksum,
         ),
       ).thenAnswer((_) async => initialAssets);
@@ -180,7 +183,7 @@ void main() {
       expect(c1, isTrue);
       when(
         () => assetRepository.getAll(
-          ownerId: owner.isarId,
+          ownerId: owner.id,
           sortBy: AssetSort.checksum,
         ),
       ).thenAnswer((_) async => remoteAssets);
@@ -194,7 +197,7 @@ void main() {
       final currentState = [...remoteAssets];
       when(
         () => assetRepository.getAll(
-          ownerId: owner.isarId,
+          ownerId: owner.id,
           sortBy: AssetSort.checksum,
         ),
       ).thenAnswer((_) async => currentState);
@@ -252,7 +255,7 @@ void main() {
 }
 
 Future<(List<Asset>?, List<String>?)> _failDiff(
-  List<User> user,
+  List<UserDto> user,
   DateTime time,
 ) =>
     Future.value((null, null));
diff --git a/mobile/test/repository.mocks.dart b/mobile/test/repository.mocks.dart
index 7443db2815..8672297f1f 100644
--- a/mobile/test/repository.mocks.dart
+++ b/mobile/test/repository.mocks.dart
@@ -1,4 +1,5 @@
 import 'package:immich_mobile/domain/interfaces/exif.interface.dart';
+import 'package:immich_mobile/domain/interfaces/user.interface.dart';
 import 'package:immich_mobile/interfaces/album.interface.dart';
 import 'package:immich_mobile/interfaces/album_api.interface.dart';
 import 'package:immich_mobile/interfaces/album_media.interface.dart';
@@ -9,7 +10,7 @@ import 'package:immich_mobile/interfaces/auth_api.interface.dart';
 import 'package:immich_mobile/interfaces/backup_album.interface.dart';
 import 'package:immich_mobile/interfaces/etag.interface.dart';
 import 'package:immich_mobile/interfaces/file_media.interface.dart';
-import 'package:immich_mobile/interfaces/user.interface.dart';
+import 'package:immich_mobile/interfaces/partner.interface.dart';
 import 'package:mocktail/mocktail.dart';
 
 class MockAlbumRepository extends Mock implements IAlbumRepository {}
@@ -35,3 +36,5 @@ class MockAlbumApiRepository extends Mock implements IAlbumApiRepository {}
 class MockAuthApiRepository extends Mock implements IAuthApiRepository {}
 
 class MockAuthRepository extends Mock implements IAuthRepository {}
+
+class MockPartnerRepository extends Mock implements IPartnerRepository {}
diff --git a/mobile/test/services/album.service_test.dart b/mobile/test/services/album.service_test.dart
index 983b355dcb..5460faaa23 100644
--- a/mobile/test/services/album.service_test.dart
+++ b/mobile/test/services/album.service_test.dart
@@ -146,7 +146,7 @@ void main() {
         () => albumApiRepository.create(
           "name",
           assetIds: [AssetStub.image1.remoteId!],
-          sharedUserIds: [UserStub.user1.id],
+          sharedUserIds: [UserStub.user1.uid],
         ),
       ).called(1);
       verify(
@@ -204,7 +204,7 @@ void main() {
       when(
         () => albumRepository.addUsers(
           AlbumStub.emptyAlbum,
-          AlbumStub.emptyAlbum.sharedUsers.toList(),
+          AlbumStub.emptyAlbum.sharedUsers.map((u) => u.toDto()).toList(),
         ),
       ).thenAnswer((_) async => AlbumStub.emptyAlbum);
 
@@ -214,7 +214,7 @@ void main() {
 
       final result = await sut.addUsers(
         AlbumStub.emptyAlbum,
-        [UserStub.user2.id],
+        [UserStub.user2.uid],
       );
 
       expect(result, true);
diff --git a/mobile/test/services/entity.service_test.dart b/mobile/test/services/entity.service_test.dart
index 8c8b49a7e0..4d2e4f75dc 100644
--- a/mobile/test/services/entity.service_test.dart
+++ b/mobile/test/services/entity.service_test.dart
@@ -1,7 +1,9 @@
 import 'package:flutter_test/flutter_test.dart';
 import 'package:immich_mobile/entities/album.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/services/entity.service.dart';
 import 'package:mocktail/mocktail.dart';
+
 import '../fixtures/asset.stub.dart';
 import '../fixtures/user.stub.dart';
 import '../repository.mocks.dart';
@@ -33,25 +35,32 @@ void main() {
       )
         ..remoteThumbnailAssetId = AssetStub.image1.remoteId
         ..assets.addAll([AssetStub.image1, AssetStub.image1])
-        ..owner.value = UserStub.user1
-        ..sharedUsers.addAll([UserStub.admin, UserStub.admin]);
+        ..owner.value = User.fromDto(UserStub.user1)
+        ..sharedUsers.addAll(
+          [User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)],
+        );
 
-      when(() => userRepository.get(album.ownerId!))
+      when(() => userRepository.get(any()))
+          .thenAnswer((_) async => UserStub.admin);
+      when(() => userRepository.getByUserId(any()))
           .thenAnswer((_) async => UserStub.admin);
 
       when(() => assetRepository.getByRemoteId(AssetStub.image1.remoteId!))
           .thenAnswer((_) async => AssetStub.image1);
 
-      when(() => userRepository.getByIds(any()))
+      when(() => userRepository.getByUserIds(any()))
           .thenAnswer((_) async => [UserStub.user1, UserStub.user2]);
 
       when(() => assetRepository.getAllByRemoteId(any()))
           .thenAnswer((_) async => [AssetStub.image1, AssetStub.image2]);
 
       await sut.fillAlbumWithDatabaseEntities(album);
-      expect(album.owner.value, UserStub.admin);
+      expect(album.owner.value?.toDto(), UserStub.admin);
       expect(album.thumbnail.value, AssetStub.image1);
-      expect(album.remoteUsers.toSet(), {UserStub.user1, UserStub.user2});
+      expect(
+        album.remoteUsers.map((u) => u.toDto()).toSet(),
+        {UserStub.user1, UserStub.user2},
+      );
       expect(album.remoteAssets.toSet(), {AssetStub.image1, AssetStub.image2});
     });
 
diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart
index 19b2d6e705..a5a89a2440 100644
--- a/mobile/test/test_utils.dart
+++ b/mobile/test/test_utils.dart
@@ -12,10 +12,10 @@ import 'package:immich_mobile/entities/backup_album.entity.dart';
 import 'package:immich_mobile/entities/duplicated_asset.entity.dart';
 import 'package:immich_mobile/entities/etag.entity.dart';
 import 'package:immich_mobile/entities/ios_device_asset.entity.dart';
-import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/log.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:isar/isar.dart';
 import 'package:mocktail/mocktail.dart';