Flutter Firebase & DDD Course [16] - Watching Notes on Firestore

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
the application layer responsible for continuously loading notes from the repository in other words watching the notes is implemented now it's time to get into the details of how to watch the notes using Forrester's real-time capabilities Oh [Music] welcome to reso Cole where you are getting prepared for real app development so that you will get freelance clients or a job and be confident about the apps you build so subscribe and hit the bell to join us on a quest for becoming in demand flutter developers so yes it's true in this part which is the sixteenth part of the domain driven design for our series we are finally going to start working with firestore we're going to implement the I note repository but not all of it only its first two methods which as you can see are the watch all and watch uncompleted method as I've said in the beginning in the previous part we have implemented the application layer which means in our case that blocks for watching all and watching uncompleted notes so now it's time to do the implementation when it comes to the repository so where will the actual implementation of the repository live because the interface of it is present inside the domain layer but its implementation fits right into the infrastructure layer so let's create a new file under infrastructure notes it will be called note repository that dart and just like that we are going to create a note repository I'm going to use a shortcut to create a class file as I call it so class note repository and it will implement I note repository so we have this interface here so that if we for some reason decide that we want to have a different implementation of a repository using some sort of a different service we can just swap them out but the repository which is saying that we should be able to watch all which uncompleted create update and delete will remain unchanged now I understand that you are probably not going to be changing firestore for some other service very often and if you did it would not even hurt to rewrite a bit of a logic but the interface has a beautiful property which is that you are just forced to program sort of into an interface and the implementation therefore will come out of the interface because if you start building the implementation without an interface without really thinking through that you should be able to watch all what John completed create update delete at first some bad code may come out of that process but when you have an interface already in place without implementation you have just these method definitions that just forces you to think about all of the possible possibilities well which are going to be doable through a repository or whatever other class you have as an interface at first so because we have an interface here we can hit control that or command dot and create five missing overrides using the S code yeah that quite an work which is unfortunate the whole code is just a mess so I'm going to probably oh it did work okay I'm just hit control set and now it works very weird behavior from vs code but hey we have it here and it's all cool we are not going you implement create delete and update in this part so let's move the watch all and watch uncompleted methods to the top of the repository because we are going to implement only them in this part and while we are at it we want to operate with fire store inside of this repository so we're going to create final fire store fire store and let's import fire store we have already added it as dependency in some of the previous parts and just by the way we are currently at the version of fire store which is 0.3 0.6 so make sure to follow along with the same precise version as I have here Oh 36 ok let's add this field to the constructor and let's also make this repository into a lazy singleton so at lazy singleton we do it with the capital L because we want to register it under a different type we want to register it under eye node repository so we're gonna say s I note repository and this way for example from within our block whenever we request a I know repository using the injectable package we are going to receive our node repository which is its implementation ok that's why we are using the interface in the first place now some of you may object that repository should not in itself depend on fire stored directly but instead we should create some sort of a we should have something like final notes fire store service notifier store service something like that right so we should create some other layer of abstraction on top of fire store and the repository would communicate with that I am all for these kinds of like data services or data sources for example when you are operating with a regular REST API which you need to cache yourself in a repository I am all for having something like nodes remote service remote and then having local service which would communicate with a local database for example SQL flight using more or something like sand baths or hives so note local service call and so on right so I'm all for it but as you can see it creates just another layer of abstraction and when it comes to firestore which truly does everything for you even caches data locally whenever it seems fit then it also is quite simple it's not like you are calling some HTTP endpoint yourself you really just call functions which are dart functions they are easy to work with and they are expressive they're not just some strings of addresses so that's why when it comes to firestore especially in this series I have decided that we are now going to have any such are layers of abstractions and we are going to depend on firestore directly inside the repository okay so let's implement watch all shall we the structure under which we are going to store the individual notes is that every user will have his own collection of notes and actual structure of firestore be as follows there will be one loot collection called users then this users collection will hold user documents and every user will have only one document which will be identified by user ID this user ID comes from firebase off by the way and then every user document will have collection of notes which will be identified by note ID and then this individual no document will hold fields for the note body and the two dues which are contained inside the note will be represented by an array of to do objects now that we know about the basic structure we have to ask ourselves a question how can we get to this notes collection so that we can watch all of the documents all of the notes present inside this collection we need to be able to navigate to users / user ID / notes how can we do that well of course in order to get the user ID which is the real culprit here we need to obtain the authentication data of the currently signed and user and how can we do that well if you remember all the way back way back we have implemented an auth facade so let's check out the eye of facade or is it off domain aisle facade over here we have a method called get signed in user and this returns an option of user and this user has a unique ID ID and this eye of facades implementation if we check it out by hitting ctrl or command + f12 I guess it's the same on Mac hopefully if not then finally yourself this method is implemented that the user is of course the fire based user and therefore the ID is of course the fire based IDE which we need so we in our words need to be able to call get signed and user from within our note repository to obtain this user ID in order to get to the proper collection of notes and by the way this is not secure we still don't have the security rules in place which prevent other users from accessing inappropriate user collections we're going to come to that later for now we are just going to focus on the dart code securing the firestore back and welcome later on this is just because in tutorials it's quite hard to just jump in between different stuff in between windows we want to really focus on this repository in this part but if you are building the app yourself it may be good to implement security rules as you go because that way you are now going to forget about them I already have them implemented so I know that I am NOT gonna forget about them but you may forget so just please implement them as you go in your own real-world projects since obtaining the user document represented by the user ID is quite a lengthy process and we need to do it multiple times throughout this note repository we are actually now going to write the code directly in here we are instead going to create an extension function on the firestore instance so let's go to infrastructure core right-click and we want to create a new file first or helpers the dart and inside of there we are going to create an extension first or X on first or and in here we want to create extension method which is going to give us a readily available user document or it's going to also throw an unrecoverable error so we want to be able to get future of course because it's all asynchronous and then document reference which comes from thyristor and we want to be able to get a user document so let's name this method accordingly it's going to be asynchronous and how is it going to look like well as I said at first we want to get the signed-in user and we can do so from our office ah we want to call get signed and user now we can do this completely fine because if you have noticed we are within the infrastructure layer so this means that even though we are calling a different feature held inside the infrastructure layer we are operating from within the core and calling code which is present inside the earth feature this is all well and cool because in the design spec of vdd which I have at least proposed for this particular tutorial series nowhere in there is it said that you cannot call code which is from a different feature all you cannot do is to break the layer structure so this means that while calling the auth feature from within core is okay what is not okay is calling domain from infrastructure that's not cool okay that's just for some of you purists out there I know that you are out there watching this video so with this purism out of the way let's call the get sign-in user on I office odd because we are using get it and actually injectable for dependency injection obtaining the eye or fossa instance is absolutely simple all we need to do is call get it I or facade because I or facade is lazy singleton we are going to receive the one and only instance which is present in our app which is awesome and yet silent user should be called on it now this is asynchronous so we want to await it and put the result of this call into final user option okay or make it equal to that and now we want to extract the actual user from this user option and in case that there is no user currently signed in we want to unrecoverable crash the app we want to throw an error which will never be caught by our code we are doing this because if we get to this point inside our app we call this user document extension which means that we want to obtain the user document but no user is currently signed in that means that we have a huge problem taking place right now because it should be impossible to get to a point where we request the user document and the user is not signed in that's because by the design of our app we sign in the user before trying to obtain the user document if the design of your app is different you would handle this case when the user is not signed in differently possibly offer the user an option to sign in right but in our case we are now gonna offer the user any kind of a dialog saying hey sign before you can do this action we're just going to crash the app because that's what our design spec says so while we want to retrieve a user final users stored inside this variable we want to obtain it from user option by saying get or else and this or else Clause will just throw out some sort of an error which we actually need to create right now we are storing all of our errors inside the domain layer core errors we already have an unexpected value error which also just crashes the app all of the errors are just for crashing the app in case something really horrible happens and this one will be called not authenticated error again if you had a different design spec you would show some sort of a dialog to sign in if you so desire so it's going to extend error and this will be just a very plain class it's not going to have any implementation of its own so now we want to throw this not authenticated error from here it's 10 she ate it and import it to here and lastly if no error has been thrown and we have the user instance readily available we want to return from this extension method firestorm instance collection users okay we are getting to it and then document and if you remember every user document is registered under its owner's ID so we will say user ID and then again if the ID is not available something has gone horribly wrong so we want to get or crash with this unique ID value object while we are here inside first or helpers creating these kinds of extensions let's also create another extension which will be a document reference extension so there will be extension document reference X on docu and reference and this will simply define an extension for getting the notes collection usually if you want to get a collection of something you do it like this that collection and specify its name so we could say something like that collection on the document of course that collection notes right and this would give us users slash document ID which is the user ID slash notes collection just as I have written out in the note repository but specifying always the notes string is not necessarily good practice because hey it's a string we should at least provide some sort of a notes can stand string write some sort of a constant but again we can do it better we can create an extension so that we will be able to just call something like note collection which is going to give us the collection of notes without putting in any kind of a string or a constant string ok so let's create such an extension right now it's going to return collection reference we get it will be get only property note collection and it's going to return collection notes awesome alright so we have all of the extensions in place your document extension also note collection extension so let's come to the note repository and since fortunately extensions cannot be automatically imported we need to copy this extensions path so this first or helpers let's right-click on that and we want to copy relative path now paste it into our node repository put it into these quotation marks okay instead of a Lib we're going to say package notes firebase DVD course alright and slash and then change all of the back slashes to be forward slashes like this and let's say import oops like that now we are talking we can use these extensions from our code base so let's first tackle the watch all method right because it's simpler we do not need to filter anything we can just watch all as it says in the name all of the notes so in order to get the user document we are going to simply say firestorm that user document we are using our own extension and let's actually put this into final user doc okay and let's make it be awaited because it's asynchronous and let's make this method asynchronous but not just regular asynchronous but instead an asynchronous generator because as you can notice we are now going to return a simple future from this method but instead a stream and we are going to do it in a way where we are going to utilize asynchronous generation which is a very cool feature which Dart offers we want to observe or watch all of the nodes they are present inside the user dock and in the note collection again we are using our own extensions here and collections offer the snapshots method which is going to return a stream of query snapshot and this will notify our code whenever some new node is added to the backend this is the real-time functionality of thyristor and it's absolutely awesome and actually it can result in even a lower cost of using thyristor because these snapshot observers of the collections are actually optimized for cost they do not just push the whole collection which would have many document reads basically built to your account but if only one document changes in the collection only that one document update is sent over the network so you pay only for the one document not for the whole collection at all times constantly right so it's really optimized if you can use real-time data like this where you observe it as a stream you should absolutely do so it's definitely much better and also cheaper therefore than just calling node collection get documents because this could get all the documents at all times you do not want to do that instead use snapshots which returns a stream whenever you can it's going to be cheaper okay but now these snapshots are nowhere near our wanted type which is either a node failure or a KT list node these snapshots hold something called the query snapshot which is an absolute low-level type coming from firestore and we need to extract our data from it and transform it into our own types before we do so though these snapshots are not really ordered in any way but we want to order our nodes descending lis based on the time of their latest update and if you remember if we check out the note details we are indeed storing a a server time stem field inside our note on fire store it's implemented as first or field value which is how is it called Sentinel value that's right it's the Sentinel value which is only populated in fire store but regardless of how it's implemented we should know that we have a server timestamp field on our documents representing nodes inside fire store so we can actually order the notes by this server times time so let's do just that we're going to copy the server timestamp name and before calling snapshots on note collection we want to create a query and we want to order the documents by nothing else then server timestamp and we want it to be descending so descending true awesome right and of course because we should return a stream from this method was actually yield each thing which comes through this Corey snapshot stream of course we cannot again return Corey snapshots directly we need to map them to either a node failure or a immutable cutline list of nodes so let's do just that we are going to say map on the stream we're going to rename event to snapshot to be more explainable and let's talk about how errors are handled in streams by default when it comes to Dart the way that errors are handled is nowhere near as awesome as we are doing it with either type by default streams errors are handled very similarly to how for example regular exceptions are passed around in dart usually you handle your errors unless you are a fan of the either type of the functional way of handling errors then you would probably handle errors coming from a stream in a way that only when you listen to the stream then you can have a on error function and perform something in this function write error and display something to the user some sort of error sniper or something you would do this from the bottom most layer from Idaho from application layer let's say or from the presentation layer so this is very similar to what you have with the try catch blocks you need to remember you need to absolutely remember yourself that there is a possibility of an error and you need to catch it yourself and you need to handle it yourself without any sort of a help from the compiler and I say if we have the compiler why not use it to the full extent in a way that it's going to help us with caching and handling which is the most important part of errors so on this lesson function you can see that the flow of the good the correct data is separated from the bad erroneous data and this also means that when it comes to functions like map they are not going to receive the error fault data or however you want to call it the Corie snapshot is always going to be just quarry snapshot it's not going to be the exception which can possibly happen on fire store for example when we don't have sufficient permissions to view some document and because this map function always receives only the correct data we can always return the right side of either from here so right which is going to contain the snapshot that documents which is a list of document snapshots and we want to map this list of document step shots into a KT list of notes so we're going to say map and map each and every document snapshot called doc to a note DTO at first from first or that's important all dto actually from fire store doc and now once we have this node eto mapped from fire store now we want to map this node eto to domain which is the node type right and then this just returns an iterable and we want to map even this iterable to immutable list which actually comes from the KT the heart extension for some reason it doesn't want to appear here hmm okay this is interesting we have imported the KT dart slash SRC but we should actually import something completely different for the KT dart library so let's remove this import from up top here and let's try again to import KT list and now we want to import it from KT darts for / katydid art and this will allow us to use the two immutable list extension method like that let's put a comma in here and we are all in call done cool so we are returning only the right side of either from the map function because the map function will only receive the correct data which in our case we represent it as right side of either but what about the left side of either how can we catch the exceptions passing through the stream and transform them into something which is the left side of either in our case into node failures well the easiest way to do it is with our ex dart which is basically an extension now truly an extension on top of regular dart streams with our ex dart we are gonna be able to call something called on error return with as I said in order to be able to use this method we need to be able to use our X dot so let's go over to pub spec yamo and we're going to say pub spec assist add update dependency our X underscore dart actually no underscore okay currently it's at the version o point when t 4.1 so make sure to use this one and now we are gonna be able to use on error return with but again because it's an extension method and extensions are just not being able to be imported easily as of now we need to import the rx dart library ourselves so we're going to say package Collin our ex dart /rx darts dot dot awesome now as you can see we currently have the on error return with method available which is an extension on top of a regular stream and this accepts the exception e or error e however you want to call it although I prefer exception because as I use the Naaman closure extensions can be caught and errors are basically a subtype of extensions which are never caught errors are for crashing the app while extensions are for telling something to other layers of the app but we don't use even extensions and if I set extensions instead of exceptions in the previous minute or two then of course I meant exceptions all along of course not extensions by exceptions so what do we want to do from here well we want to check the now currently dynamic exception type if it is so if E is platform exception which is the exception which comes out from firebase snapshots the documentation on this exception type is really really poor I had to search through it for quite a long time when I first started out with firestore and flour I mean they could really improve the documentation so I'm now really able to show you all of the exception codes because even I don't know them it's really an unfortunate situation that we are in but basically if the exception is of type platform exception and its message so II that message contains permission denied which is a string permission denied this means that the user or we in the app have tried to access a document which is protected by the security rules to not be accessed by this particular user and of course it wouldn't hurt if we provided an end operator here of course so now we are talking and for some reason this e is not getting smart casted to a platform exception but we know that if this condition passes then e is of course off type platform exceptions so we can access its message just fine without any problems so in the case of this when the permission has been denied and that's why we have got an exception then we want to return left and we want to return a note failure which we currently don't have because we have only unexpected failure present and e note failure Union but of course this case is not unexpected this is a known case which we should handle accordingly so whenever permission is denied we should return something like insufficient permissions failure so let's come to the failure union by hitting f12 and in addition to unexpected we are going to also create insufficient permissions let's also rename the class name insufficient permissions okay and let's run our favorite command which is going to be in the video description to Flora problem we'll run a watch delete conflicting outputs to generate this Union case and while this is running we can go to the note repository and say in sufficient permissions that's it and also add the constant modifier over here and otherwise if there is some error exception happening or if the message does not contain permission denied then it's the perfect case to return the unexpected note failure from this on-air return with method and also I would recommend you to log the unexpected exceptions so something like log error E string something like that right or maybe LOC error I will just leave this comment in here to signify that you should probably lock unexpected exceptions and send them somewhere at least while you are in debug mode of course but we are not going to do that we're now going to log anything to make this code a little bit more simple we have this watch all method right now fully implemented but as you can see we are getting some errors here saying that the type stream either dynamic kata is note implied by the yield expression must be assignable to stream either note failure catalyst note so somewhere along the way we have lost the type information for the left side of either it should be no trail yer but currently it's dynamic so what can we do I mean this behavior is really unfortunate it's an insufficiency in type inference when it comes to Dart but we can fix it pretty easily by just providing the generic type explicitly for this right function so we can specify the L and our generic types ourselves which is going to make the codebase a bit more clunky but hey it needs to be done so we cannot complain the left side is of course note failure and the right side is kaity list note if we put it here now as you can see the error has gone away and everything is cool now we want to implement watch uncompleted and this is going to be totally simple to do we are just going to copy yes copy the watch all method implementation and paste it into watch uncompleted and of course we could extract bit of code here and there into its own method but in the case of these two methods it would actually create more confusion then then good things so that's why we are actually going for code duplication it's not that horrible in this case I would say so again this should be an async generator like that and the only change in the watch uncompleted compared to watch all is going to be in the map function of course because think about it what do we want to do in side watch uncompleted well we want to check if the to dues are all completed and if they are we do not want to show them in the output in the output stream so for example if I show you here you can see that this red node has all of its to do is complete it read Plato's Republic or about Socrates and because it has everything completed we want to filter it out whenever we want to watch all the uncompleted things from here okay like that and yes we are doing this filtering client-side not server-side in this case it's completely cool but if you have some other use case where you want to switch between things like for example ordering or something you would actually want to order it or filter it on the server side but here again we are going to filter and do everything on the client side inside this map function actually to make this filtering process of only uncompleted nodes a bit simpler to understand we are going to create another map function so chain it after the first one because we have this second mapping function this is where the filtering of the uncompleted nodes and also their conversion to either node failure or catalyst node is going to take place this first mapping function will now be only a simple conversion between the low level document snapshots and nodes details are actually node domain entities so we're going to return remove the right node failure catalyst function and also two immutable lists will be removed from here so all that's left here is the mapping of the document snapshots from document snapshots to note entities and now all of the magic will be moved to the second map function so again we want to return from here right note failure KT list let's rename the parameter to notes so this will be returned from here and these notes are an iterable holding note and we want to say notes and filter them which is done with the wear method on an iterable and we want to only have notes in this stream of uncompleted notes which are of course uncompleted so we're going to say we're notes that to deuce because we are inside an entity we need to get or crash and we will say any of the two deuce it's not done so to do item to do item dun dun and negate this thing and then say two immutable lists some like that so again in this first map function we just map the document snapshots to note entities which are then passed over to the second map function as an iterable of notes and from here we return only the right side of either because again map is never going to receive the incorrect values the failures and since we want to return only the uncompleted or actually yield only the uncompleted notes we need to filter them on the client side so we say we want to have only nodes where the notes to dues has any which means has at least one not done to do and then of course we want to get it in the format of Katie race so we say two immutable lists and the rest the error-handling can stay the same and just like that we have implemented the watch all and watch uncompleted and I understand that especially this second part may have been a bit exhausting with all of this filtering so as always you can get the code from the link in the video description of course you can also rewatch the parts which where your sticking points and if you are serious about becoming a great flora developer who can build real apps for clients or at the job go to flutter that education link is also in the video description to get the top curated flora news and resources aimed at improving your app development career over there you can also subscribe to my mailing list to get the best flora resources delivered weekly right into your inbox and if you do not want to miss more tutorials like this from the domain driven design series and also our flower tutorials be sure to subscribe to this channel and also join to the vacation squad by hitting the belly button to make sure you grow your flora skills because here on reso corer I am determined to provide you with the best tutorials and resources so that you will become an in-demand flutter developer if this video helped you with operating with the real-time data coming from fire store and then mapping it accordingly to your own classes give this video a like and also share it with our developers who are surely going to find it beneficial to leave a comment if you have anything to say see you in minutes [Music]
Info
Channel: Reso Coder
Views: 5,550
Rating: undefined out of 5
Keywords: resocoder, tutorial, programming, code, programming tutorial, flutter, flutter tutorial, flutter firebase, flutter firestore, flutter firebase auth, flutter firestore tutorial, flutter firebase tutorial, flutter architecture, flutter architecture patterns, flutter domain driven design, flutter ddd, flutter database, flutter todo app, flutter todo app tutorial, flutter todo app firebase, flutter app tutorial, flutter app example, flutter clean architecture, flutter bloc
Id: LGSC7TiAcSY
Channel Id: undefined
Length: 49min 5sec (2945 seconds)
Published: Fri Jun 19 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.