Isolating DB layer dependency for a better app architecture: a use case with Realm


If you are an iOS developer and you haven’t been hidden in the middle of nowhere for the last three years, you should know what Realm is: basically it’s an awesome dependency to you app.

We have seen during the latest years that other dependencies that helped a lot of people to build thousand of apps have been shut down. Yes I’m referring to Parse.

So I’m going to use Realm for my latest side project but the main goal for me now is to completely (or as much as possible) isolate the persistence layer from it’s concrete implementation (yes, you know, SOLID is just around the corner).

The first goal of introducing a new layer like this one is that any client would be able to program to an interface without caring at all about the persistence layer implementation. Another reason is that you could easily change database implementation or the persistence layer without affeting the rest of the codebase.

A quick Realm example:

(taken from the Realm doc)

The main components you are going to use when you import Realm in your projects are:

  • Realm() that is basically the file in which data is stored;
  • class subclassing Object: the entities stored in the database;
  • realm.write { }: the block in which every write transition should happen;

Let’s break dependencies:

The final code for this article is on github.

The basic usage of a database is to perform CRUD operations over collections of entities.An entity in our context is any subclass of Realm.Object.

So a generic function to fetch data could be something like:

But obviously this means that your API will contain a direct dependency to RealmSwift.

The idea is to remove this dependency creating a dummy protocol like this:

So our delightful API could become something like:

This could look like a silly move but it could lead us to share this fetch API, for instance, with Core Data Entities.

In that case all we need is to create

and that it’s. Also the Core Data Entities could be used with our new API. It’s silly to say but this is an interesting first step to create a shared API across multiple persistence frameworks.

Create an High Level API:

Now that we are convinced that we can create a more abstracted API, let’s do it!

Here we go, everything appears quite easy so far. Basically we have built basic CRUD operations to:

  • create an empty object
  • save an object
  • update an object
  • delete an object / delete all objects of a certain type
  • retrieve objects based on the type.

If you are wondering what Sorted is, it’s just a struct like this one:

Move to the concrete implementation:

Now we are ready to provide a concrete implementation based on Realm. The first step is to configure a new RealmStorageContext that adopts our StorageContext. The first stage is to inject in some way a configuration within the concrete Storage.

As you may know we can instantiate Realm to work with the concrete database (Realm.Configuration.defaultConfiguration) or to be stored in memory (setting configuration.inMemoryIdentifier). For this reason I have extended the StorageContext protocol to include a quite abstract:

And the implementation of this init could be something like:

Now, once the RealmStorageContext is instantiated, we have a backed Realm object to work with for our operations.

The latest step is to implement concrete CRUD operations specifically for Realm:

  • write and update operations
  • delete operations
  • fetch operation

Test time!

Well now, in order to understand the usage of this code, I’ve written few unit tests that use the RealmStorageContext:

Instead of focusing on the tests details what I want to underline here is that basically we are testing the persistence layer without importing RealmSwift, so we can assume that the dependency is finally broken 🎉

PS: you can find the test suite here (link).

Create Storage Context Stubs:

The latest step the can justify the presence of this layer is that if a component uses this client, we are able to test the component without the concrete database implementation.

Let’s consider that we have this class in which dummyMethod returns true if the method fetches more 10 objects, otherwise it returns false.

In order to test that method we are going to create a stub for StorageContext in this way:

And finally we can test that method:

Conclusion:

  1. the most curious reader could notice that in the RealmStorageContext exposes the realm and this is quite dangerous but now I want to let the client the possibility to subscript, for instance, to the Realm notifications;
  2. Yes, we have added a new layer that should be maintained and fixed over the time but this layer provide a quite powerful abstraction that can help us in the future replacing (It’s not about you Realm guys ☺️) the backed persistence framework;
  3. Even though these unit test are quite wrong (because I’m testing how Realm works instead of testing the persistence flow 🤔), the idea is that our unit test suites are not importing RealmSwift at all! (dependency broken!);
  4. Yes, having a single protocol for our storage makes the creation of the Stub a lot of useless code (we have to add a ton of empty methods); the solution is to create multiple protocols to separate the logic of the DB API;

The repository for this article is on Github.

Feel free to contact me on Twitter for any comment. I’d love to hear any kind of feedback! ✌🏼

Thanks for reading! 👽