0

I'm creating a prototype mobile app that will store data locally in an sqLite database and also on the cloud (CloudKit for now). The cloud storage uses a certain way of storing the data, and sqlite uses another.

my class looks like this:

    public class MyDataClass {
        public int ID;
        public string Name;
        public string SomeOtherData;

        // in reality this has some more logic, and implements INotifyPropertyChanged for all 
    }

As I see it, there are two ways to store the data:


  1. Have the class be a pure data class that doesn't know or care about its storage methods. Then duplicate the class and copy data for each data store. So the data store copies would contain all the boilerplate. For example [PrimaryKey] for sqlite and "NamePropertyKey" for CloudKit, etc unique to each storage method.

This method has the advantage of the model class itself being very clean and easy to maintain. But now if I add or refactor a field, I have to change it in 3 spots to sync the changes to the databases.

So here you'd have my above class, plus another class for SQL Looking like this:

    public class MySQLDataClass {

        
        //Constructor needed here to map all the fields over from base data class
        
        
        [PrimaryKey] [Column("id")]
        public int ID { get; set; }
        
        [Column("name")]
        public string Name { get; set; }
        
        [Column("someotherdata")]
        public string SomeOtherData { get; set; }


    }

Plus a similar class for CloudKit


  1. Have all the boilerplate garbage included in the main data class. This would clutter up the class significantly.

This method has the advantage that there is a single version of truth. I only have to add a new field in one spot. However, It couples the data model heavily to the storage methods. If I decide to drop support for Cloudkit, or add support for AWS, I would need to heavily modify my data class, which might break the local database functionality.

    public class MyCombinedDataClass {
        
        //for cloudkit
        const string NamePropertyKey = nameof(Name);
        
        // No Constructor needed to map fields, since this is base data class
        
        [PrimaryKey] [Column("id")]
        public int ID { get; set; }
        
        // This is for cloudkit since it can't really use ID.
        public Guid guid { get; set; }
        
        [Column("name")]
        public string Name { get; set; }
        
        [Column("someotherdata")]
        public string SomeOtherData { get; set; }
        
        // plus more boilerplate plus INotifyPropertyChanged
    }

I'm currently going with option 2. But I can see it getting unwieldy as my data class is growing out of control with only a few fields so far.

I'm sure this is a pretty common problem in apps that have multiple sources of data storage. Are there any ways to elegantly deal with this problem? Any ideas how to improve my design?

For completeness I'm currently coding for Xamarin.IOS as a proof-of-concept, but have future plans to support android using another database system other than CloudKit.

1 Answer 1

3

Rule of thumb:

  • If 80% to 90% of the code will be equal, and you have to maintain only 10 to 20% of invidual fields for each storage type, keep it in one class. For example, in your example, you have shown us one constant and one field to be individual for CouldKit, but everything else (maybe even the unshown parts) seems to be identical. That does not look like "a significant amount of extra clutter" to me.

  • If only 50% of the code (or less) can be used for both storage types, and 50% or more is individual, use separate classes

And for something between 50 and 80%, use your best judgement.

Also, you should probably try to work towards the 90% (or more), by avoiding to implement too much database specific logic directly inside your data class. Instead, use repository classes for the CRUD operations, so you have a SqliteRepository and a CloudKitRepository which implement the invidual operations, managing the (mostly) database agnostic instances of your data classes. Both repositories should have a common interface, so you can clearly separate database specific and database agnostic code.

1
  • 1
    That sounds like reasonable advice. There is a fair amount of boilerplate though like foreign keys, etc. I will definitely try to keep as much logic out of the class as possible. In some further reading I came across a class in c# called Automapper. Which might make option 1 a bit more appealing than before.
    – Adam B
    Commented Nov 19, 2021 at 6:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.