I am currently building an employee scheduling app in Flutter and am trying to manage the availability of the employees. I need the availability for when I am assigning new shifts and to show to the admin the calendar of a given employee.
Until now, I saved basic employee parameters such as name, salary, designation, email, ... into an Employee object and pushed it into Firestore as a document within the Employees collection. To manage this, I am using Employee Bloc and Employee Repository (almost identical to the Flutter Firestore Todos Tutorial)
Now I am thinking of creating subcollections for each employee to store their unavailability. The unavailability will be containing a given date, reason (assigned to shift/ sick/ vacation), and description.
Should I create an extra unavailability Repository and unavailability Bloc or use the already existent Employee Bloc to manage them?
Hi @georgiossalon ๐
Thanks for opening an issue!
I would probably expose the availability through the EmployeeRepository and manage the availability through the EmployeeBloc since the availability is already part of the Employee model. You can create additional blocs if you feel you need to manage local state of the features but my initial reaction without additional context is to use the existing bloc unless it feels really awkward ๐
If you'd like to discuss this in more detail, I'm happy to do a live coding session so just message me on discord ๐
Let me know if that helps ๐
@georgiossalon you'd have to think about how you'd use that unavailability data.
If you're going to show it on the same screen as the common employee data then it makes sense to imbue it in the employee document as a map.
If you're going to show it on a different screen then it makes sense to add it in a sub-collection.
In both cases you could easily manage it though the same repository.
As for bloc part, it would depend on your setup, you could use the same bloc or a new bloc for unavailability screen but consuming the same repository. Both are viable solutions.
I'd try to force the sub-collection approach since it will grow in time, and you don't wanna be having to download all that data every single time you read the employee document.
I'd show it in a different details screen and maybe have the most recent unavailability available on the employee screen. This means you could treat the unavailability sub-collection more like a history. With this approach you would always duplicate the latest unavailability on the employee document, which is more than fine in a nosql data model.
Hope that helps you chose the right approach โ
Definitely a good point @RollyPeres ๐
A lot of it depends on the UX haha but great point about saving some money by decoupling the availability subcollection from the employees, I completely agree.
Cheers @felangel !
Would save money(egress) and mobile bandwidth ๐
Having collection group queries available would also allow for advanced querying on all the unavailability sub-collections. e.g.:
Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation ๐
This is great stuff. @RollyPeres Yes the recommended feature of yours (history), is something that I would like to implement in the future. So same repository and probably same Bloc it is. Also, the group queries are something that I will need when assigning a new shift (looking for available employees).
@georgiossalon same bloc only if you need the availability information right away. If you can afford to lazily request it on demand then it's probably best to have a separate bloc manage the availability of each employee.
@RollyPeres How should I get then the unavailability data within my repository? At the moment I am getting my employee data like this:
@override
Stream<List<Employee>> employees() {
return employeeCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => Employee.fromEntity(EmployeeEntity.fromSnapshot(doc)))
.toList();
});
}
Shouldn't I integrate the implementation of the fetching of the unavailability data within the upper function?
Should it look like something like the following part?
@override
Stream<List<Employee>> employees() {
return employeeCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) {
Stream<List<Unavailability>> unavailabilities =
employeeCollection
.document(doc.documentID)
.collection('Unavailabilities')
.snapshots().map((snapshot) {
return snapshot.documents
.map((innerDoc) => Unavailability.fromEntity(UnavailabilityEntity.fromSnapshot(innerDoc)));
});
return Employee.fromEntity(EmployeeEntity.fromSnapshot(doc),unavailabilities);})
.toList();
});
}
Stream<List<Unavailability>> unavailabilities =
employeeCollection
.document(doc.documentID)
.collection('Unavailabilities')
.snapshots().map((snapshot) {
return snapshot.documents
.map((innerDoc) => Unavailability.fromEntity(UnavailabilityEntity.fromSnapshot(innerDoc)));
});
return Employee.fromEntity(EmployeeEntity.fromSnapshot(doc, unavailabilities));})
.toList();
});
}
@georgiossalon I will start from the assumption that you're not trying to make google even richer than they already are ๐, so I would recommend against that approach.
In your example you're reading all your employees documents and for each document you're also reading ALL unavailabilities.
So for 50 employees having each an average of 10 unavailabilities, you would end up with 500 reads on each screen init; we can easily notice that this could escalate quickly.
Assuming we want to be as efficient and scalable as possible, we could make the assumption that on the employee screen you won't need all the unavailabilities at once; I think it's safe to say the latest one is what you're always interested in seeing. So have all the employee's unavailabilities be available on a separate screen and keep the latest only on his profile screen.
So basically your Employee will also have a field like latestUnavailability which could be a map reflecting all the existing data in an Unavailability document. With this approach, whenever you're creating a new unavailability, you'll both add it to the corresponding unavailability sub-collection as well as replicate it to the employee document(overwrite the existing latestUnavailability - basically setData with merge: true).
So now you'll just need to read the employee document, since it will already contain this bit of data. When you wanna see all the unavailability history for a specific employee just make it available on a button tap maybe in a separate screen where you'll be querying the proper sub-collection based on user id.
Hope it helps! ๐
@RollyPeres Uff the first paragraph had me laughing. I think the google boys have enough cash.
Yeah, this helped me a lot. So I will integrate snippets in each employee's document containing the unavailability lets say for the current week. Thus, the admin can monitor the workload of his employees. These snippets though shall be updated in realtime (I guess with the setData and merge you wrote).
Now, if an admin wants to check the calendar of an employee, by default only the current week will be shown from the snippets. If the admin wants to see if the employee has in,e.g., 3 weeks vacation, only then I retrieve the data, right? This way I do not need an unavailability Stream if I did understand correctly?
@georgiossalon haha, almost as much as we'll have when we retire ๐
You can get live updates from your employee document directly.
Whenever someone updates the availability of an employee, e.g.: marks that employee as being in holidays 3 weeks from now, a transaction or batch write might run and that will add a new document to the unavailabilities sub-collection as well as updating the employee document(if this is listened in real time it will of course show fresh data).
Most helpful comment
@georgiossalon you'd have to think about how you'd use that unavailability data.
If you're going to show it on the same screen as the common employee data then it makes sense to imbue it in the employee document as a map.
If you're going to show it on a different screen then it makes sense to add it in a sub-collection.
In both cases you could easily manage it though the same repository.
As for bloc part, it would depend on your setup, you could use the same bloc or a new bloc for unavailability screen but consuming the same repository. Both are viable solutions.
I'd try to force the sub-collection approach since it will grow in time, and you don't wanna be having to download all that data every single time you read the employee document.
I'd show it in a different details screen and maybe have the most recent unavailability available on the employee screen. This means you could treat the unavailability sub-collection more like a history. With this approach you would always duplicate the latest unavailability on the employee document, which is more than fine in a nosql data model.
Hope that helps you chose the right approach โ