server – Parse Back4App database as a master database change log rather than actual database- thoughts on this design principle?

first post here. I’m looking for some general input as to the correctness of what I’m trying to do here. Is this a good idea, bad idea, what could be plus points or negative aspects to this design?

If this is not the right forum for this question please advise where is best, thanks 🙂

What I’m envisioning is using the Parse Back4App database as a “master database event log” which would act as a master database change log for any user accounts connected to one database. Full databases are stored with Hive on client devices. Any changes made are pushed to the server as log events. These will be read by client devices as instructions for any local database changes that need to be made so all databases stay in sync. This will be a last to write wins design. This database would be auto-deleting for data older than say every 3 months to ensure all users are updated and so that the data transfer doesn’t get too large.

My code is a bit messy as I haven’t refactored too much so please bear with that. I’m simply looking for advice as to if this is a good idea or not, and any other advice as per first paragraph.

Secondly (possibly this part belongs in SO) but if anyone wants to make general suggestions for my encryption issue please see the readMe file under MAJOR ISSUE on GitHub.

Example database log with create & delete actions logged.
Example database log

FULL PROJECT

Book model

import 'package:hive/hive.dart';
part 'book_model.g.dart';

@HiveType(typeId: 0)
class Book {
  @HiveField(0)
  String title;

  @HiveField(1)
  String author;

  @HiveField(2)
  DateTime publishingDate;

  @HiveField(3)
  DateTime dateAdded;

  @HiveField(4)
  DateTime lastModified;

  Book({
    this.title,
    this.author,
    this.publishingDate,
    this.dateAdded,
    this.lastModified,
  });

  @override
  String toString() {
  return '''
  title: $title
  author: $author
  publishingDate: $publishingDate
  dateAdded: $dateAdded
  lastModified $lastModified
  ''';
   }

  Book.fromJson(Map<String, dynamic> json)
      : title = json('title'),
        author = json('author'),
        publishingDate = json('publishingDate'),
        dateAdded = json('dateAdded'),
        lastModified = json('lastModified');

  Map<String, dynamic> toJson() => {
        'title': title,
        'author': author,
        'publishingDate': publishingDate,
        'dateAdded': dateAdded,
        'lastModified': lastModified
      };
}

Database sync item model

import 'book_model.dart';
import 'package:hive/hive.dart';
part 'database_sync_model.g.dart';

@HiveType(typeId: 1)
class DatabaseSyncItem {
  @HiveField(0)
  Book previousBookValue;

  @HiveField(1)
  Book updatedBookValue;

  @HiveField(2)
  DateTime dateAdded;

  @HiveField(3)
  DateTime lastModified;

  @HiveField(4)
  DatabaseAction entryAction;

  DatabaseSyncItem({
    this.previousBookValue,
    this.updatedBookValue,
    this.dateAdded,
    this.lastModified,
    this.entryAction,
  });

  @override
  String toString() {
  return '''
  previousValue: $previousBookValue
  updatedValue: $updatedBookValue
  dateAdded: $dateAdded
  lastModified: $lastModified
  entryAction: $entryAction
  ''';
   }

  // Turn json back into data model
  DatabaseSyncItem.fromJson(Map<String, dynamic> json)
      : previousBookValue = json('previousBookValue'),
        updatedBookValue = json('updatedBookValue'),
        dateAdded = json('dateAdded'),
        lastModified = json('lastModified'),
        entryAction = json('entryAction');

  // Turn data model into json
  Map<String, dynamic> toJson() => {
        'previousBookValue': previousBookValue,
        'updatedBookValue': updatedBookValue,
        'dateAdded': dateAdded,
        'lastModified': lastModified,
        'entryAction': entryAction,
      };
}

enum DatabaseAction {
  create,
  update,
  delete,
}

Local database services

import 'package:hive/hive.dart';
import 'package:service_database_sync/models/book_model.dart';
import 'package:service_database_sync/services/server_database_services.dart';

class ClientDatabaseServices {
  final String hiveBox = 'book_box';
  List<Book> _bookList = ();
  Book _activeBook;
  ServerDatabaseServices parseAction = ServerDatabaseServices();

  ///
  /// CREATE EVENT
  ///
  // Add book to database & update list
  Future<void> addBook(Book newBook) async {
    var box = await Hive.openBox<Book>(hiveBox);
    await box.add(newBook);
    _bookList = box.values.toList();
    await parseAction.logCreateEvent(newBook);
  }

  ///
  /// READ EVENTS
  ///
  // Send database items to list
  Future<void> _databaseToRepository() async {
    var box = await Hive.openBox<Book>(hiveBox);
    _bookList = box.values.toList();
  }

  // Return list for use
  Future<List<Book>> getBookList() async {
    await _databaseToRepository();
    return _bookList;
  }

  // Getter for list
  List<Book> get bookList => _bookList;

  // Return specific book
  Book getBook(index) {
    return _bookList(index);
  }

  // Return list length
  int get bookCount {
    return _bookList.length;
  }

  // Get active book
  Book getActiveBook() {
    return _activeBook;
  }

  // Set active book
  void setActiveBook(key) async {
    var box = await Hive.openBox<Book>(hiveBox);
    _activeBook = box.get(key);
  }

  ///
  /// UPDATE EVENT
  ///
  // Updates specific book with new data
  void editBook({Book book, int bookKey}) async {
    var box = await Hive.openBox<Book>(hiveBox);
    await box.put(bookKey, book);
    _bookList = box.values.toList();
    _activeBook = box.get(bookKey);
  }

