A Repository Pattern Implementation for  Firestore Subcollections
Tags: firestore, java
Published: 2019-09-17 05:53:27

If you already use [Google Cloud Firestore](https://firebase.google.com/docs/firestore), you probably know its [data model](https://firebase.google.com/docs/firestore/data-model) and you noticed that **Subcollections** are one of the most interesting features.
I met them for the first time while working on my personal pet-project (a project where I can experiment with new technologies and methodologies). 

So, I wondered if there was a way to adapt the **Repository Pattern** to deal with Firestore Subcollections.
The main aspect to address was that in the Repository Pattern each repository is usually related to a specific model, in other words, it is related to a type. But reading from [Google documentation page](https://firebase.google.com/docs/firestore/data-model#subcollections):
> A Subcollection is a collection associated with a specific document.

This means that, for a subcollection, a repository is not related to a type but to a specific _Document_.

Let's see this experiment results... :microscope:

## Scenario
To get the point imagine this practical example:
We have to deal with "Houses" and each of these have some "Rooms" (a bathroom, a kitchen, ...). In each room there is the "Furniture" (a bed, two chairs and so on).
To model this scenario we can imagine to implement these entities:

+ Furniture Item
+ Room
+ House

and they will be related in this way:
```
Houses   <--- a collection
 |
 |--- My-House <---a Document of Houses
        |--Address <---- a field of "My-House" document
        |--Rooms   <----- a Subcollection of "My-House"
            |
            |--Kitchen
            |--Bathroom
            |--Bedroom <--- a document of Rooms
                    |
                    |--FurnitureItems  <--- a Subcollection of Bedroom document
                            |
                            |-- Bed
                            |-- Wardrobe

```


## The Repository

For the sake of simplicity let's focus on these two methods
+ "Add" adds a document to the collection
+ "GetById" returns a document by its Id

Speaking in terms of interface we have:

```java
public interface IRepository<T>{
    Either<String,String> Add(T data);
    Either<String, T> GetById(String id);
}
```


Ignoring for a moment Subcollections, an implementing class could be:
``` java
public  class  FirestoreRepository<T> implements IRepository<T>{

    private CollectionReference collection;
    private Class<T> classType;
    
    public FirestoreRepository(Firestore firestore, String collectionPath, Class<T> classType){
        this.collection = firestore.collection(collectionPath);
        this.classType = classType;
    }
    
    public Either<String,String> Save(T data){ 
        try{
            ApiFuture<DocumentSnapshot> futureResult = collection.add(data).get().get();
            DocumentSnapshot result = futureResult.get();
            return Either.right(result.getId());
        }catch(Exception ex){
            return Either.left(ex.getMessage());
        }
    }

    public Either<String, T> GetById(String id) {
        try{
            ApiFuture<DocumentSnapshot> futureEntry = collection.document(id).get();
            DocumentSnapshot entry = futureEntry.get();
            return entry.exists()
              	? Either.right(entry.toObject(classType))
		        : Either.left("Data not found for id: "+id);
        }catch(Exception ex){
            return Either.left(ex.getMessage());
        }
    }
}
```
___
**Note:**
The GoogleApi methods to retrieve a _DocumentSnasphot_ are async, they indeed returns an [ApiFuture](https://googleapis.github.io/api-common-java/1.1.0/apidocs/com/google/api/core/ApiFuture.html)<DocumentSnapshot> (a GoogleAPI implementation of java Future).

For the sake of simplicity, I chose to avoid the async approach blocking the execution until the future is complete. 
Yes, I know, It's not a good idea to do in real projects :nerd_face:
___

The above implementation assumes that all the collection are at root level, in the constructor the collection reference is indeed created from the firestore object:
```java
this.collection = firestore.collection(collectionPath);
```

This assumption is not correct when we use the firestore Subcollections. A Subcollection is related to a specific document, so we need
a _DocumentReference_ to get one of its collections.
For example:

```java
CollectionReference furnitureCollection = firestore
        .collection("Houses")
        .document("My-House")
        .collection("Rooms")
        .document("bathroom")
        .collection("FurnitureItems");
```
We notice that we need to chain _CollectionReferences_ and _DocumentReference_ to access to the right Subcollection.

So, as first, we need to modify the constructor of our FirestoreRepository to accept a _CollectionReference_.

```java
public FirestoreRepository(CollectionReference collection, Class<T> classType){
        this.collection = collection;
        this.classType = classType;
    }
```

In this way we have removed the assumption that each collection is at root level.

## The Repository Factory
At this point let me introduce the **RepositoryFactory** whose responsibility is:
+ chain _CollectionReferences_ and _DocuementReferences_
+ create an IRepository instance for a specific Collection (or Subcollection).

For example, if we want to get the Repository for the "FurnitureItems"
of the document "bathroom" relative to the "My-House", we expect to have something like:
```java
  IRepository<FurnitureItem> furnitureRepo = repoFactory
        .FromDocument(House.class, "My-House")
        .FromDocument(Room.class, "bathroom")
        .GetRepository(FurnitureItem.class);

    furnitureRepo.Add(new FurnitureItem("chair"));
```

Otherwise, if we want to query through the _House_ collection we should have:

```java
  IRepository<House> houseRepo = repoFactory.GetRepository(FurnitureItem.class);
  houseRepo.Add(new House());
```

We notice that the method _FromDocument_ should return a new instance of RepositoryFactory. This allows us to keep a _RepositoryFactory_ reference to access all its Subcollections.
For example consider that a "House", in addition to the "Rooms" collection, has a "Tenants" collection. In this scenario we can write:

```java
RepositoryFactory myHouseRepoFactory = repoFactory.FromDocument(House.class, "MyHouse");

myHouseRepoFactory
    .FromDocument(Room.class, "kitchen") //this do not modify the collection root of myHouseRepoFactory
    .GetRepository(FurnitureItem.class)
    .Add(new FurnitureItem());

myHouseRepoFactory
    .GetRepository(Tenant.class)
    .Add(new Tenant());

```

## Design the RepositoryFactory :triangular_ruler: :pencil2:

Bearing in mind everything, our _RepositoryFactory_ needs:

1. A map to link the Model classType with a collection name
2. A pointer to create a repository: if the repo is at root level then it should be a firestore instance, a _DocumentReference_ otherwise.
3. A method to move the pointer to the next _DocumentReference_ returning a new _RepositoryFactory_ instance.
4. A method to get the _IRepository_ instance for a specified collection (or _Subcollection_)

#### The Model-Collection Map

To link each model class type with the relative collection name we use a map like this:
```java
private final static Map<Class<?>, String> collectionMap = Map.of(
        House.class, "Houses",
        Room.class, "Rooms",
        FurnitureItem.class, "Furniture"
    );   
```

#### The Pointer
Keep in mind that we have two kinds of pointer:
- The _Firestore_ instance if the collection is at the root level
- A _DocumentReference_ if the collection is relative to a specific Document (a.k.a Subcollection)
  
We could keep a reference to _Firestore_ and one to a _DocumentReference_, then we should have some logic to choose what of them to use.

```java
public class RepositoryFactory{
    ....
    private Firestore firestore;
    private DocumentRefernce documentReference;
    private Boolean isRootLevel;

    public RepositoryFactory(Firestore firestore){
        this.firestore = firestore;
        isRootLevel = true;
    }

    private RepositoryFactory(DocumentReference documentReference){
        this.documentReference = documentReference;
        isRootLevel = false;
    }
    
    ....

    private CollectionReference getCollectionReference(String collectionName){
        return isRootLevel
            ? firestore.collection(collectionName)
            : documentReference.collection(collectionName);
    }
    ....
}
```
But when move the pointer from the root level to a document level, the firestore reference is no more useful.
Is there a way to keep only the pointer we need?

:bulb: Functional approach to the rescue!

We could indeed keep only a reference to a function that, given a collection name, retrieves a _CollectionReference_:

```java
public class RepositoryFactory{
    ....
    private Function<String, CollectionReference> getCollectionReference;
    ....

    public RepositoryFactory(Firestore firestore){
        getCollectionReference = collectionPath -> firestore.collection(collectionPath);
    }

    private RepositoryFactory(DocumentReference document){
        getCollectionReference = collectionPath -> document.collection(collectionPath);
    }
    ....
}
```

In this way, each instance of RepositoryFactory keeps only the reference to the right pointer.

#### Move the pointer to a specific Document

To move through _DocumentsReference(s)_ we don't want to modify the existing _RepositoryFactory_ instance but we have to create a new one with the new _DocumentReference_ as pointer:

```java

 public  RepositoryFactory FromDocument(Class<?> classType, String documentId){
        String collectionName = collectionMap.get(classType);
        DocumentReference documentReference = getCollectionReference.apply(collectionName).document(documentId);
        return new RepositoryFactory(documentReference);
    }

```

#### Create a Repository for a given Model
At this point we need a method that, given a Model classType, returns a _IRepository<>_ instance:

```java
 public <T> IRepository<T> GetRepository(Class<T> classType){
        String collectionName = collectionMap.get(classType);
        return new FirestoreRepository<T>(getCollectionReference.apply(collectionName), classType);
    }
```

## Pick up the pieces
Finally, our Repository Pattern implementation should be something like this:

```java

//IRepository.java
public interface IRepository<T>{
    Either<String,String> Add(T data);
    Either<String, T> GetById(String id);
}

//FirestoreRepository.java
public  class  FirestoreRepository<T> implements IRepository<T>{

    private CollectionReference collection;
    private Class<T> classType;
    
    public FirestoreRepository(CollectionReference collection, Class<T> classType){
        this.collection = collection;
        this.classType = classType;
    }
    
    public Either<String,String> Add(T data){ 
        try{
            DocumentSnapshot result = collection.add(data).get().get().get();
            return Either.right(result.getId());
        }catch(Exception ex){
            return Either.left(ex.getMessage());
        }
    }

    public Either<String, T> GetById(String id) {
        try{
            DocumentSnapshot entry = collection.document(id).get().get();
	    return entry.exists()
              	? Either.right(entry.toObject(classType))
		        : Either.left("Data not found for id: "+id);
        }catch(Exception ex){
            return Either.left(ex.getMessage());
        }
    }
}

//RepositoryFactory.java
public class RepositoryFactory{
    
    private Function<String, CollectionReference> getCollectionReference;
    
    private final static Map<Class<?>, String> collectionMap = Map.of(
        House.class, "Houses",
        Room.class, "Rooms",
        FurnitureItem.class, "Furniture"
    );   

    public RepositoryFactory(Firestore firestore){
        getCollectionReference = collectionPath -> firestore.collection(collectionPath);
    }

    private RepositoryFactory(DocumentReference document){
        getCollectionReference = collectionPath -> document.collection(collectionPath);
    }

    public  RepositoryFactory FromDocument(Class<?> classType, String documentId){
        String collectionName = collectionMap.get(classType);
        DocumentReference documentReference = getCollectionReference.apply(collectionName).document(documentId);
        return new RepositoryFactory(documentReference);
    }

    public <T> IRepository<T> GetRepository(Class<T> classType){
        String collectionName = collectionMap.get(classType);
        return new FirestoreRepository<T>(getCollectionReference.apply(collectionName), classType);
    }
}

```

## To sum up
This is how I implemented the repository pattern to deal with Firestore subcollections. 
For this example I chose to use Java, but it should be easy to move to other languages.

## Open points 
In this post I have overlooked some aspects that could be addressed in separate topics:
+ How to deal with Google _ApiFuture_ and Java _CompletableFuture_.
+ How to deal with queries specific for a model.

## Technologies
+ Java
+ Firestore
+ Subcollections
+ Repository Pattern


Let me know your way to work with subcollections!