[0:00] in this episode we are going to look at [0:02] hexagonal architecture [0:04] hi i'm mark bradley and welcome to [0:06] testing all the things [0:08] a screencast in which we use live coding [0:10] to demonstrate different types of [0:11] testing tools and techniques [0:15] in this video we're not going to write [0:16] any code we're going to look at the code [0:19] we wrote [0:20] over the previous episode to create our [0:22] book api [0:23] and we're going to draw out the [0:25] architecture that we used [0:27] hexagonal architecture this is a way of [0:31] designing applications to be loosely [0:33] coupled [0:35] the idea was created by alistair [0:36] cockburn [0:38] let's look at the code [0:48] in the videos where we created our book [0:51] api [0:52] i tried to avoid using the architectural [0:54] pattern we were going to use [0:56] i wanted to concentrate it on the test [0:58] we were creating [1:00] but now i think it's important we look [1:02] at the architectural decisions we made [1:05] you saw how useful they were in the aid [1:07] of testing each layer [1:10] now let's look at how they can improve [1:12] your [1:13] applications in other ways hexagonal [1:16] architecture is very simple to [1:18] understand [1:19] there are many diagrams on the internet [1:20] that show how to use it but i thought [1:22] it'd be very useful to apply it to the [1:24] code we've been writing [1:25] so the first thing we should look at is [1:27] this the hexagon of our diagram [1:30] the hexazon doesn't actually mean [1:31] anything in particular it was just a [1:33] shape that was picked [1:34] but in the center of the hexagon is [1:36] where our business [1:37] logic and our business codes it i'm [1:40] going to put that [1:41] in our diagram straight away this is [1:43] where [1:44] business logic sits [1:50] stretch out [1:56] we're going to start over here on the [1:57] left hand side of our diagram [1:59] i'm going to add a circle [2:03] that sits here [2:15] now this circle represents the [2:18] implementation of our http [2:20] handler [2:23] if we if we go and look at the code [2:31] we called that [2:34] the book by isbn [2:48] controller [3:14] click those two things together [3:20] okay so this is our controller and the [3:23] idea here [3:24] is this controller understands [3:27] http and rest and jason [3:32] that's all it understands about our [3:34] application [3:38] what comes in and out of here from the [3:40] real world [3:59] what comes in and out of this controller [4:01] is http [4:04] rest and json those are the things it [4:07] understands it knows how to [4:09] handle an incoming http request look at [4:12] the [4:12] uri and recognize the [4:15] isbn in within the pattern of the uri [4:19] and it knows how to respond with json [4:21] for different circumstances [4:24] that's all it understands not quite [4:26] there's one other thing it understands [4:27] it knows it has access to an interface [4:36] ships here sits [4:39] in between our http controller [4:42] and our business logic layer [4:51] like so we've got this interface here in [4:56] our code [4:58] we called this interface we can see if [5:00] we open up our isbn controller [5:02] an isbn book retriever and that's just [5:06] an interface [5:07] with one function retrievebook by isbn [5:11] if we go back to our diagram we label [5:15] the interface [5:27] if we link that [5:32] like so [5:41] so now we're starting to develop some [5:43] understanding that so this [5:44] isbn book retriever is the interface to [5:48] our business logic layer [5:50] our controller layer doesn't need to [5:52] understand [5:53] anything that happens within here it [5:55] just needs them to know [5:57] that once it's got its isbn from the [6:00] request that came in the http request [6:03] once it's done its thing it then needs [6:06] to pass an isbn [6:07] over into this interface and this [6:10] interface [6:12] retrieve a book for it to render into [6:14] json [6:15] and return so that's what we have here [6:19] it's very simple interface in fact i'm [6:21] just going to put [6:22] i here so we know that it's an interface [6:33] and then this jumps into our business [6:34] logic so our business logic is the thing [6:37] that implements [6:38] the function within this interface [6:42] which is retrieve book by isbn [6:46] which we called the book manager so if [6:49] we go and [6:50] label our business logic area [6:56] this is our book manager [7:05] it's there okay so our business manage [7:08] logic layer here is our book manager [7:12] and it knows it has to implement the [7:14] interface here that sits [7:16] between it and something that gives it [7:18] an isbn [7:19] so it knows it needs to return a book it [7:22] also knows it then [7:23] is responsible for obtaining a book from [7:25] somewhere [7:26] and this is where we start looking at [7:28] the left-hand side of our screen at the [7:30] right hand side sorry of our screen [7:34] and this is where we start looking at [7:36] the right hand side of our screen [7:39] our implementation our business logic [7:41] layer takes a book [7:43] repository as a dependency [7:46] and again this is another oh okay [7:51] it should have been an interface so [7:53] let's [7:54] go back and maybe we we could have had a [7:56] refactor here added that [7:58] interface which would have been better [8:00] um [8:02] and maybe we should do that so let's go [8:05] back and implement the interface [8:08] stuff set up in the code so we want to [8:11] create another interface [8:12] that sits here [8:17] okay whatever interface that sits here [8:31] and we will give this a name now [8:41] so i will quit i [8:44] sbn book repository [8:52] and that's gonna be another interval [9:06] interface [9:09] i'll go and create the interface in the [9:11] code after this and just push it i won't [9:13] make you watch me create an interface [9:14] and make those [9:15] code changes but we'll create that [9:17] interface and we'll call it that [9:19] and we know that that interface is [9:22] implemented [9:23] by a book repository so let's [9:27] create the implementation on this side [9:32] yeah okay [9:42] okay i'm going to give that a label and [9:43] that was the book repository [10:05] our book manager businesslogic layer [10:09] now implements interface it knows that [10:10] it has to [10:12] respect and it also makes use of an [10:14] interface [10:15] that it can use to obtain books a [10:18] repository [10:19] a storage solution for books because of [10:22] the interface [10:23] we should have created it doesn't have [10:26] any knowledge [10:27] of how those books are stored how [10:29] they're retrieved [10:30] it just knows that if it gives a isbn [10:34] book repository [10:35] an isbn then a book will be returned [10:38] and it can then fulfill its interface [10:41] with [10:42] over here and return that book to the [10:45] thing that's been asking for it [10:47] we could have done some better naming [10:49] here this could have been a refactor [10:51] that we [10:51] we could do as well so our book [10:53] repository again it doesn't really give [10:55] us much information [10:56] by the class name so we could have [10:58] called this book repository [11:00] instead of just book repository we could [11:02] have called it [11:03] postgres book repository or psql book [11:06] repository [11:08] to give the user of the book repository [11:12] not the user of the interface but the [11:13] user of the repository the understanding [11:16] that [11:16] this book repository uses postgres [11:20] we know now that this book repository [11:23] needs to talk to something [11:25] in the outside world so let's represent [11:28] that [11:40] okay and this is i'll stick a label on [11:42] it [11:48] the sql doesn't fit with it now [11:53] there we go [11:57] okay so the book repository here [12:01] is completely responsible for the [12:02] communication in and out of [12:11] psql [12:21] there we go so this is responsible for [12:24] implementing the interface [12:26] at least one of the interfaces we know [12:27] about the isbn book repository [12:30] and interacting with the outside world [12:33] so this shows you kind of how these [12:36] hexagonal architecture diagrams relate [12:38] to the code [12:39] we've created so i know we've got a bit [12:42] of [12:42] stuff that sits around over here which [12:44] is our application which probably [12:45] actually [12:46] imagine sits around all of the code [12:47] we've got here we'll ignore that we're [12:49] just looking at the [12:51] the details of application not the the [12:54] bootstrapping provided [12:56] by the slim framework so hopefully you [12:59] can see [13:00] why this way of structuring your code [13:02] and following this [13:04] architectural pattern makes your code [13:06] very testable [13:07] one thing that can make your code very [13:09] testable is following the [13:11] s of the solid principles the single [13:13] responsibility [13:14] and this is a really good way of [13:16] separating out your code so each layer [13:18] has a single responsibility [13:20] if you imagine here but it's nice over [13:23] here the controller is responsible [13:26] for handling http understanding http [13:29] rest and json so not [13:32] masses of stuff really sitting over here [13:36] just got one responsibility and it has [13:39] knowledge if one thing passes [13:42] it's once it's done its thing it can [13:44] pass an isbn [13:46] into our business logic layer now [13:48] business logic layer [13:50] okay it's at this point it's very simple [13:52] its responsibility is very low [13:54] it is to it knows it has access to [13:57] something that [13:58] holds a number of books and it can [14:01] access those books by the isbn it's been [14:03] given [14:05] but if this is if we had this business [14:08] logic layer [14:09] it was responsible for the saving of [14:12] books or the creation of books [14:14] then the business object layer might [14:16] also do some validation [14:18] of the data that comes into it [14:21] and reject books for saving if they [14:24] don't have an offer or they [14:25] have too many numbers of pages or [14:27] something like that and then [14:30] we would have another interface sitting [14:32] on this hand side would be would be [14:34] book store interface our book repository [14:37] would then implement two interfaces [14:40] and interface with our database [14:44] and again small amount of responsibility [14:47] over here sitting here [14:49] our repository is just responsible for [14:51] taking data and throwing it to its data [14:53] store [14:54] in this case a postgres database that [14:57] shows [14:58] that's what that covers why this [15:01] way of writing code and separating code [15:04] out [15:05] makes testing very easy the other thing [15:08] it's really [15:08] useful for is it means that because [15:11] you've got your business object that [15:12] sits [15:13] in the center and business object would [15:15] be [15:16] reasonably stable okay then you know my [15:19] feature changes [15:20] might slightly tweak the business logic [15:23] layer [15:24] and yes that would have um [15:27] cascading changes potentially on the [15:30] controller side over here on the left [15:31] and possibly on the repository side over [15:33] there on the right [15:34] it might be more data need to be stored [15:36] and more data be returned or something [15:38] like that [15:40] but if we want to make more sweeping [15:45] external architectural changes to our [15:48] application [15:50] it means we don't have we can do that [15:53] very simply without having to affect our [15:56] business logic [15:58] so for instance if we wanted to make [16:00] some changes to the [16:02] the front interface the access to our [16:05] application [16:07] we can easily make these by creating [16:10] something else [16:11] that can obtain an isbn [16:14] and pass it through our interface into [16:17] our business logic layer [16:19] so for instance we could change the fact [16:21] that it is http [16:22] rest to be a grpc [16:26] implementation or it could be [16:29] not grpc we could make it a graphql [16:31] service [16:32] or we could have the fact that [16:36] somehow the communication is not http at [16:39] all but it goes over [16:41] a queue or kafka topics or something [16:44] like that so [16:45] this part here [16:48] is completely interchangeable without [16:52] affecting our business logic layer [16:55] and the same can also happen on [16:59] the back end of our syst application our [17:02] data store [17:04] so because of the interface here we [17:06] could have all sorts of different [17:09] options if we decided we didn't want to [17:12] use postgres anymore [17:13] we want to switch out completely we can [17:16] switch out to [17:17] mysql a different sql database [17:20] or we could go and say actually we don't [17:23] want an sql database at all we want a [17:25] nosql database [17:26] or a document store we want to go to [17:28] using mongodb [17:30] or elasticsearch we could quite easily [17:33] again swap out our data store our [17:36] repository of books [17:38] into a completely different technology [17:40] and our business logic would not be the [17:41] wiser [17:42] all it knows is begin it's being given [17:46] something that implements the interface [17:48] that it needs to obtain books [17:51] obviously the example we implemented was [17:53] very simple it was just about retrieving [17:55] books [17:57] but we might want if we look think about [18:00] now the creation of books [18:02] and we keep with our http rest [18:05] implementation so [18:06] a book is given to us via json [18:10] and we can take that json and the http [18:13] request [18:14] and construct the data that needs to be [18:16] passed over whatever interface we create [18:18] here [18:19] isbn book creator maybe interface and it [18:22] goes into our business logic layer [18:24] now business only player would know they [18:26] could store [18:27] the book that is given into an a [18:30] bookstore [18:31] interface and that's implemented by a [18:33] book repository [18:34] now as we remember we could say that our [18:37] our validation of the incoming book [18:39] would sit within the business logic [18:40] layer [18:42] and that's quite simple um but [18:45] what might happen when we're creating [18:47] books we might want to do [18:49] more than just store our book [18:54] over here on the right hand side we [18:56] might want to [18:58] notify something of our [19:01] book being created so what we might have [19:04] is another interface that sits over here [19:07] so book creation notifier maybe [19:10] and that could be implemented by again a [19:13] number of different things we could have [19:14] it sending an email to all the managers [19:16] that a book has been created [19:17] or we could have an implementation that [19:19] sends an sms [19:21] message or sends a slack notification [19:24] to people that a book has been created [19:27] okay so that's kind of power the [19:29] hexagonal architecture [19:31] it's nice it gives you the ability to be [19:32] able to swap things out [19:34] very simply without having to affect [19:36] your business logic [19:38] it keeps your business like central to [19:39] everything [19:41] i hope you enjoyed this walk through the [19:43] architectural design [19:45] of our api i'll make the changes and add [19:48] the interface [19:49] we talked about and add this diagram to [19:52] the repository [19:54] see you again soon [19:58] thank you for watching this episode of [19:59] testing all the things i hope you [20:01] enjoyed it [20:02] if you did please rate and review the [20:04] video and subscribe [20:06] to the channel if you did not and you [20:08] have any feedback [20:09] please leave a comment in the comment [20:10] section of the video [20:12] or contact me on twitter at bradley