[0:00] hey everybody welcome back in this final [0:02] part of my python project structure [0:04] video series I'm going to look at [0:06] extending the app we build just a little [0:08] bit but using a general framework that [0:11] you can use for all of your applications [0:13] or scripts that you write in Python what [0:15] we're going to be doing is adding a [0:17] domain folder to group all of the domain [0:20] Logic for our app uh Services folder [0:23] which is going to have all of the sort [0:25] of logic of our app and then an adapters [0:27] folder which is going to include things [0:29] that external to our app that we want to [0:31] bring in and use in here this is a [0:33] general purpose framework that you can [0:35] use to structure your projects I got [0:37] this idea from this architectures book [0:39] for python the ideas discussed in this [0:41] book apply to Big monolithic systems [0:44] that are really complicated there's [0:46] Advanced topics like a unit of work they [0:49] have a lot of abstractions that make [0:51] these really big systems work nicely so [0:54] if this is so complicated and advanced [0:55] why am I talking about it well there's a [0:57] few basic ideas that I gleamed from here [1:00] which I apply to my projects and [1:02] basically just help me structure things [1:05] so I've already talked about them in my [1:06] own words but I'm going to use the [1:08] author's words there's a domain layer [1:10] this defines the business logic in our [1:12] case with our example of a shopping cart [1:15] with fruits in it the business logic is [1:17] this concept of a fruit and a shopping [1:20] cart these are our domain entities next [1:22] we'll have a service later this defines [1:24] the jobs the system should perform and [1:26] orchestrates different components in our [1:29] case we want want to add items to the [1:31] shopping cart and remove them it's [1:32] pretty much it so that's our service [1:34] layer although the authors have more in [1:36] here the last one that I use regularly [1:39] is an adapter's layer concrete [1:42] implementations of an interface that [1:44] goes from our system to the outside [1:46] world all right so the authors are [1:49] describing this as a way of of of their [1:52] system talking to the outside world this [1:54] could be like a database integration [1:56] where you want your system to write data [1:58] to a database [2:00] but for me I also use adapters to pull [2:02] data in from the outside world so let's [2:04] go ahead and restructure our project to [2:07] work like this it's really easy first [2:09] I'm going to hop into our shopping cart [2:11] and make these folders that I've been [2:12] describing I'll have one for domain [2:15] Services adapters typing inside of each [2:18] of these I'm going to put an init [2:24] file and now the fruit file we just want [2:27] to put this directly into adapters into [2:29] here okay let's move my shopping cart [2:31] now into Services let's check out fruit [2:33] oops I accidentally put that in adapters [2:36] okay that's in domain this is clearly [2:38] like a domain entity and shopping cart [2:41] is like a service right so I put that in [2:42] here but I've got this domain object [2:45] inside of here let's move that over [2:48] shoppingcart [2:50] py let's open that and paste and this is [2:53] good because fruit is local now right to [2:55] this domain so we we've got all our [2:57] domain stuff together in one place and [2:59] and now I I just want to make sure we [3:01] get rid of it here get rid of that [3:03] probably don't need data classes now I [3:05] don't have my shopping cart I'm going to [3:07] go to my domain shopping import shopping [3:11] cart so here's my project so if I run my [3:14] app so it's not finding a module named [3:17] shopping cart. shopping cart anymore and [3:20] maybe this is my init files I never [3:22] updated that so let's go back in this [3:25] init file I'm grabbing some stuff from [3:27] shopping cart now in my services like so [3:30] I have to update that and for fruit [3:33] that's in my domain now see let's try it [3:34] again fruit is not defined I suppose [3:37] what I did do inside of my shopping cart [3:40] service I probably have a fruit object [3:44] here just instead of shopping cart I [3:49] want fruit right and uh I'll do it this [3:54] way just grab it like that still feeling [3:56] pretty good U I'm not really going to [3:57] think too much about it let's try and [3:59] run it again great it worked so my app [4:01] is still working after refactoring it [4:04] well let's throw together a quick [4:05] example on the Fly of how we might want [4:07] to have an adapter for this application [4:10] let's imagine we're like an e-commerce [4:11] website where users will have a shopping [4:13] cart what you might want to do is [4:15] classify those users for like a [4:17] propensity of how valuable they are [4:19] maybe all the items in the shopping cart [4:21] are worth like a lot of money so we [4:22] should kind of identify that let me go [4:24] ahead and build this starting in the [4:26] services layer score shopping cart and [4:30] let's add some documentation assign a [4:33] score to the shopping cart based on the [4:39] contents like the other functions here [4:42] we want to add a shopping cart we're [4:45] passing in like what our current cart is [4:47] now what I want to do is score it here's [4:49] where our adapters layer is going to [4:51] come in so I'll say from adapters score [4:55] and I'm going to say import shopping [4:57] card scorer so no I'm using camel case [5:02] or rather like python convention for an [5:04] object goe and initialize it in here [5:06] score do score and I'm going to pass in [5:10] my cart this will provide me score I'll [5:12] call it cart [5:14] score I'm going to assign this to the [5:16] shoing cart object let's remind oursel [5:19] of what that looks like so now I could [5:20] have a score down here and I'm going to [5:22] call that int it's going to be an [5:23] integer score and it's going to default [5:25] To None because we don't need to have a [5:28] score when we're initializing shopping [5:30] cart let's go back to our services cart [5:33] doore equals [5:35] this assigning score cart [5:40] score okay so what do we have to do now [5:42] well this shopping cart score doesn't [5:44] exist we need to create it and we need [5:46] to make sure that it has this right [5:47] method I'm going to create a score file [5:51] and we know we need these things so [5:52] class shoing card scorer is going to [5:55] come in here and this is going to have [5:57] to do some stuff let is going to have to [5:59] to have this [6:03] method okay it's going to pick itself [6:06] and a cart when we initialize [6:08] this I'm not going to actually do [6:10] anything when we initialize this and [6:12] then for this guy here um we need to [6:15] make sure he's returning an integer I [6:17] could just say that score is going to [6:19] equal and I could say item. price for [6:24] item in cart. [6:27] items and I could say or zero because [6:32] the price might be none and then I can [6:34] just sum those up and I could return [6:37] that as a score one last thing I want to [6:40] do is pass that as an integer or I guess [6:43] to this would be more in line with the [6:46] Zen of python to do things this way [6:48] pretty happy with with everything here [6:50] even though I'm not really using my [6:51] initialization method in general I might [6:53] want to do that now one other thing I [6:55] might want to do here is say from domain [6:59] do shopping cart import go here and [7:04] explicitly say what we're passing in [7:07] here um let's see if everything uh works [7:09] if I run my app that seems to be working [7:11] the thing is my app's not actually doing [7:14] anything we're going to need to import [7:16] that so if I go back to my services here [7:19] it is score shopping cart is what I want [7:22] put that here okay so I'm just going to [7:26] import it along with everything else and [7:28] then if I go to the again and grab the [7:30] names now where am I doing okay so now I [7:33] want to score this and I want to score [7:35] my card and that was it I didn't have [7:36] any other arguments so after I do that [7:38] if I print my cart [7:40] again and I'll just say [7:43] print and we'll be able to see [7:46] that see if it'll work it can't import [7:49] that I believe it's because I didn't add [7:51] it to my [7:53] init okay so score shopping cart cool [7:57] let's see so here it's assigning a score [7:59] zero because um I have no prices when I [8:01] add my fruits I'll just assign some [8:04] arbitrary prices say $99 because these [8:07] are the most fantastic fruits in the [8:09] world and there's my score of 198 you [8:11] could stop watching here and I wouldn't [8:13] blame you and if you do I thank you for [8:16] going this far with me I would really [8:18] appreciate it if you consider giving [8:20] these videos a like or maybe subscribing [8:22] to my channel if you like this content [8:24] as a new channel this really helps me [8:27] get visibility if you liked my layout [8:29] with the terminal I have another video [8:32] on using vim and t-o and how I've [8:35] configured these things it will send [8:37] your productivity into an absolute [8:39] spiraling death I mean um I mean uh you [8:43] should check that out a little bonus the [8:45] last thing I'm going to do is make this [8:47] adapter better we might want to have [8:49] different ways of scoring things what [8:51] I've created here is more of like a base [8:54] score it's almost more like a unit test [8:56] really I make a new folder and I'm going [8:58] to call it Gore [9:00] plural and then inside of here I'm going [9:02] to I'm going to copy this guy and I'm [9:04] going to put it in here delete I want to [9:06] call this a base score rename it and I'm [9:08] going to rename it base I guess py [9:11] called this base shopping cart score I'm [9:14] going to have to go down an additional [9:16] level so I'll add another dot there [9:18] where am I going with this well I might [9:20] want to have different scores I'm going [9:21] to copy this guy and I'm going to paste [9:22] it what do we want to call another score [9:25] Apex I don't know what that means but [9:27] let's say we have some model we've [9:29] called it Apex okay so let's call that [9:31] Apex shopping cart score so I could [9:34] imagine one situation where I have a [9:37] different projects set up for this Apex [9:39] and I can say from Apex scorer import [9:43] shopping cart [9:46] score and then I could go down here and [9:49] where I have score shopping cart I just [9:52] want to return the Apex call to shopping [9:57] cart score we're assuming that we have [10:00] this somewhere else on our system and [10:02] all we want to do is call that here and [10:05] so this adapter lets us do that in a [10:08] nice structured way so we have our Apex [10:11] shopping card score object and we also [10:14] have a base scoring object which does [10:16] the same thing it has a scoring shopping [10:18] cart function so these are implementing [10:21] the same thing which is going to be [10:23] important for what we do next so I'll [10:25] have scorer. py so in here I'm going to [10:27] create a new function score shop cing [10:30] card I want a function that's going to [10:31] bring in my objects from my subfolder in [10:33] this case from do [10:37] scores and then I'll do the same thing [10:39] for [10:40] base now in here I want a score type gu [10:44] string I'm going to return a score a [10:47] union of one of these so B basically it [10:50] means it's one of these two things this [10:52] is not really super clean I wouldn't [10:54] actually want to do it this way but I'll [10:56] say if the score type [11:00] is equal to Apex bring that into our [11:03] application and else if this is B like [11:08] that okay good everybody's happy so I'm [11:11] typing have Union if we haven't returned [11:14] anything I'm going to raise a value [11:16] error and a [11:18] type and just a little exclamation mark [11:21] and actually what I really want to do is [11:23] I want to get the scar this is going to [11:25] return an object and then I'm going to [11:27] have to call that object still so by [11:30] doing this the last thing I want to do [11:33] is fetch that here and I can say in my [11:35] init file for adapters I'll say from [11:38] scorer I'll import um get scorer and uh [11:43] let's go down and Implement that in our [11:44] services now in my adapter's file I just [11:48] put that in my init file is get score I [11:52] can just fetch that directly out of my [11:54] adapters now here is the shopping cart [11:56] score no longer like that it's get score [11:58] and I need a score type so score type [12:01] and we're going to use um let's surface [12:04] this type up to here and score type and [12:09] it's going to be like a string we said [12:11] so now that just kind of is going to get [12:13] passed in from here [12:18] oops okay happy with that and we're [12:20] going to score our cart base on this [12:23] score which is returned from here now we [12:25] need to add the scor type to our app [12:28] wherever I score my shopping cart here [12:31] um I need my score type to be defined [12:35] what I could do is make this a global [12:36] variable in in my in my app here and I [12:39] I'm going to say we're going to use the [12:41] base one we know what to expect if I've [12:43] done everything right this should just [12:45] work okay it's saying no module named [12:48] Apex scorer [12:50] oh of course there's no module named [12:52] Apex score because we just created that [12:55] right it it it doesn't actually exist [12:57] for the purposes of getting this working [12:58] let's just like I don't know why I [13:00] didn't see that coming leave that and [13:03] say return zero and Implement score see [13:07] if that does the trick uh score shopping [13:10] cart is missing a cart argument okay [13:14] yeah this is a weird one this is an [13:16] object or this is a class you could call [13:19] it score class just so you understand [13:21] what's happening right and I need to [13:22] initialize that I was not doing that [13:25] that's what I want to do [13:27] okay we're working here this is [13:30] fantastic and one last thing I could do [13:33] just for my own edification is in my app [13:37] I want to try the other scor type our [13:40] awesome Apex model which [13:43] returns [13:44] zero like we see here oh yeah okay this [13:48] was a lot of fun as you can see it's got [13:50] quite dark outside I have another video [13:53] that talks about my t-u and my Vim [13:55] config which has eaten up massive hours [13:58] of my evenings this week and I think vim [14:01] and t-o configs could eat up massive [14:03] hours of your evening as well it has [14:05] been fantastic to go through this demo [14:08] and I hope you take these ideas and use [14:10] them in your coding and build better [14:12] python applications thanks so much and [14:16] see you in the next one