  ///
  /// DELETE EVENTS
  ///
  // Deletes specific book and updates list
  Future<void> deleteBook(key) async {
    var box = await Hive.openBox<Book>(hiveBox);
    await parseAction.logDeleteEvent(box.getAt(key));
    await box.deleteAt(key);
    _bookList = box.values.toList();
  }

  // Empties hive box for database reset
  Future<void> deleteAll() async {
    var box = await Hive.openBox<Book>(hiveBox);
    await box.clear();
  }
}

Server database services

import 'package:parse_server_sdk/parse_server_sdk.dart';
import 'package:service_database_sync/models/book_model.dart';
import 'package:service_database_sync/models/database_sync_model.dart';

class ServerDatabaseServices {
  // Server app keys & data
  final keyApplicationId = 'jVrkUb6tvSheT4NHqGuF9FtFDtQkmqS3pJbKRyLN';
  final keyClientKey = 'MFYPnwLM1d38TtG2523YXxMQ4lCZdX9maovSjrdu';
  final keyParseServerUrl = 'https://parseapi.back4app.com';

  // String values
  String previousBookValue = 'previousBookValue';
  String updatedBookValue = 'updatedBookValue';
  String dateAdded = 'dateAdded';
  String entryAction = 'entryAction';
  String lastModified = 'lastModified';

  ///
  ///
  /// CREATION LOG EVENT
  Future<void> logCreateEvent(Book book) async {
    final createEvent = DatabaseSyncItem(
      previousBookValue: null,
      updatedBookValue: book,
      dateAdded: book.dateAdded,
      entryAction: DatabaseAction.create,
      lastModified: DateTime.now(),
    );
    final toServer = ParseObject('Event')
      ..set(previousBookValue, createEvent.previousBookValue)
      ..set(updatedBookValue, createEvent.updatedBookValue.toJson())
      ..set(dateAdded, createEvent.dateAdded)
      ..set(entryAction, createEvent.entryAction.toString())
      ..set(lastModified, createEvent.lastModified);
    await toServer.save();
  }

  ///
  ///
  /// UPDATE LOG EVENT
  Future<void> logEditEvent(Book previousValue, Book updatedValue) async {
    updatedValue.lastModified = DateTime.now();
    final editEvent = DatabaseSyncItem(
      previousBookValue: previousValue,
      updatedBookValue: updatedValue,
      dateAdded: previousValue.dateAdded,
      entryAction: DatabaseAction.update,
      lastModified: DateTime.now(),
    );
    final toServer = ParseObject('Event')
      ..set(previousBookValue, editEvent.previousBookValue.toJson())
      ..set(updatedBookValue, editEvent.updatedBookValue.toJson())
      ..set(dateAdded, editEvent.dateAdded)
      ..set(entryAction, editEvent.entryAction.toString())
      ..set(lastModified, editEvent.lastModified);
    await toServer.save();
  }

  ///
  ///
  /// DELETE LOG EVENT
  Future<void> logDeleteEvent(Book book) async {
    book.lastModified = DateTime.now();
    final deleteEvent = DatabaseSyncItem(
      previousBookValue: book,
      updatedBookValue: null,
      dateAdded: book.dateAdded,
      entryAction: DatabaseAction.delete,
      lastModified: DateTime.now(),
    );
    final toServer = ParseObject('Event')
      ..set(previousBookValue, deleteEvent.previousBookValue.toJson())
      ..set(updatedBookValue, deleteEvent.updatedBookValue)
      ..set(dateAdded, deleteEvent.dateAdded)
      ..set(entryAction, deleteEvent.entryAction.toString())
      ..set(lastModified, deleteEvent.lastModified);
      await toServer.save();
  }
}

Main

import 'dart:io';
import 'package:hive/hive.dart';
import 'package:parse_server_sdk/parse_server_sdk.dart';
import 'package:service_database_sync/data/books_hardcoded.dart';
import 'package:service_database_sync/models/book_model.dart';
import 'package:service_database_sync/services/client_database_services.dart';
import 'package:service_database_sync/services/server_database_services.dart';

Future<void> main(List<String> arguments) async {
  Hive.init('hive_database');
  Hive.registerAdapter(BookAdapter());
  await Parse().initialize(
    ServerDatabaseServices().keyApplicationId,
    ServerDatabaseServices().keyParseServerUrl,
    clientKey: ServerDatabaseServices().keyClientKey,
    debug: true,
  );

  await addBooksToLibrary();
  sleep(Duration(seconds: 5));
  await deleteSpicificBook(2);
}

Future<void> addBooksToLibrary() async {
  final bookDatabaseActions = ClientDatabaseServices();
  await bookDatabaseActions.addBook(bookOne);
  await bookDatabaseActions.addBook(bookTwo);
  await bookDatabaseActions.addBook(bookThree);
  await bookDatabaseActions.addBook(bookFour);
}

Future<void> deleteSpicificBook(int index) async {
  final bookDatabaseActions = ClientDatabaseServices();
  await bookDatabaseActions.deleteBook(index);
}

Future<void> deleteAllBooks() async {
  final bookDatabaseActions = ClientDatabaseServices();
  await bookDatabaseActions.deleteAll();
}

void printBookLibrary() async {
  final bookDatabaseActions = ClientDatabaseServices();
  final box = await Hive.openBox<Book>(bookDatabaseActions.hiveBox);
  final books = box.values;
  print(books);
}