From e3c8ee7d8f9da2c3b959c66130738aaace7c2869 Mon Sep 17 00:00:00 2001
From: Andri Joos <andri@joos.io>
Date: Mon, 6 May 2024 00:16:09 +0200
Subject: [PATCH] add factories & hive storage

---
 .../converter/appwrite_converters/point.dart  |   8 +-
 .../converter/appwrite_converters/route.dart  |  18 +--
 car_app/lib/converter/converter.dart          |   2 +-
 .../location_point_point_converter.dart       |  15 +++
 .../location_converters.dart/point.dart       |   8 +-
 .../location_converters.dart/position.dart    |  11 ++
 .../lib/converter/point_latlng_converter.dart |   4 +-
 .../converter/position_point_converter.dart   |  10 --
 .../{ => appwrite}/point.dart                 |  11 +-
 .../{ => appwrite}/route.dart                 |  24 ++--
 car_app/lib/factory/appwrite/point.dart       |  14 ++
 car_app/lib/factory/appwrite/route.dart       |  17 +++
 car_app/lib/factory/interface/point.dart      |   5 +
 car_app/lib/factory/interface/route.dart      |   6 +
 car_app/lib/main.dart                         | 125 +++++++++++++++---
 car_app/lib/model/appwrite/base_model.dart    |   8 ++
 car_app/lib/model/appwrite/point.dart         |  12 ++
 car_app/lib/model/appwrite/route.dart         |  19 +++
 car_app/lib/model/id_model/id_model.dart      |   3 +
 car_app/lib/model/id_model/point.dart         |   4 +
 car_app/lib/model/id_model/route.dart         |   5 +
 car_app/lib/model/interface/README.md         |   1 +
 car_app/lib/model/interface/point.dart        |   4 +
 car_app/lib/model/interface/route.dart        |   8 ++
 car_app/lib/model/location/point.dart         |  11 ++
 car_app/lib/model/point.dart                  |   7 -
 car_app/lib/model/route.dart                  |  11 --
 .../lib/service/appwrite/appwrite_point.dart  |   8 +-
 .../lib/service/appwrite/appwrite_route.dart  |  17 +--
 car_app/lib/service/chached_route.dart        |  38 +++---
 ...location.dart => geolocator_location.dart} |  40 +++---
 .../service/interface/key_value_storage.dart  |   1 +
 car_app/lib/service/interface/location.dart   |  13 ++
 car_app/lib/service/interface/point.dart      |   5 +
 car_app/lib/service/interface/route.dart      |   7 +-
 car_app/lib/service/storage/hive_storage.dart |  68 ++++++++++
 .../model_storage/appwrite/point_storage.dart |  17 +++
 .../model_storage/appwrite/route_storage.dart |  21 +++
 .../storage/model_storage/model_storage.dart  |  20 +++
 .../storage/model_storage/point_storage.dart  |  34 +++++
 .../storage/model_storage/route_storage.dart  |  65 +++++++++
 .../lib/service/storage/route_storage.dart    |  34 -----
 .../lib/service/storage/secure_storage.dart   |   8 ++
 car_app/lib/view/tracking.dart                |   2 +-
 car_app/lib/viewmodel/tracking.dart           |  38 ++++--
 car_app/pubspec.lock                          |  16 ++-
 car_app/pubspec.yaml                          |   3 +
 47 files changed, 625 insertions(+), 201 deletions(-)
 create mode 100644 car_app/lib/converter/location_converters.dart/location_point_point_converter.dart
 create mode 100644 car_app/lib/converter/location_converters.dart/position.dart
 delete mode 100644 car_app/lib/converter/position_point_converter.dart
 rename car_app/lib/converter/storage_converters/{ => appwrite}/point.dart (53%)
 rename car_app/lib/converter/storage_converters/{ => appwrite}/route.dart (51%)
 create mode 100644 car_app/lib/factory/appwrite/point.dart
 create mode 100644 car_app/lib/factory/appwrite/route.dart
 create mode 100644 car_app/lib/factory/interface/point.dart
 create mode 100644 car_app/lib/factory/interface/route.dart
 create mode 100644 car_app/lib/model/appwrite/base_model.dart
 create mode 100644 car_app/lib/model/appwrite/point.dart
 create mode 100644 car_app/lib/model/appwrite/route.dart
 create mode 100644 car_app/lib/model/id_model/id_model.dart
 create mode 100644 car_app/lib/model/id_model/point.dart
 create mode 100644 car_app/lib/model/id_model/route.dart
 create mode 100644 car_app/lib/model/interface/README.md
 create mode 100644 car_app/lib/model/interface/point.dart
 create mode 100644 car_app/lib/model/interface/route.dart
 create mode 100644 car_app/lib/model/location/point.dart
 delete mode 100644 car_app/lib/model/point.dart
 delete mode 100644 car_app/lib/model/route.dart
 rename car_app/lib/service/{location.dart => geolocator_location.dart} (82%)
 create mode 100644 car_app/lib/service/interface/location.dart
 create mode 100644 car_app/lib/service/interface/point.dart
 create mode 100644 car_app/lib/service/storage/hive_storage.dart
 create mode 100644 car_app/lib/service/storage/model_storage/appwrite/point_storage.dart
 create mode 100644 car_app/lib/service/storage/model_storage/appwrite/route_storage.dart
 create mode 100644 car_app/lib/service/storage/model_storage/model_storage.dart
 create mode 100644 car_app/lib/service/storage/model_storage/point_storage.dart
 create mode 100644 car_app/lib/service/storage/model_storage/route_storage.dart
 delete mode 100644 car_app/lib/service/storage/route_storage.dart

diff --git a/car_app/lib/converter/appwrite_converters/point.dart b/car_app/lib/converter/appwrite_converters/point.dart
index 728e2fe..7260098 100644
--- a/car_app/lib/converter/appwrite_converters/point.dart
+++ b/car_app/lib/converter/appwrite_converters/point.dart
@@ -1,6 +1,6 @@
-import 'package:car_app/model/point.dart';
+import 'package:car_app/model/appwrite/point.dart';
 
