Refactoring to the Repository Pattern

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello it's Duncan it never ceases to amaze me how bad I am at predicting the subject of the next week's video this week I really did set out to Benchmark Kor on hvv fork for end points that perform i o but it very quickly became clear that the data model from the example project wasn't suitable it uses cot's mutable list but the operations on that interface are not suspend functions and Kor needs co- routines in order to not block on iO so instead of measuring I ended up refactoring migrating from mutable list to a repository object that is much more amable to implementation with a database even if in the test we still actually hold data in an array list okay we're good and everything passes so far we've discovered that both HTTP for K and Kor are able to process HTTP requests faster than we can make them or at least it seems faster than the Mac TCP stack can accept them mostly though our web apps are limited by the fact that our handlers will be making database requests collecting information from other systems and so on in order to make a response that will all take time and I like to simulate that in our tests now our tests have been talking to the customer route and our customer model is this mutable list of customer now mutable list is an interface but what we'll actually be getting back from this mutable list is an array list so all of our requests are just going to be look up in an array that's not very realistic in most real applications we would expect to be going to a database to look up the customer so let's see how that fact changes the interface that we want of our model let's first of all see how we're using the inmemory one so we'll look in Roots 4 and let's look at what we're doing with our customers so here we're asking whether the customers is empty here we're asking customers find well that's a cotlin extension on iterable and certainly not something we'd be able to do with the network we are also doing add a customer here and remove if here we might expect add to work well over the network but remove if taking a cotlin Lambda again that would be hard to get running against the database actually it's interesting there are two models here there's the model that Our Roots takes this mutable list of customer and there's the model that Our Roots exposes and that is that we can list all of the customers we can get a particular customer by ID we can add a customer by posting and we can delete a customer by ID and the job of these roots is to map the rest interface this http1 here to the mutable list interface here and that's done by these operations here like customers find now often it's a good idea to have our Roots as thin as possible so they don't really have any logic in them all they do is map to and from HTTP Concepts on the subject of which there's one gratuitously bad mapping which is to say that if we don't have any customers we're sending a responsive okay with the body of a string of no customers found that that was in the original example code that we reproduced in HP Fork but putting ourselves in the position of a user of this API if we want to pause the body of this response at the moment we'd have to look at the content type to decide whether to use adjacent pass or not alternatively I suppose we could look for the string no customers found and if we don't have it try and pause the result as Json but both of those seem to be jumping through a lot of Hoops when it' be possible to just return an empty array when there aren't any customers that would simplify everything and also allow us not to rely on this operation here customers is empty so at the risk of breaking existing clients let's fix that first we'll go to the tests and we'll say the customer route HBK tests here's returns no customers found with there are no customers I think we could change this to returns empty array so if we clear the customers we still get back an okay but the body string will be equal to an empty Json array now if we run those tests we'll have a failure that we can make pass by just removing this code here try that good and we'll do the same thing for the Kor version so where do we have we have returns an empty array when there are no customers and again we can do that run fix up the Kor version and that is in here on the other side of the else good so what's the problem with using mutable list as our internal model well let's have a look here is mutable list it's actually the Java mutable List It supports all the operations on list and also things like add remove add all from a collection remove all retain all set an item at a particular index remove at an index and all these operations would be very hard to make work against the database for example what does it mean to replace an item at a particular index with another one in a database table database tables don't really have a natural sort order so let's see how we can go about narrowing the interface we use from mutable list to just the operations that we actually need a way of thinking about this is that we need to move from a type that we don't own mutable list of customer to a type that we do and a good first step on that list is a typ alius so what I'm going to do is I'm going to take this and introduce a typ Alias there and it's offering customer mutable list I think we're just going to call this customers and there you can see we have it we it's introduced a type a list of customers as a mutable list of customer un unfortunately what it hasn't done is found all the other places where we use mutable list of customer and use the type alas so for example when we see where we call this this is still mutable as a customer that's not very helpful when it comes to earning the type so we're going to have to fix that up by hand we go back here let's move customers into its own file customers got KT yes in the same Source rout I think we want to be in com example models let's make that refactor add it and now let's replace in our code base we want to find mutable list of customer and replace with customers so there's one let's replace that one there's another good that's the definition so we don't want to replace that skip over to that one replace that one that one and that one okay and now let run the tests ah but irritatingly we haven't made an import so let's fix that and every one of these by hand okay now all the mutable list of customers are our type Alias we can make them not a type Alias but an interface so if we say this is interface customers that EX Den's mutable list customer let's try building that okay so now we have a problem we've introduced a new type and because of that a mutable list of customer is not the same as our customer type let's pull that over there and what I think I'm going to do is I'm going to return this to the type alias in order that we can add a replacement for this function here that will return our type so let's say we'll make a Constructor function fun customers which is going to return customers and is mutable list of customer and in fact it turns out we don't need to say the type so now if we go and find the places we've called this and replace it with customers we can do that one and that one and that one Blended and build that and again we have to do some importing that for some reason we can't find here don't know why let's say customers don't know why in couldn't do that for me okay we're good and everything passes okay now we're in the position to make this an interface like that and now here we need an implementation of that interface I think we'll call that a class in memory customers now that implements customers which is mutable this customer and it can do it by creating an empty mutable list well actually it can't what it actually has to do is it has to implement mutable list of customer and then customers separately and now this can return in memory customers let's see how we're getting on H roots four here ah we missed a way of creating a mutable list here this is a mutable list that isn't empty it's got a thing in it now we can either create another Constructor or we can use apply so I think I'm going to do that for now we're going to say this is customers cre an empty one apply add a customer any more and the same thing here so in fact let's take that copy it into there and once again we have a place here where we need to create customers anym no run oh a failure looking at this it turns out that both the Kor and the HTTP Fork versions are failing in the same place with serialization exceptions and the problem is that previously we were serializing a mutable list of customer and the serialization code knew how to do that now though when we return the customers we're serializing this type here which we haven't told serializers how to do that this is in places like here where we say call respond customers where this customers is now our custom type now we could find a way to make the serializer serialize this customers type but I think a way that is closer to a database sort of interface is to say that our customers interface here isn't actually a list it's something that can give us a list we'll need it to be a list for a while until we finish this job but what I propose is that we add in here a fun list that returned the list customer now then we can Implement that here and the implementation is just return this because this in memory customers is itself the list at the moment and now we can go to the place where we're treating customers as a list and call list well the first of those is here here now instead of returning this customers return this customer list now that as you saw returns a list of customer which the serializer will be able to work with so just check that we fixed those ones and now we have to do the same with the htbk version so that's here and again we can just say dot list run that good so now customers has gained its first independent operation that's this list okay let's return to customer Roots okay when we bind to slash with an ID parameter at the moment we go through our list of customers and we find the one with an ID if we were talking to a database though we'd be running a select we can't pass a cot Lambda across the wire so we'd better make this an operation its own right and the nice way of doing that is to make this thing into a method so we're going to call this find by ID now when I say method I meant of course function is just here on its own but if we make this the receiver then we have the sort of operation we want on our type now we can take this make it public and cut it out of here and put it into our customers and for now we'll just paste it there and we have this nice update usages to reflect the moves so we do Alt Enter to fix those things does that still pass it does good now ordinarily you can convert an extension to a method but it doesn't seem to be true for interfaces that's a bit of a shame but let's just move it down here like that take that away say override and now create the one in our interface and run oh and we have to fix that compilation good looking at this it's interesting that we had this ideas nullable that came about I think because this request or path returns a string question mark but I don't think it makes any sense to pass null to find by ID so let's change the type of that and that build and now we have to decide what to do if this IDE is null now as it happens I think we have a test for that so if we look in the HTTP K customer root tests we have this returns all customers with no ID now I suspect that means that this can never actually be null because if it is then this route will be hit so I think we can probably just say question mark colon error and we use these or errors to check our logic so I think in here we should be able to say should not be null because that is a different are we right well the test say we are okay so now we've introduced customers fine by ID let's use it in the Kor version so we're looking customer roots and here we are get ID this has checked to see whether we have an ID but now here I think we can say customers find by ID passing in the ID and if that is null then we're returning not found is that good yes it is let's do our next operation from the Kor side we have this customers ad now that add is on mutable list let's take this code here we'll make a function out of it which is ADD customer we'll make this a receiver make it public cut it out of here go to customers and put it in here update things run that's all good and now we do the same thing as we did before we'll take this thing make it a method on customers Force the interface to declare it it's good and now we'll do the same thing for the HTP K version so that's customer roots and we can now say customers add customer splended and now we're just left with delete and that is customers remove if again taking a Lambda again we can't pass that lambra over the Y to the database so we want a semantic operation for that which will make a function for this is delete by ID then we'll make that the receiver take it out of here into our customers put it there fix up callers check that things are good move it into here say override fun and put it into the interface delete that run that go to the Kor version and here we can say customers delete by ID giving it ID great now then we should be able to go back to customers and see whether we can remove the mutable listeners from customer so let's get rid of that and build the first issue we have is that we now no longer have ad but we know we replace that with ADD customer so that's good okay clear now clear is an operation on mutable list should it be an operation on our customers well it could be but that feels dangerous to me I'm not sure something that deletes everything from a production database table should appear in an allal interfaces instead though we could add this to in memory customers the reason we're not being offered it is because this function says that it returns customers not in memory customers I think this was just transient so I think maybe we should change that like that now we've exposed in memory customers which is a mutable list of customer and that can work let's just see whether that gets us off the hook oh it does good and that gives us a lot of flexibility for the way we treat our inmemory customers the ones we'll be using for tests in fact I think maybe a little bit too much flexibility what I'd rather do I think is not at this immutable list so let's say that this has a Val customer list which we'll leave us public for now now this would be mutable list of customer by customer list and should continue to work good but this is what we want to get rid of replacing with customer list when we List It customer list find custom list add and custom list remove if now the clear over here we can create as a member function in in memory customers or we could create an extension function that talk to this customer list I think a member function on inmemory customers and this will be customer list do clear is that true okay customer list is empty this is another operation and the moment is just required by testing and that then gives us two options do we Implement is empty as an extension or as a method well we chose a method last time for clear so let's be consistent and create a member function in memory customers is empty which is going to return Boolean and we return customer list is empty and I think that's a very definition of a single expression how are we doing okay finally when we added a customer we're checking that the customers is a list of customer well that simply isn't true we need to say list here and the same is through here uh there's another add customer but we could now say in memory customers given a mutable list of the One customer we have and I think I'll revisit that in a second I just want to get everything running this one is also customers. list as is this one and we are good now I don't think this Constructor function is only its keep so we'll in line it and now if we look at the places where we call this Constructor there is main here there's a test here where we create a mutable list of customer I think it would be nice to have a Constructor for this that took the initial list so that would be Constructor that takes a varag customers which is customer and invokes the super class with that to a mutable list that would let this be in memory customer like that this one again we've got inmemory customers where we are adding one so we can get rid of that simplifying that get order that's just a plain in memory customers throughput tests has nothing in it and this main has nothing run good one last little tidy up I think we could call this list conflicts detected oh conflicts detected nothing to show nothing to show refactor anyway I suspect it has an issue with the fact that we've got a function here called list and a property here called list but they should be separate not all usages were renamed the following usage was safely skipped function list is already declared well okay is that all right it seems to be good oh and I've Just Seen another issue here delete by ID I think should be taking a non-nullable string wonder if that has any effects ah we have the same issue here with customer Roots I think we could say error and I don't think it's a root this time I think it's the method which is to say this delete versus get all still good Splendid and now in fact we can use this file to check that our mapping from the roots to the customers is as simple as it can be so each root here is simply extracting any data that it needs to perform an operation on customers and rendering the result of that operation and if we look at customers we'll see that we have a set of operations that we can see how we'd Implement with SQL or another database and we can see that with enough customers we'd have to find a way of implementing paging in our list function now that we've paid it back I think it also allows us to consider some of the error cases for example our customers come with an ID but what happens if we try and add a customer with an ID that's already taken mutable list as an interface we be perfectly happy with that but I suspect our database shouldn't be and finally if you're a Kor programmer you're probably wondering about suspend these are all places that we be doing IO in the real world so we will want to make them all suspend if I do this if should be K won't be able to call the methods at least not directly and we can't really take one interface and express that interface with its function suspended it would be nice to be able to say something like interface co- customer is customers where these are all suspend say but I can't find anywhere making anything like that work I think I'm just resigned to having two versions of in memory customers one of which is suspend and one of which isn't but if you have a clever idea then please let me know in the comments and if you're quick then I'll be able to incorporate that into next week's video if you'd like to see that video then please subscribe click the like button to incentivize me and maybe even buy the book that I wrote in that price called Java toot refactoring guide book details of which are in the show notes below thanks for watching
Info
Channel: Refactoring to Kotlin
Views: 1,030
Rating: undefined out of 5
Keywords:
Id: PdH-1UGAqBk
Channel Id: undefined
Length: 24min 21sec (1461 seconds)
Published: Fri Jun 28 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.