-extension PointMapConverter on Point {
+extension PointMapConverter on AppwritePoint {
   static const String idFieldName = "id";
   static const String longitudeFieldName = "longitude";
   static const String latitudeFieldName = "latitude";
@@ -11,8 +11,8 @@ extension PointMapConverter on Point {
     latitudeFieldName: latitude,
   };
 
-  static Point fromMap(Map<String, dynamic> map) {
-    return Point(
+  static AppwritePoint fromMap(Map<String, dynamic> map) {
+    return AppwritePoint(
       id: map[idFieldName],
       latitude: map[latitudeFieldName],
       longitude: map[longitudeFieldName],
diff --git a/car_app/lib/converter/appwrite_converters/route.dart b/car_app/lib/converter/appwrite_converters/route.dart
index 19c8fa8..65e2e7e 100644
--- a/car_app/lib/converter/appwrite_converters/route.dart
+++ b/car_app/lib/converter/appwrite_converters/route.dart
@@ -1,8 +1,7 @@
-import 'package:car_app/converter/appwrite_converters/point.dart';
-import 'package:car_app/model/point.dart';
-import 'package:car_app/model/route.dart';
+import 'package:car_app/model/appwrite/route.dart';
+import 'package:car_app/model/interface/point.dart';
 
-extension RouteMapConverter on Route {
+extension RouteMapConverter on AppwriteRoute {
   static const String idFieldName = "id";
   static const String teamIdFieldName = "teamId";
   static const String pointsFieldName = "points";
@@ -10,12 +9,11 @@ extension RouteMapConverter on Route {
   static const String endTimeFieldName = "endTime";
   static const String userIdFieldName = "userId";
 
-  Map<String, dynamic> toMap(String userId, {Iterable<Point>? points}) {
-    points ??= this.points;
+  Map<String, dynamic> toMap(String userId, Iterable<String>? pointIds) {
 
     return {
       idFieldName: id,
-      pointsFieldName: points.map((e) => e.id).toList(),
+      pointsFieldName: points,
       teamIdFieldName: carId,
       startTimeFieldName: startTime.toIso8601String(),
       endTimeFieldName: endTime.toIso8601String(),
@@ -23,10 +21,8 @@ extension RouteMapConverter on Route {
     };
   }
 
-  static Route fromMap(Map<String, dynamic> map, {Iterable<Point>? points}) {
-    points ??= List<Point>.from((List<Map<String, dynamic>>.from(map[pointsFieldName])).map((x) => PointMapConverter.fromMap(x)));
-    
-    return Route(
+  static AppwriteRoute<T> fromMap<T extends Point>(Map<String, dynamic> map, Iterable<T> points) {    
+    return AppwriteRoute(
       id: map[idFieldName],
       carId: map[teamIdFieldName],
       points: points,
diff --git a/car_app/lib/converter/converter.dart b/car_app/lib/converter/converter.dart
index cf1446c..3a8dbb4 100644
--- a/car_app/lib/converter/converter.dart
+++ b/car_app/lib/converter/converter.dart
@@ -1,3 +1,3 @@
-abstract class Converter <T1, T2> {
+abstract interface class Converter <T1, T2> {
   T2 convert(T1 t);
 }
diff --git a/car_app/lib/converter/location_converters.dart/location_point_point_converter.dart b/car_app/lib/converter/location_converters.dart/location_point_point_converter.dart
new file mode 100644
index 0000000..0addce4
--- /dev/null
+++ b/car_app/lib/converter/location_converters.dart/location_point_point_converter.dart
@@ -0,0 +1,15 @@
+import 'package:car_app/converter/converter.dart';
+import 'package:car_app/factory/interface/point.dart';
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/model/location/point.dart';
+
+class LocationPointToPointConverter<T extends Point> implements Converter<LocationPoint, T> {
+  final PointFactory<T> pointFactory;
+
+  LocationPointToPointConverter({required this.pointFactory});
+
+  @override
+  T convert(LocationPoint t) {
+    return pointFactory.createInstance(t.latitude, t.longitude);
+  }
+}
\ No newline at end of file
diff --git a/car_app/lib/converter/location_converters.dart/point.dart b/car_app/lib/converter/location_converters.dart/point.dart
index 66dc1a5..7127cd8 100644
--- a/car_app/lib/converter/location_converters.dart/point.dart
+++ b/car_app/lib/converter/location_converters.dart/point.dart
@@ -1,6 +1,6 @@
-import 'package:car_app/model/point.dart';
+import 'package:car_app/model/location/point.dart';
 
-extension PointMapConverter on Point {
+extension PointMapConverter on LocationPoint {
   static const String longitudeFieldName = "longitude";
   static const String latitudeFieldName = "latitude";
 
@@ -9,8 +9,8 @@ extension PointMapConverter on Point {
     latitudeFieldName: latitude,
   };
 
-  static Point fromMap(Map<String, dynamic> map) {
-    return Point(
+  static LocationPoint fromMap(Map<String, dynamic> map) {
+    return LocationPoint(
       latitude: map[latitudeFieldName],
       longitude: map[longitudeFieldName],
     );
diff --git a/car_app/lib/converter/location_converters.dart/position.dart b/car_app/lib/converter/location_converters.dart/position.dart
new file mode 100644
index 0000000..af3010a
--- /dev/null
+++ b/car_app/lib/converter/location_converters.dart/position.dart
@@ -0,0 +1,11 @@
+import 'package:car_app/model/location/point.dart';
+import 'package:geolocator/geolocator.dart';
+
+extension LocationPointConverter on Position {
+  LocationPoint toLocationPoint() {
+    return LocationPoint(
+      latitude: latitude,
+      longitude: longitude
+    );
+  }
+}
diff --git a/car_app/lib/converter/point_latlng_converter.dart b/car_app/lib/converter/point_latlng_converter.dart
index 183ade0..2ad50b2 100644
--- a/car_app/lib/converter/point_latlng_converter.dart
+++ b/car_app/lib/converter/point_latlng_converter.dart
@@ -1,8 +1,8 @@
 import 'package:car_app/converter/converter.dart';
-import 'package:car_app/model/point.dart';
+import 'package:car_app/model/interface/point.dart';
 import 'package:latlong2/latlong.dart';
 
-class PointToLatLngConverter extends Converter<Point, LatLng> {
+class PointToLatLngConverter implements Converter<Point, LatLng> {
   @override
   LatLng convert(Point t) {
     return LatLng(t.latitude.toDouble(), t.longitude.toDouble());
diff --git a/car_app/lib/converter/position_point_converter.dart b/car_app/lib/converter/position_point_converter.dart
deleted file mode 100644
index 700d17d..0000000
--- a/car_app/lib/converter/position_point_converter.dart
+++ /dev/null
@@ -1,10 +0,0 @@
-import 'package:car_app/converter/converter.dart';
-import 'package:car_app/model/point.dart';
-import 'package:geolocator/geolocator.dart';
-
-class PositionToPointConverter extends Converter<Position, Point> {
-  @override
-  Point convert(Position t) {
-    return Point(latitude: t.latitude, longitude: t.longitude);
-  }
-}
\ No newline at end of file
diff --git a/car_app/lib/converter/storage_converters/point.dart b/car_app/lib/converter/storage_converters/appwrite/point.dart
similarity index 53%
rename from car_app/lib/converter/storage_converters/point.dart
rename to car_app/lib/converter/storage_converters/appwrite/point.dart
index 66dc1a5..7260098 100644
--- a/car_app/lib/converter/storage_converters/point.dart
+++ b/car_app/lib/converter/storage_converters/appwrite/point.dart
@@ -1,16 +1,19 @@
-import 'package:car_app/model/point.dart';
+import 'package:car_app/model/appwrite/point.dart';
 
-extension PointMapConverter on Point {
+extension PointMapConverter on AppwritePoint {
+  static const String idFieldName = "id";
   static const String longitudeFieldName = "longitude";
   static const String latitudeFieldName = "latitude";
 
   Map<String, dynamic> toMap() => {
+    idFieldName: id,
     longitudeFieldName: longitude,
     latitudeFieldName: latitude,
   };
 
-  static Point fromMap(Map<String, dynamic> map) {
-    return Point(
+  static AppwritePoint fromMap(Map<String, dynamic> map) {
+    return AppwritePoint(
+      id: map[idFieldName],
       latitude: map[latitudeFieldName],
       longitude: map[longitudeFieldName],
     );
diff --git a/car_app/lib/converter/storage_converters/route.dart b/car_app/lib/converter/storage_converters/appwrite/route.dart
similarity index 51%
rename from car_app/lib/converter/storage_converters/route.dart
rename to car_app/lib/converter/storage_converters/appwrite/route.dart
index 5102a2e..5e1853f 100644
--- a/car_app/lib/converter/storage_converters/route.dart
+++ b/car_app/lib/converter/storage_converters/appwrite/route.dart
@@ -1,35 +1,29 @@
-import 'package:car_app/converter/storage_converters/point.dart';
-import 'package:car_app/model/point.dart';
-import 'package:car_app/model/route.dart';
+import 'package:car_app/model/appwrite/route.dart';
+import 'package:car_app/model/interface/point.dart';
 
-extension RouteMapConverter on Route {
+extension RouteMapConverter on AppwriteRoute {
   static const String idFieldName = "id";
   static const String teamIdFieldName = "teamId";
   static const String pointsFieldName = "points";
   static const String startTimeFieldName = "startTime";
   static const String endTimeFieldName = "endTime";
 
-  Map<String, dynamic> toMap() {
-    List<Map<String, dynamic>> jsonPoints = [];
-
-    for (var point in points) {
-      jsonPoints.add(point.toMap());
-    }
-
+  Map<String, dynamic> toMap(Iterable<String> pointIds) {
     return {
       idFieldName: id,
-      pointsFieldName: jsonPoints,
+      pointsFieldName: pointIds,
       teamIdFieldName: carId,
       startTimeFieldName: startTime.toIso8601String(),
       endTimeFieldName: endTime.toIso8601String(),
     };
   }
 
-  static Route fromMap(Map<String, dynamic> map) {
-    return Route(
+  static AppwriteRoute<T> fromMap<T extends Point>(Map<String, dynamic> map, Iterable<T> points) {
+    // List<Point>.from((List<Map<String, dynamic>>.from(map[pointsFieldName])).map((x) => PointMapConverter.fromMap(x)))
+    return AppwriteRoute(
       id: map[idFieldName],
       carId: map[teamIdFieldName],
-      points: List<Point>.from((List<Map<String, dynamic>>.from(map[pointsFieldName])).map((x) => PointMapConverter.fromMap(x))),
+      points: points,
       startTime: DateTime.parse(map[startTimeFieldName]),
       endTime: DateTime.parse(map[endTimeFieldName]),
     );
diff --git a/car_app/lib/factory/appwrite/point.dart b/car_app/lib/factory/appwrite/point.dart
new file mode 100644
index 0000000..dfd1a84
--- /dev/null
+++ b/car_app/lib/factory/appwrite/point.dart
@@ -0,0 +1,14 @@
+import 'package:appwrite/appwrite.dart';
+import 'package:car_app/factory/interface/point.dart';
+import 'package:car_app/model/appwrite/point.dart';
+
+class AppwritePointFactory implements PointFactory<AppwritePoint> {
+  @override
+  AppwritePoint createInstance(num latitude, num longitude) {
+    return AppwritePoint(
+      id: ID.unique(),
+      latitude: latitude,
+      longitude: longitude
+    );
+  }
+}
diff --git a/car_app/lib/factory/appwrite/route.dart b/car_app/lib/factory/appwrite/route.dart
new file mode 100644
index 0000000..896c405
--- /dev/null
+++ b/car_app/lib/factory/appwrite/route.dart
@@ -0,0 +1,17 @@
+import 'package:appwrite/appwrite.dart';
+import 'package:car_app/factory/interface/route.dart';
+import 'package:car_app/model/appwrite/route.dart';
+import 'package:car_app/model/interface/point.dart';
+
+class AppwriteRouteFactory<T extends Point> implements RouteFactory<AppwriteRoute<T>, T> {
+  @override
+  AppwriteRoute<T> createInstance(String? carId, Iterable<T> points, DateTime startTime, DateTime endTime) {
+    return AppwriteRoute(
+      id: ID.unique(),
+      carId: carId,
+      points: points,
+      startTime: startTime,
+      endTime: endTime
+    );
+  }
+}
diff --git a/car_app/lib/factory/interface/point.dart b/car_app/lib/factory/interface/point.dart
new file mode 100644
index 0000000..6f07b14
--- /dev/null
+++ b/car_app/lib/factory/interface/point.dart
@@ -0,0 +1,5 @@
+import 'package:car_app/model/interface/point.dart';
+
+abstract interface class PointFactory<T extends Point> {
+  T createInstance(num latitude, num longitude);
+}
diff --git a/car_app/lib/factory/interface/route.dart b/car_app/lib/factory/interface/route.dart
new file mode 100644
index 0000000..be99202
--- /dev/null
+++ b/car_app/lib/factory/interface/route.dart
@@ -0,0 +1,6 @@
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/model/interface/route.dart';
+
+abstract interface class RouteFactory<T extends Route<T1>, T1 extends Point> {
+  T createInstance(String? carId, Iterable<T1> points, DateTime startTime, DateTime endTime);
+}
diff --git a/car_app/lib/main.dart b/car_app/lib/main.dart
index a8d3d42..bc7f380 100644
--- a/car_app/lib/main.dart
+++ b/car_app/lib/main.dart
@@ -1,16 +1,30 @@
+import 'dart:convert';
+
+import 'package:car_app/converter/location_converters.dart/location_point_point_converter.dart';
+import 'package:car_app/exception/key_not_found.dart';
+import 'package:car_app/factory/appwrite/point.dart';
+import 'package:car_app/factory/appwrite/route.dart';
+import 'package:car_app/factory/interface/route.dart' as route_factory;
+import 'package:car_app/model/appwrite/point.dart';
+import 'package:car_app/model/appwrite/route.dart';
 import 'package:car_app/service/appwrite/appwrite_authentication.dart';
 import 'package:car_app/service/appwrite/appwrite_calendar_event.dart';
 import 'package:car_app/service/appwrite/appwrite_car.dart';
+import 'package:car_app/service/appwrite/appwrite_point.dart';
 import 'package:car_app/service/appwrite/appwrite_route.dart';
 import 'package:car_app/service/cached_username_password_authentication.dart';
 import 'package:car_app/service/chached_route.dart';
+import 'package:car_app/service/geolocator_location.dart';
 import 'package:car_app/service/interface/authentication.dart';
 import 'package:car_app/service/interface/calendar_event.dart';
 import 'package:car_app/service/interface/car.dart';
+import 'package:car_app/service/interface/location.dart';
 import 'package:car_app/service/interface/logging.dart';
 import 'package:car_app/service/interface/route.dart';
 import 'package:car_app/service/logging/logger/logger.dart';
-import 'package:car_app/service/storage/route_storage.dart';
+import 'package:car_app/service/storage/hive_storage.dart';
+import 'package:car_app/service/storage/model_storage/appwrite/point_storage.dart';
+import 'package:car_app/service/storage/model_storage/appwrite/route_storage.dart';
 import 'package:car_app/service/storage/secure_storage.dart';
 import 'package:car_app/view/calendar.dart';
 import 'package:car_app/view/startup.dart';
@@ -20,55 +34,113 @@ import 'package:car_app/viewmodel/authentication.dart';
 import 'package:car_app/viewmodel/calendar.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:path/path.dart' as path;
 
 import 'package:car_app/viewmodel/tracking.dart';
+import 'package:hive/hive.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:provider/provider.dart';
 
+const String hiveEncryptionKeyFieldName = "hiveEncryptionKey";
 
 void main() async {
   WidgetsFlutterBinding.ensureInitialized();
   var applicationDocumentsDirectory = await getApplicationDocumentsDirectory();
+  var hiveEncryptionKey = await getOrMakeHiveEncryptionKey();
+
   runApp(App(
-    applicationDocumentsPath: applicationDocumentsDirectory.path
+    applicationDocumentsPath: applicationDocumentsDirectory.path,
+    hiveEncryptionKey: hiveEncryptionKey,
   ));
 }
 
+Future<List<int>> getOrMakeHiveEncryptionKey() async {
+  var secureStorage = SecureStorageService();
+
+  List<int> hiveEncryptionKey;
+  try {
+    var decodedHiveKey = await secureStorage.get(hiveEncryptionKeyFieldName);
+    hiveEncryptionKey = base64Decode(decodedHiveKey);
+  } on KeyNotFoundException catch (_) {
+    hiveEncryptionKey = Hive.generateSecureKey();
+    await secureStorage.add(hiveEncryptionKeyFieldName, base64Encode(hiveEncryptionKey));
+  }
+
+  return hiveEncryptionKey;
+}
+
 class App extends StatelessWidget {
   final String applicationDocumentsPath;
+  final List<int> hiveEncryptionKey;
 
-  const App({super.key, required this.applicationDocumentsPath});
+  const App({
+    super.key,
+    required this.applicationDocumentsPath,
+    required this.hiveEncryptionKey,
+  });
 
   @override
   Widget build(BuildContext context) {
+    // We must create the services here to ensure types
+
+    // Factories
+    var appwriteRouteFactory = AppwriteRouteFactory<AppwritePoint>();
+    var appwritePointFactory = AppwritePointFactory();
+
+    // Services
+    var secureStorageService = SecureStorageService();
+    var loggingService = Logger(filePath: applicationDocumentsPath, isRelease: kReleaseMode);
+    var authenticationService = CachedUsernamePasswordAuthenticationService(
+      authenticationService: AppwriteAuthentication(),
+      storageService: secureStorageService,
+    );
+
+    var appwritePointService = AppwritePointService();
+    var appwritePointStorageService = AppwritePointStorageService(
+      storageService: getCachingHiveStorageService("point", applicationDocumentsPath),
+    );
+    var appwriteRouteService = CachedRouteService(
+      routeService: AppwriteRouteService<AppwritePoint>(
+        authenticationService: authenticationService,
+        pointService: appwritePointService,
+      ),
+      storageService: AppwriteRouteStorageService(
+        storageService: getCachingHiveStorageService("route", applicationDocumentsPath),
+        pointStorageService: appwritePointStorageService,
+      )
+    );
+    var appwriteCarService = AppwriteCarService(
+      authenticationService: authenticationService,
+    );
+    var appwriteCalendarEventService = AppwriteCalendarEventService();
+    var locationService = GeolocatorLocationService<AppwritePoint>(
+      locationPointToPointConverter: LocationPointToPointConverter(
+        pointFactory: appwritePointFactory,
+      ),
+    );
+
     return MultiProvider(
       providers: [
-        // Providers
-        Provider<LoggingService>(create: (context) => Logger(filePath: applicationDocumentsPath, isRelease: kReleaseMode)),
-        Provider<AuthenticationService>(create: (context) => CachedUsernamePasswordAuthenticationService(
-          authenticationService: AppwriteAuthentication(),
-          storageService: SecureStorageService(),
-        )),
-        Provider<RouteService>(create: (context) => CachedRouteService(
-          routeService: AppwriteRouteService(
-            authenticationService: Provider.of<AuthenticationService>(context, listen: false)
-          ),
-          storageService: RouteStorageService(
-            storageService: SecureStorageService(),
-          )
-        )),
-        Provider<CarService>(create: (context) => AppwriteCarService(
-          authenticationService: Provider.of(context, listen: false),
-        )),
-        Provider<CalendarEventService>(create: (context) => AppwriteCalendarEventService()),
+        // Factories
+        Provider<route_factory.RouteFactory<AppwriteRoute<AppwritePoint>, AppwritePoint>>(create: (context) => appwriteRouteFactory),
+
+        // Service providers
+        Provider<LoggingService>(create: (context) => loggingService),
+        Provider<AuthenticationService>(create: (context) => authenticationService),
+        Provider<RouteService<AppwriteRoute<AppwritePoint>, AppwritePoint>>(create: (context) => appwriteRouteService),
+        Provider<CarService>(create: (context) => appwriteCarService),
+        Provider<CalendarEventService>(create: (context) => appwriteCalendarEventService),
+        Provider<LocationService<AppwritePoint>>(create: (context) => locationService),
 
         // ChangeNotifierProviders
         // currently no better (auto) injection method found, best case would be something similar to https://git.420joos.dev/ost/seproj/studiapp/-/blob/master/StudiApp/StudiApp/MauiProgram.cs
-        ChangeNotifierProvider(create: (context) => TrackingViewModel(
+        ChangeNotifierProvider<TrackingViewModel>(create: (context) => TrackingViewModel<AppwriteRoute<AppwritePoint>, AppwritePoint>(
           authenticationService: Provider.of(context, listen: false),
           routeService: Provider.of(context, listen: false),
           carService: Provider.of(context, listen: false),
           loggingService: Provider.of(context, listen: false),
+          locationService: Provider.of(context, listen: false),
+          routeFactory: Provider.of(context, listen: false),
         )),
         ChangeNotifierProvider(create: (context) => AuthenticationViewModel(
           authenticationService: Provider.of(context, listen: false),
@@ -96,4 +168,13 @@ class App extends StatelessWidget {
       ),
     );
   }
+
+  HiveStorageService getCachingHiveStorageService(String boxName, String applicationDocumentsDirectory) {
+    return HiveStorageService(
+      dbName: "cache",
+      dbPath: path.join(applicationDocumentsDirectory, "db"),
+      boxName: boxName,
+      encryptionKey: hiveEncryptionKey,
+    );
+  }
 }
diff --git a/car_app/lib/model/appwrite/base_model.dart b/car_app/lib/model/appwrite/base_model.dart
new file mode 100644
index 0000000..bcc4eb3
--- /dev/null
+++ b/car_app/lib/model/appwrite/base_model.dart
@@ -0,0 +1,8 @@
+import 'package:car_app/model/id_model/id_model.dart';
+
+abstract class AppwriteBaseModel implements IdModel<String> {
+  @override
+  final String id;
+
+  AppwriteBaseModel({required this.id});
+}
diff --git a/car_app/lib/model/appwrite/point.dart b/car_app/lib/model/appwrite/point.dart
new file mode 100644
index 0000000..044ff17
--- /dev/null
+++ b/car_app/lib/model/appwrite/point.dart
@@ -0,0 +1,12 @@
+import 'package:car_app/model/appwrite/base_model.dart';
+import 'package:car_app/model/id_model/point.dart';
+
+class AppwritePoint extends AppwriteBaseModel implements PointIdModel<String> {  
+  @override
+  final num latitude;
+  
+  @override
+  final num longitude;
+
+  AppwritePoint({required super.id, required this.latitude, required this.longitude});
+}
diff --git a/car_app/lib/model/appwrite/route.dart b/car_app/lib/model/appwrite/route.dart
new file mode 100644
index 0000000..bf3413a
--- /dev/null
+++ b/car_app/lib/model/appwrite/route.dart
@@ -0,0 +1,19 @@
+import 'package:car_app/model/appwrite/base_model.dart';
+import 'package:car_app/model/id_model/route.dart';
+import 'package:car_app/model/interface/point.dart';
+
+class AppwriteRoute<T extends Point> extends AppwriteBaseModel implements RouteIdModel<String, T> {
+  @override
+  final String? carId;
+
+  @override
+  final Iterable<T> points;
+
+  @override
+  final DateTime startTime;
+
+  @override
+  final DateTime endTime;
+
+  AppwriteRoute({required super.id, required this.carId, required this.points, required this.startTime, required this.endTime});
+}
diff --git a/car_app/lib/model/id_model/id_model.dart b/car_app/lib/model/id_model/id_model.dart
new file mode 100644
index 0000000..7ef45e7
--- /dev/null
+++ b/car_app/lib/model/id_model/id_model.dart
@@ -0,0 +1,3 @@
+abstract interface class IdModel<T> {
+  T get id;
+}
diff --git a/car_app/lib/model/id_model/point.dart b/car_app/lib/model/id_model/point.dart
new file mode 100644
index 0000000..f34b315
--- /dev/null
+++ b/car_app/lib/model/id_model/point.dart
@@ -0,0 +1,4 @@
+import 'package:car_app/model/id_model/id_model.dart';
+import 'package:car_app/model/interface/point.dart';
+
+abstract interface class PointIdModel<T> implements IdModel<T>, Point {}
diff --git a/car_app/lib/model/id_model/route.dart b/car_app/lib/model/id_model/route.dart
new file mode 100644
index 0000000..cc78bf8
--- /dev/null
+++ b/car_app/lib/model/id_model/route.dart
@@ -0,0 +1,5 @@
+import 'package:car_app/model/id_model/id_model.dart';
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/model/interface/route.dart';
+
+abstract interface class RouteIdModel<T, T1 extends Point> implements IdModel<T>, Route<T1> {}
diff --git a/car_app/lib/model/interface/README.md b/car_app/lib/model/interface/README.md
new file mode 100644
index 0000000..de4155e
--- /dev/null
+++ b/car_app/lib/model/interface/README.md
@@ -0,0 +1 @@
+All derivations of these models should override == and hashcode
diff --git a/car_app/lib/model/interface/point.dart b/car_app/lib/model/interface/point.dart
new file mode 100644
index 0000000..b54623c
--- /dev/null
+++ b/car_app/lib/model/interface/point.dart
@@ -0,0 +1,4 @@
+abstract interface class Point {
+  num get longitude;
+  num get latitude;
+}
diff --git a/car_app/lib/model/interface/route.dart b/car_app/lib/model/interface/route.dart
new file mode 100644
index 0000000..199e0c8
--- /dev/null
+++ b/car_app/lib/model/interface/route.dart
@@ -0,0 +1,8 @@
+import 'package:car_app/model/interface/point.dart';
+
+abstract interface class Route<T extends Point> {
+  String? get carId;
+  Iterable<T> get points;
+  DateTime get startTime;
+  DateTime get endTime;
+}
diff --git a/car_app/lib/model/location/point.dart b/car_app/lib/model/location/point.dart
new file mode 100644
index 0000000..3f6b0d8
--- /dev/null
+++ b/car_app/lib/model/location/point.dart
@@ -0,0 +1,11 @@
+import 'package:car_app/model/interface/point.dart';
+
+class LocationPoint implements Point {
+  @override
+  final num latitude;
+
+  @override
+  final num longitude;
+
+  LocationPoint({required this.latitude, required this.longitude});
+}
diff --git a/car_app/lib/model/point.dart b/car_app/lib/model/point.dart
deleted file mode 100644
index 8392b22..0000000
--- a/car_app/lib/model/point.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-class Point {
-  final String? id;
-  final num longitude;
-  final num latitude;
-
-  Point({this.id, required this.longitude, required this.latitude});
-}
\ No newline at end of file
diff --git a/car_app/lib/model/route.dart b/car_app/lib/model/route.dart
deleted file mode 100644
index 1aa6db5..0000000
--- a/car_app/lib/model/route.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-import 'package:car_app/model/point.dart';
-
-class Route {
-  final String? id;
-  final String? carId;
-  final Iterable<Point> points;
-  final DateTime startTime;
-  final DateTime endTime;
-
-  Route({this.id, this.carId, required this.points, required this.startTime, required this.endTime});
-}
diff --git a/car_app/lib/service/appwrite/appwrite_point.dart b/car_app/lib/service/appwrite/appwrite_point.dart
index 9b6d26f..8671ade 100644
--- a/car_app/lib/service/appwrite/appwrite_point.dart
+++ b/car_app/lib/service/appwrite/appwrite_point.dart
@@ -1,11 +1,13 @@
 import 'package:car_app/converter/appwrite_converters/point.dart';
-import 'package:car_app/model/point.dart';
+import 'package:car_app/model/appwrite/point.dart';
 import 'package:car_app/service/appwrite/appwrite_api_function.dart';
+import 'package:car_app/service/interface/point.dart';
 
-class AppwritePointService extends AppwriteApiFunctionService {
+class AppwritePointService extends AppwriteApiFunctionService implements PointService<AppwritePoint> {
   static const String path = "/point";
 
-  Future<Point> add(Point point) async {
+  @override
+  Future<AppwritePoint> add(AppwritePoint point) async {
     var action = "add point";
     var execution = await executeApiFunction(action, "POST", path, body: point.toMap());
 
diff --git a/car_app/lib/service/appwrite/appwrite_route.dart b/car_app/lib/service/appwrite/appwrite_route.dart
index 7f847b1..b5a9eda 100644
--- a/car_app/lib/service/appwrite/appwrite_route.dart
+++ b/car_app/lib/service/appwrite/appwrite_route.dart
@@ -1,28 +1,29 @@
 import 'package:car_app/converter/appwrite_converters/route.dart';
-import 'package:car_app/model/route.dart';
+import 'package:car_app/model/appwrite/route.dart';
+import 'package:car_app/model/id_model/point.dart';
 import 'package:car_app/service/appwrite/appwrite_api_function.dart';
-import 'package:car_app/service/appwrite/appwrite_point.dart';
 import 'package:car_app/service/interface/authentication.dart';
+import 'package:car_app/service/interface/point.dart';
 import 'package:car_app/service/interface/route.dart';
 
-class AppwriteRouteService extends AppwriteApiFunctionService implements RouteService {
+class AppwriteRouteService<T extends PointIdModel> extends AppwriteApiFunctionService implements RouteService<AppwriteRoute<T>, T> {
   static const String path = "/route";
 
-  final AppwritePointService pointService = AppwritePointService();
+  final PointService<T> pointService;
   final AuthenticationService authenticationService;
 
-  AppwriteRouteService({required this.authenticationService});
+  AppwriteRouteService({required this.pointService, required this.authenticationService});
 
   @override
-  Future<Route> add(Route route) async {
+  Future<AppwriteRoute<T>> add(AppwriteRoute<T> route) async {
     var points = await Stream.fromFutures(route.points.map((e) => pointService.add(e))).toList();
     
     var action = "add route";
     var user = await authenticationService.user;
-    var execution = await executeApiFunction(action, "POST", path, body: route.toMap(user.id, points: points));
+    var execution = await executeApiFunction(action, "POST", path, body: route.toMap(user.id, points.map((e) => e.id)));
     
     throwIfExecutionBodyIsEmpty(execution, action);
 
-    return instanceFromExecution(execution, (m) => RouteMapConverter.fromMap(m, points: points), action);
+    return instanceFromExecution(execution, (m) => RouteMapConverter.fromMap(m, points), action);
   }
 }
diff --git a/car_app/lib/service/chached_route.dart b/car_app/lib/service/chached_route.dart
index 44cea9c..0fbc025 100644
--- a/car_app/lib/service/chached_route.dart
+++ b/car_app/lib/service/chached_route.dart
@@ -1,38 +1,37 @@
 import 'package:car_app/exception/key_not_found.dart';
 import 'package:car_app/exception/server.dart';
-import 'package:car_app/model/route.dart';
-import 'package:car_app/service/interface/key_value_storage.dart';
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/model/interface/route.dart';
 import 'package:car_app/service/interface/route.dart';
+import 'package:car_app/service/storage/model_storage/route_storage.dart';
 
-class CachedRouteService implements RouteService {
-  final RouteService routeService;
-  final KeyValueStorageService<String, List<Route>> storageService;
-  final String storageKey = "routes";
+class CachedRouteService<T extends Route<T1>, T1 extends Point> implements RouteService<T, T1> {
+  final RouteService<T, T1> routeService;
+  final RouteStorageService<T, T1> storageService;
 
   CachedRouteService({required this.routeService, required this.storageService});
 
   @override
-  Future<Route> add(Route route) async {
+  Future<T> add(T route) async {
     var isSynchronized = await _synchronize();
 
     if(isSynchronized) {
       try {
         return routeService.add(route);
       } on ServerException catch (_) {
-        await _addRouteToStorage(route);
+        await storageService.add(route);
         return route;
       }
     }
-    await _addRouteToStorage(route);
+    await storageService.add(route);
     return route;
   }
 
   Future<bool> _synchronize() async {
-    List<Route> unsuccessfulRoutes = [];
+    List<T> unsuccessfulRoutes = [];
     try {
-      var routes = await storageService.get(storageKey);
       
-      for (var route in routes) {
+      await for (var route in storageService.getAll()) {
         try {
           await routeService.add(route);
           return true;
@@ -43,17 +42,10 @@ class CachedRouteService implements RouteService {
 
     } on KeyNotFoundException catch (_) {} // No routes stored yet
 
-    storageService.add(storageKey, unsuccessfulRoutes);
-    return false;
-  }
-
-  Future<void> _addRouteToStorage(Route route) async {
-    List<Route> routes = [];
-      try {
-        routes = await storageService.get(storageKey);
-      } on KeyNotFoundException catch (_) {} // Ignore exception, as no routes are stored yet
+    for (var route in unsuccessfulRoutes) {
+      await storageService.add(route);
+    }
 
-      routes.add(route);
-      return storageService.add(storageKey, routes);
+    return false;
   }
 }
diff --git a/car_app/lib/service/location.dart b/car_app/lib/service/geolocator_location.dart
similarity index 82%
rename from car_app/lib/service/location.dart
rename to car_app/lib/service/geolocator_location.dart
index ef4af0e..f00a37a 100644
--- a/car_app/lib/service/location.dart
+++ b/car_app/lib/service/geolocator_location.dart
@@ -3,9 +3,11 @@ import 'dart:ui';
 import 'dart:io' show Platform;
 
 import 'package:car_app/converter/location_converters.dart/point.dart';
-import 'package:car_app/converter/position_point_converter.dart';
+import 'package:car_app/converter/location_converters.dart/position.dart';
 import 'package:car_app/exception/permission.dart';
-import 'package:car_app/model/point.dart';
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/model/location/point.dart';
+import 'package:car_app/service/interface/location.dart';
 import 'package:flutter_background_service/flutter_background_service.dart';
 import 'package:flutter_background_service_android/flutter_background_service_android.dart';
 import 'package:geolocator/geolocator.dart';
@@ -13,24 +15,21 @@ import 'package:permission_handler/permission_handler.dart';
 
 import 'package:car_app/converter/converter.dart';
 
-enum Mode {
-  foreground,
-  background,
-}
-
-class LocationService {
-  static final LocationService _locationService = LocationService._privateConstructor();
+class GeolocatorLocationService<T extends Point> implements LocationService<T> {
   final FlutterBackgroundService _backgroundService = FlutterBackgroundService();
-  final StreamController<Point> _locationStream = StreamController.broadcast();
+  final StreamController<T> _locationStream = StreamController.broadcast();
+  final Converter<LocationPoint, T> locationPointToPointConverter;
 
   late StreamSubscription _backgroundServiceSubscription;
 
-  factory LocationService() => _locationService;
-
-  Stream<Point> get locationUpdate => _locationStream.stream;
+  @override
+  Stream<T> get locationUpdate => _locationStream.stream;
 
-  LocationService._privateConstructor();
+  GeolocatorLocationService({
+    required this.locationPointToPointConverter,
+  });
 
+  @override
   Future<void> start() async {
     var permissionStatus = await Permission.locationWhenInUse.request();
     if (permissionStatus != PermissionStatus.granted) {
@@ -46,15 +45,15 @@ class LocationService {
     );
     _backgroundServiceSubscription = _backgroundService.on('update').listen((event) {
       if (event != null && event.containsKey('point')) {
-        var point = PointMapConverter.fromMap(event['point']);
+        var position = PointMapConverter.fromMap(event['point']);
+        var point = locationPointToPointConverter.convert(position);
         _locationStream.add(point);
       }
     });
     _backgroundService.startService();
   }
 
-  static void _onServiceStart(ServiceInstance service) async {
-    final Converter<Position, Point> converter = PositionToPointConverter();
+  static void _onServiceStart<T extends Point>(ServiceInstance service) async {
     final StreamController<Position> positionUpdate = StreamController<Position>.broadcast();
     late Stream<Position> positionStream;
     final List<Position> backgroundStore = [];
@@ -62,9 +61,8 @@ class LocationService {
     StreamSubscription<Position>? backgroundSubscription;
 
     void sendUpdate(Position position) {
-      var point = converter.convert(position);
-      service.invoke('update',{
-        'point': point.toMap(),
+      service.invoke('update', {
+        'point': position.toLocationPoint().toMap()
       });
     }
 
@@ -134,11 +132,13 @@ class LocationService {
     });
   }
 
+  @override
   Future<void> stop() async {
     _backgroundService.invoke('stop');
     _backgroundServiceSubscription.cancel();
   }
 
+  @override
   void setMode(Mode mode) {
     if (mode == Mode.foreground) {
       _backgroundService.invoke('setAsForeground');
diff --git a/car_app/lib/service/interface/key_value_storage.dart b/car_app/lib/service/interface/key_value_storage.dart
index 53655c0..be17d94 100644
--- a/car_app/lib/service/interface/key_value_storage.dart
+++ b/car_app/lib/service/interface/key_value_storage.dart
@@ -2,4 +2,5 @@ abstract interface class KeyValueStorageService<T, T1> {
   Future<void> add(T key, T1 value);
   Future<T1> remove(T key);
   Future<T1> get(T key);
+  Stream<MapEntry<T, T1>> getAll();
 }
diff --git a/car_app/lib/service/interface/location.dart b/car_app/lib/service/interface/location.dart
new file mode 100644
index 0000000..61ca611
--- /dev/null
+++ b/car_app/lib/service/interface/location.dart
@@ -0,0 +1,13 @@
+import 'package:car_app/model/interface/point.dart';
+
+enum Mode {
+  foreground,
+  background,
+}
+
+abstract interface class LocationService<T extends Point> {
+  Stream<T> get locationUpdate;
+  Future<void> start();
+  Future<void> stop();
+  void setMode(Mode mode);
+}
diff --git a/car_app/lib/service/interface/point.dart b/car_app/lib/service/interface/point.dart
new file mode 100644
index 0000000..4631786
--- /dev/null
+++ b/car_app/lib/service/interface/point.dart
@@ -0,0 +1,5 @@
+import 'package:car_app/model/interface/point.dart';
+
+abstract interface class PointService<T extends Point> {
+  Future<T> add(T point);
+}
diff --git a/car_app/lib/service/interface/route.dart b/car_app/lib/service/interface/route.dart
index 29cf487..e3965c9 100644
--- a/car_app/lib/service/interface/route.dart
+++ b/car_app/lib/service/interface/route.dart
@@ -1,6 +1,7 @@
-import 'package:car_app/model/route.dart';
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/model/interface/route.dart';
 
-abstract interface class RouteService {
-  Future<Route> add(Route route);
+abstract interface class RouteService<T extends Route<T1>, T1 extends Point> {
+  Future<T> add(T route);
   // Future<List<RouteInformation>> getAll();
 }
diff --git a/car_app/lib/service/storage/hive_storage.dart b/car_app/lib/service/storage/hive_storage.dart
new file mode 100644
index 0000000..7b555a4
--- /dev/null
+++ b/car_app/lib/service/storage/hive_storage.dart
@@ -0,0 +1,68 @@
+import 'package:car_app/exception/key_not_found.dart';
+import 'package:car_app/service/interface/key_value_storage.dart';
+import 'package:hive/hive.dart';
+
+class HiveStorageService implements KeyValueStorageService<String, Map<String, dynamic>> {
+  final String dbPath;
+  final String dbName;
+  final String boxName;
+  final List<int> encryptionKey;
+
+  HiveStorageService({
+    required this.dbName,
+    required this.dbPath,
+    required this.boxName,
+    required this.encryptionKey,
+  });
+
+  @override
+  Future<void> add(String key, Map<String, dynamic> value) async {
+    var box = await getBox();
+    box.put(key, value);
+  }
+
+  @override
+  Future<Map<String, dynamic>> get(String key) async {
+    var box = await getBox();
+    var value = await box.get(key);
+
+    if(value == null) {
+      throw KeyNotFoundException(key);
+    }
+    return castHiveResult(value);
+  }
+
+  @override
+  Future<Map<String, dynamic>> remove(String key) async {
+    var box = await getBox();
+    var value = await get(key);
+
+    await box.delete(key);
+    return value;
+  }
+
+  Future<CollectionBox<Map<dynamic, dynamic>>> getBox() async {
+    var collection = await BoxCollection.open(
+      dbName,
+      {boxName},
+      path: dbPath, 
+      key: HiveAesCipher(encryptionKey)
+    );
+
+    return await collection.openBox<Map<dynamic, dynamic>>(boxName);
+  }
+
+  Map<String, dynamic> castHiveResult(Map<dynamic, dynamic> map) {
+    return Map<String, dynamic>.from(map);
+  }
+  
+  @override
+  Stream<MapEntry<String, Map<String, dynamic>>> getAll() async* {
+    var box = await getBox();
+    var keys = await box.getAllKeys();
+
+    for(var key in keys) {
+      yield MapEntry(key, await get(key));
+    }
+  }
+}
diff --git a/car_app/lib/service/storage/model_storage/appwrite/point_storage.dart b/car_app/lib/service/storage/model_storage/appwrite/point_storage.dart
new file mode 100644
index 0000000..ae5328e
--- /dev/null
+++ b/car_app/lib/service/storage/model_storage/appwrite/point_storage.dart
@@ -0,0 +1,17 @@
+import 'package:car_app/converter/storage_converters/appwrite/point.dart';
+import 'package:car_app/model/appwrite/point.dart';
+import 'package:car_app/service/storage/model_storage/point_storage.dart';
+
+class AppwritePointStorageService extends PointStorageService<AppwritePoint> {
+  AppwritePointStorageService({required super.storageService});
+
+  @override
+  AppwritePoint pointFromMap(Map<String, dynamic> map) {
+    return PointMapConverter.fromMap(map);
+  }
+
+  @override
+  Map<String, dynamic> pointToMap(AppwritePoint model) {
+    return model.toMap();
+  }
+}
diff --git a/car_app/lib/service/storage/model_storage/appwrite/route_storage.dart b/car_app/lib/service/storage/model_storage/appwrite/route_storage.dart
new file mode 100644
index 0000000..bc173f7
--- /dev/null
+++ b/car_app/lib/service/storage/model_storage/appwrite/route_storage.dart
@@ -0,0 +1,21 @@
+import 'package:car_app/converter/storage_converters/appwrite/route.dart';
+import 'package:car_app/model/appwrite/route.dart';
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/service/storage/model_storage/route_storage.dart';
+
+class AppwriteRouteStorageService<T extends Point> extends RouteStorageService<AppwriteRoute<T>, T> {
+  @override
+  final String pointIdsFieldName = RouteMapConverter.pointsFieldName;
+
+  AppwriteRouteStorageService({required super.storageService, required super.pointStorageService});
+
+  @override
+  AppwriteRoute<T> routeFromMapWithPoints(Map<String, dynamic> map, Iterable<T> points) {
+    return RouteMapConverter.fromMap<T>(map, points);
+  }
+
+  @override
+  Map<String, dynamic> routeToMapWithPointIds(AppwriteRoute<T> route, Iterable<String> pointIds) {
+    return route.toMap(pointIds);
+  }
+}
diff --git a/car_app/lib/service/storage/model_storage/model_storage.dart b/car_app/lib/service/storage/model_storage/model_storage.dart
new file mode 100644
index 0000000..ea47e4a
--- /dev/null
+++ b/car_app/lib/service/storage/model_storage/model_storage.dart
@@ -0,0 +1,20 @@
+import 'dart:async';
+
+import 'package:car_app/service/interface/key_value_storage.dart';
+import 'package:uuid/uuid.dart';
+
+abstract class ModelStorageService<T> {
+  final KeyValueStorageService<String, Map<String, dynamic>> storageService;
+  final uuid = const Uuid();
+
+  ModelStorageService({required this.storageService});
+
+  Future<String> add(T model);
+  Future<T> get(String key);
+  Future<T> remove(String key);
+  Stream<T> getAll();
+
+  String generateId() {
+    return uuid.v4();
+  }
+}
diff --git a/car_app/lib/service/storage/model_storage/point_storage.dart b/car_app/lib/service/storage/model_storage/point_storage.dart
new file mode 100644
index 0000000..b24d8a7
--- /dev/null
+++ b/car_app/lib/service/storage/model_storage/point_storage.dart
@@ -0,0 +1,34 @@
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/service/storage/model_storage/model_storage.dart';
+
+abstract class PointStorageService<T extends Point> extends ModelStorageService<T> {
+  PointStorageService({required super.storageService});
+
+  Map<String, dynamic> pointToMap(T model);
+  T pointFromMap(Map<String, dynamic> map);
+
+  @override
+  Future<String> add(T model) async {
+    var id = generateId();
+    await storageService.add(id, pointToMap(model));
+
+    return id;
+  }
+
+  @override
+  Future<T> get(String key) async {
+    Map<String, dynamic> map = await storageService.get(key);
+    return pointFromMap(map);
+  }
+
+  @override
+  Future<T> remove(String key) async {
+    var map = await storageService.remove(key);
+    return pointFromMap(map);
+  }
+
+  @override
+  Stream<T> getAll() {
+    return storageService.getAll().map((e) => pointFromMap(e.value));
+  }
+}
diff --git a/car_app/lib/service/storage/model_storage/route_storage.dart b/car_app/lib/service/storage/model_storage/route_storage.dart
new file mode 100644
index 0000000..e4efa5e
--- /dev/null
+++ b/car_app/lib/service/storage/model_storage/route_storage.dart
@@ -0,0 +1,65 @@
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/model/interface/route.dart';
+import 'package:car_app/service/storage/model_storage/model_storage.dart';
+import 'package:car_app/service/storage/model_storage/point_storage.dart';
+
+abstract class RouteStorageService<T extends Route<T1>, T1 extends Point> extends ModelStorageService<T> {
+  final PointStorageService<T1> pointStorageService;
+
+  String get pointIdsFieldName;
+
+  RouteStorageService({required super.storageService, required this.pointStorageService});
+
+  T routeFromMapWithPoints(Map<String, dynamic> map, Iterable<T1> points);
+  Map<String, dynamic> routeToMapWithPointIds(T route, Iterable<String> pointIds);
+
+  @override
+  Future<String> add(T model) async {
+    var pointIds = <String>[];
+    for (var point in model.points) {
+      pointIds.add(await pointStorageService.add(point));
+    }
+
+    var id = generateId();
+    await storageService.add(id, routeToMapWithPointIds(model, pointIds));
+    return id;
+  }
+
+  @override
+  Future<T> get(String key) async {
+    return routeFromMap(await storageService.get(key));
+  }
+
+  @override
+  Stream<T> getAll() {
+    return storageService.getAll().asyncMap((e) async => await routeFromMap(e.value));
+  }
+
+  @override
+  Future<T> remove(String key) async {
+    var map = await storageService.get(key);
+    var item = await routeFromMap(map);
+    var pointIds = map[pointIdsFieldName] as List<String>;
+
+    var futures = <Future<T1>>[];
+    for (var pointId in pointIds) {
+      futures.add(pointStorageService.remove(pointId));
+    }
+
+    await Future.wait(futures);
+    await storageService.remove(key);
+
+    return item;
+  }
+
+  Future<T> routeFromMap(Map<String, dynamic> map) async {
+    var pointIds = map[pointIdsFieldName] as List<String>;
+
+    var points = <T1>[];
+    for (var pointId in pointIds) {
+      points.add(await pointStorageService.get(pointId));
+    }
+
+    return routeFromMapWithPoints(map, points);
+  }
+}
diff --git a/car_app/lib/service/storage/route_storage.dart b/car_app/lib/service/storage/route_storage.dart
deleted file mode 100644
index 19eb7c4..0000000
--- a/car_app/lib/service/storage/route_storage.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-import 'dart:convert';
-
-import 'package:car_app/converter/storage_converters/route.dart';
-import 'package:car_app/model/route.dart';
-import 'package:car_app/service/interface/key_value_storage.dart';
-
-class RouteStorageService implements KeyValueStorageService<String, List<Route>> {
-  final KeyValueStorageService<String, String> storageService;
-
-  RouteStorageService({required this.storageService});
-
-  @override
-  Future<void> add(String key, List<Route> value) {
-    var json = jsonEncode(value.map((e) => e.toMap()).toList());
-    return storageService.add(key, json);
-  }
-
-  @override
-  Future<List<Route>> get(String key) async {
-    var json = await storageService.get(key);
-    return _getRoutesFromJson(json);
-  }
-
-  @override
-  Future<List<Route>> remove(String key) async {
-    var json = await storageService.remove(key);
-    return _getRoutesFromJson(json);
-  }
-
-  List<Route> _getRoutesFromJson(String json) {
-    var routeMaps = List<Map<String, dynamic>>.from(jsonDecode(json));
-    return routeMaps.map((e) => RouteMapConverter.fromMap(e)).toList();
-  }
-}
diff --git a/car_app/lib/service/storage/secure_storage.dart b/car_app/lib/service/storage/secure_storage.dart
index 4b2e0e1..3c93e42 100644
--- a/car_app/lib/service/storage/secure_storage.dart
+++ b/car_app/lib/service/storage/secure_storage.dart
@@ -26,4 +26,12 @@ class SecureStorageService implements KeyValueStorageService<String, String> {
     await _storage.delete(key: key);
     return value;
   }
+  
+  @override
+  Stream<MapEntry<String, String>> getAll() async* {
+    var map = await _storage.readAll();
+    for (var entry in map.entries) {
+      yield MapEntry(entry.key, entry.value);
+    }
+  }
 }
diff --git a/car_app/lib/view/tracking.dart b/car_app/lib/view/tracking.dart
index d71a1d5..b2bd7df 100644
--- a/car_app/lib/view/tracking.dart
+++ b/car_app/lib/view/tracking.dart
@@ -1,6 +1,6 @@
 import 'package:car_app/converter/point_latlng_converter.dart';
 import 'package:car_app/exception/server.dart';
-import 'package:car_app/model/point.dart';
+import 'package:car_app/model/interface/point.dart';
 import 'package:car_app/view/defaults.dart';
 import 'package:car_app/view/routes_titles.dart';
 import 'package:car_app/viewmodel/tracking.dart';
diff --git a/car_app/lib/viewmodel/tracking.dart b/car_app/lib/viewmodel/tracking.dart
index b6067dc..fffa7eb 100644
--- a/car_app/lib/viewmodel/tracking.dart
+++ b/car_app/lib/viewmodel/tracking.dart
@@ -1,13 +1,14 @@
 import 'dart:async';
 
 import 'package:car_app/exception/permission.dart';
-import 'package:car_app/model/point.dart';
-import 'package:car_app/model/route.dart';
+import 'package:car_app/factory/interface/route.dart';
+import 'package:car_app/model/interface/point.dart';
+import 'package:car_app/model/interface/route.dart';
 import 'package:car_app/service/interface/authentication.dart';
 import 'package:car_app/service/interface/car.dart';
+import 'package:car_app/service/interface/location.dart';
 import 'package:car_app/service/interface/logging.dart';
 import 'package:car_app/service/interface/route.dart';
-import 'package:car_app/service/location.dart';
 import 'package:flutter/foundation.dart';
 
 enum TrackingState {
@@ -34,13 +35,14 @@ extension TrackingButtonText on TrackingState {
   }
 }
 
-class TrackingViewModel extends ChangeNotifier {
+class TrackingViewModel<T extends Route<T1>, T1 extends Point> extends ChangeNotifier {
   final AuthenticationService authenticationService;
-  final RouteService routeService;
+  final RouteService<T, T1> routeService;
   final CarService carService;
-  final LocationService _locationService = LocationService();
+  final LocationService<T1> locationService;
   final LoggingService loggingService;
-  final List<Point> _points = [];
+  final RouteFactory<T, T1> routeFactory;
+  final List<T1> _points = [];
 
   late StreamSubscription <Point> _locationSubscription;
   late DateTime _trackingStartTime;
@@ -50,14 +52,21 @@ class TrackingViewModel extends ChangeNotifier {
   TrackingState _trackingState = TrackingState.stopped;
   String get trackingButtonText => _trackingState.text;
 
-  TrackingViewModel({required this.authenticationService, required this.routeService, required this.carService, required this.loggingService});
+  TrackingViewModel({
+    required this.authenticationService,
+    required this.locationService,
+    required this.routeService,
+    required this.carService,
+    required this.loggingService,
+    required this.routeFactory,
+  });
 
   void locationServiceToForeground() {
-    _locationService.setMode(Mode.foreground);
+    locationService.setMode(Mode.foreground);
   }
 
   void locationServiceToBackground() {
-    _locationService.setMode(Mode.background);
+    locationService.setMode(Mode.background);
   }
 
   void setCarId(String carId) {
@@ -93,12 +102,12 @@ class TrackingViewModel extends ChangeNotifier {
 
     _setTrackingState(TrackingState.starting);
 
-    _locationSubscription = _locationService.locationUpdate.listen((p) {
+    _locationSubscription = locationService.locationUpdate.listen((p) {
       onUpdate(p);
       _points.add(p);
     });
 
-    _locationService.start().then((_) {
+    locationService.start().then((_) {
       _setTrackingState(TrackingState.started);
     })
     .catchError(test: (e) => e is PermissionException, (e, s) {
@@ -111,9 +120,10 @@ class TrackingViewModel extends ChangeNotifier {
   void _stopTracking() {
     _setTrackingState(TrackingState.stopping);
     _locationSubscription.cancel();
-    _locationService.stop().then((_) async {
+    locationService.stop().then((_) async {
       _setTrackingState(TrackingState.stopped);
-      await routeService.add(Route(points: _points, carId: _carId, startTime: _trackingStartTime, endTime: DateTime.now()));
+      var route = routeFactory.createInstance(_carId, _points, _trackingStartTime, DateTime.now());
+      await routeService.add(route);
     });
   }
 
diff --git a/car_app/pubspec.lock b/car_app/pubspec.lock
index c55cdd4..c5ce58e 100644
--- a/car_app/pubspec.lock
+++ b/car_app/pubspec.lock
@@ -384,6 +384,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "0.2.2"
+  hive:
+    dependency: "direct main"
+    description:
+      name: hive
+      sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.2.3"
   html:
     dependency: transitive
     description:
@@ -553,7 +561,7 @@ packages:
     source: hosted
     version: "2.0.1"
   path:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: path
       sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
@@ -886,13 +894,13 @@ packages:
     source: hosted
     version: "3.1.1"
   uuid:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: uuid
-      sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
+      sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
       url: "https://pub.dev"
     source: hosted
-    version: "4.3.3"
+    version: "4.4.0"
   vector_math:
     dependency: transitive
     description:
diff --git a/car_app/pubspec.yaml b/car_app/pubspec.yaml
index ac4f2f2..ed42bcb 100644
--- a/car_app/pubspec.yaml
+++ b/car_app/pubspec.yaml
@@ -45,6 +45,9 @@ dependencies:
   path_provider: ^2.1.2
   table_calendar: ^3.1.1
   intl: ^0.19.0
+  hive: ^2.2.3
+  uuid: ^4.4.0
+  path: ^1.9.0
 
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
-- 
GitLab