Anatomy of a Spring Boot App with Clean Architecture by Steve Pember @ Spring I/O 2023

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
foreign [Music] welcome everyone to my talk my name is Steve I feel kind of uncomfortable talking about myself so I'm just going to leave it at that I work for a startup called stavi located in Boston in the United States and this stock is called anatomy of a spring boot uh Springwood amp with clean architecture now before the pandemic I would attend one or two software conferences a year even speak at a few of them um this is my first conference since 2019 and I'm thrilled both to be part of a conference once again because I love this stuff but also to be here for spring IO their 10th anniversary yeah it's nice anyway normally I talk a lot about event driven systems distributed systems event sourcing domain driven design and I love sharing these ideas at conferences and I will check your ear off about these things if you afterwards if you wish and while I do talk about these topics a lot my day-to-day jobs most of the time my co-workers and I end up talking more about the specifics of a given code base not so much messaging and mechanisms like that I mean once you once you know that becomes dare I say a little easy over time I know I'm being yeah you know arrogant there but anyway uh so we end up focusing more on what an individual system looks like how the how is the code laid out what should you use for data persistence are you keeping your concerns separated like letting orm models bleed into your main service logic is there business logic in your controllers Etc I realized that over the years of my career I've accumulated a set of practices that I and my colleagues believe have successfully fought against the chaos of a big ball of mud that often happens as a software artifact grows and these practices these patterns are very much inspired by the Giants in the software industry who have come before us so the past few years I've ended whoops I've ended up in positions where the architecture of a piece of software was uh either my decision or I had or I had great control over it being put in such a position is a bit daunting so I did my best to read a lot study a lot one book which I ended up reading during the pandemic was this one clean architecture by Robert Martin it's kind of a sequel to something called clean code also by the same author has anyone out here read this book before yeah there's a few hands that's a good number great I've realized that the recommendations in the book are the most aligned with how I and the people that I've worked with in the past tend to think about constructing systems and so the goal of this talk is to present to you some of these rules um and how they can be expressed within a spring app we'll talk a little bit about software architecture in general and then clean architecture more specifically the best system architectures I've worked with have nearly always been on the jvm so we'll go over what clean uh architecture concepts mean to me and how that looks within a spring app and I've got a little demo at the end that hopefully we'll have time for uh so to begin what even is software architecture which is a large lofty claim I know uh uh first I'm curious if any of you all agree with me uh on this idea that over the past few years the job title of a software architect gives people a little bit of a bad Pace in their mouth um you know you kind of get a little wary when someone calls themselves an architect I'm not entirely sure why uh perhaps the impression that all they do is whiteboard things draw boxes and lines you know they only think at the high level um and I mean I've certainly worked with people like that in the past that have had that title uh and all they did was draw boxes and lines on screen and uh and often they were often mean you know a little rude still it's a shame maybe that's why I don't know it's a shame because architecture is not just about the high level of the boxes and lines a real architect real architect uh for example creates Blueprints and In These Blueprints every detail is covered the high level room structure down to where like the electrical sockets will go not specifically in this example but not like a you know big old blue actual blueprint um and so it should be the case with software a good software architect should be able to look at uh think at and design at every level of the stack from a microservices topology all the way down to a SQL query or whatever and just like a real architect a good software architect should provide plans that your team can follow to build a system okay why should we care why should we care about the architecture of our systems there's plenty of successful businesses out there with terrible software uh maybe some of you all work for some of those places huh yeah we all have code bases that we prefer not to share with people and hide them away uh anyway all that matters to your organization's leaders and maybe this is an American thing but I highly doubt it all that matters to leaders is does it work right does it make us money anything else is secondary your company or your leaders don't care about the cleanliness of your code or what architectural style you're using or you know what it achieves at least not at first they don't at first when Building Systems everyone wants to go fast so structure proper architecture any rules or guidelines at least they just get in your way all right we don't need architecture we're going to go fast uh and I believe this is exactly why we end up with those spaghetti code disasters you start with the smallish team working on a single artifact uh there are no rules no structure just get just pump out code as fast as you can do the typey types you know get it out the door uh and these things they have to go fast of course they're going to end up with a mess and what's interesting about this often teams do not want the structure the rules and I think this is why you know some typeless languages like Python and Ruby are so popular JavaScript developers feel that the language itself is getting in their way and I don't mean to poke fun at them it's just an example the same is true of any system without any guiding rules people don't realize what it's actually doing which is providing some appropriate guard rails to help you along the way without guardrails without structure eventually the system becomes brittle loose uh easy to break too hard to maintain teams lose the ability to do estimations on the code with any reasonable accuracy new features grind to a halt and your competitors will start to overtake you your managers will get mad very mad why didn't you do better why can't you go faster etc etc hindsight right and that shouldn't be the case we should all do our best to avoid that impending crash everyone in your org should care about how well your systems are designed how good the underlying architecture is now I know all of you know that having a decent architecture to follow is a good thing I mean who would not want to organize the spaghetti right look at those boxes it's nice um but for all you managers out there uh having a well architected bit of software offers quite a few benefits and I'll admit they're a bit hard to quantify so I can't have fancy graphs or anything but having a good architecture brings you know separation of concerns highly modular decoupled systems which are flexible highly testable and arguably most important this system is far far easier to maintain than spaghetti and your developers will move faster for longer adding guard rails may seem like it slows you down at first and it and it may you know provide a little bit of a boundary but in the end you'll move much faster it's an investment in the future of this system now there's a difference between speed and velocity proper architecture proper guard rails we keep your system moving and your team moving at a nice constant velocity for the lifespan of the system another way to look at it software architecture is a set of structures to guide the future development of your artifact but it also requires the discipline you and your team to adhere to those structures like you can have all the guard ways in the world but if you ignore them and just let it go through in a pull request that's not having the proper discipline now it's also pretty hard to quantify this good architecture provides structure to a given Deployable artifact like I keep saying or service however it has to be just enough architecture not too much to Balancing Act right you need some guard rails some but not too much while still allowing the engineers to express themselves if you constrain them too much they will Rebel uh now it sounds very vague and it looks like you get a lot of pressure put on you to get it right and you know know this it absolutely is a lot of pressure you'll never know if you were correct whatever architectural style you choose you're probably wrong in the long run you know so that's nice but at least it's good to have something um I realized all of that was a little vague you might be thinking to yourself Steve that was a lot of words you just said which meant absolutely nothing so let's get into some specifics uh we're gonna quickly go over a few existing architectural principles and build towards talking more about specifically this clean architecture concept now there are a ton of patterns um existing systems or guard rails out there when designing software for for variety of different use cases and situations many buzzwords uh have come around over the years you know model view controller being object oriented microservices Etc there's many but they're all specific to certain situations like microservices how to build a distributed System model view controller how to handle some front end um front end roles and purposes however there are a few systems out there which in my opinion are broadly useful by everyone at all levels of the stack and they share many similar ideas in my opinion these similar patterns are ports and adapters hexagonal architecture onion architecture and the star of the show clean architecture there are more of course um I I admitted to main driven design another one of my favorites but I highlight these specifically because of how similar they are you know with all of these they generally again generally have a similar concept um so much so that this is a diagram associated with hexagonal architecture which is confusing because it has adapters written everywhere um your software but in all of these all of these Concepts share so all of these systems share some of the same Concepts right your software layers are imagined or should be thought of as nested Rings or in this case a hexagon um I don't know why it's a hexagon I think the author said he didn't know why it was an evocative shape um the goal of these Rings is to prevent prevent dependencies from leaking into the core ring but you can have multiple ways to get input and output and the ports are used by the inner Rings or The hexes to express a desire to communicate with some outer ring um and through the end and the the inner rings are agnostic to whatever the the implementation of the outer ring is and so the con the concrete implementation is called adapters are used to actually perform the communication through these external systems now typically we would use interfaces and implementations of those interfaces to express these communication channels um in when handling a request input generally travels from the left of the core and outwards uh to the right I don't know why they chose the direction like that but it's fine and I now again I say this is broadly useful because we could follow this approach as a single backend application like this this image is is suggesting uh or we could implement this say in a front end you know my various adapters could be wrappers around my an in-memory state repository or point to various server-side apis or you can do this in a microservice architecture where the adapters could be external services like a notification service which is which is a service dedicated to acting as an adapter which handles communication with say twilly or simple email server on Amazon uh that's just a diagram oh look boxes and lines we finally did it realistically with a class diagram layering looks much more like this um I apologize I had to write a lot of notes because there's a lot going on here uh here we have an order controller which is the main entry point for some front end which is trying to fetch some information about orders for a user in some hypothetical e-commerce application it has it has the order controller has a has a dependency there in these order details this order details API response object on the left there it's the controller's contract with the front end it needs to return that to the front end now typically you might use something like open a open API or something like that to to generate those things but the important bit is that it exists and when the controller receives a request to this certain endpoint it will call the search function uh on the controller class and now this function except some query parameters but returns that order details object which the front end is expecting and the front end has a dependency on that specific class right there inside the controller body it needs to call the core of our hexagon inside our core we have some we have some set of service classes and one of which is probably something like an order Management Service this service has one public function which expects to receive an instance of a class called order query primarily and the order Management Service oh so the this means that the controller has a dependency on that order query object so if that order query object changes at all the controller is well in trouble and it has to adjust itself to whatever the new shape of order query is it also has a little dependency there on the order Management Service because it's just calling into the method that method ever changes you know then the controller also has to change anyway this order query object gets passed into the order Management Service which in turn hands that off to the order repository to execute the search and the order repository here is an interface the port and the adapter is the actual implementation of the interface the postgres order repository which in turn knows how to communicate with postgres to store and retrieve order information now the interesting thing to note here is that the dependencies on both sides of the structure are pointing inward toward the core the controller needs to understand the query class and the postgres implementation needs to understand the methods detailed in the order repository so that it can support them but if there's one thing you should take away from this talk whatever else I say is dependencies should always Point inward to the Core oops anyway these patterns all commonly express the same basic concepts which in my opinion uh are the broad characteristics of a successful system proper layering with separate and logical boundaries a core domain package encapsulating your core business logic your rules which is agnostic to the outside and appropriate use of inversion of control mechanisms and again these broadly appropriate patterns they are useful for providing that excellent level of just the right amount of structure with the freedom to still Express yourselves and have a little fun now let's dive a little deeper into the specifics of those uh uh patterns you know again the reason why we're here the one I'm going to talk about um is clean architecture of all of those similar ideas I believe being clean it's a fun buzzword but it's the most comprehensive um it gives the most advice however I may editorialize a little bit because after all what we'll get into is just that it's advice no advice is perfect 100 of the time so from here on out I will be maybe blending or mixing a little bit with what Robert Martin expressed in his book along with some additional tidbits and some extensions maybe I will try to note where my opinions differ it's not that many but if I say something that's glaringly contradictory to the book of the people who had their hands up you know don't immediately jump out and yell at me but we can talk about it you know afterwards uh so if you ask me to summarize clean architecture which I guess you all kind of are because I'm up here talking about it I would break it down into four high level Concepts the notion of being solid component design boundaries and their dependencies upon each other and something we'll get into later details now I promise this slide uh it has the most words on it and I'm I'm cringing just looking at it and I'm sorry I just did not want to make a slide for each one of these I was being very lazy I apologize but uh are we all familiar with the solid principles no there's a few hands okay good all right no this is good so they all came from originally uh the famous clean code book by Robert Martin and uh in his opinion this is an acronym for principles that a good coach should follow uh the single responsibility single responsibility open close liskov substitution interface segregation and dependency inversion and what's interesting is that the words the definitions don't it might they don't actually often mean exactly what they say or they're a little confusing so single responsibility sounds like it should do one thing maybe it should but the actual usage of this one is different from the words what it means to me is at any given class should only support one user type that is if you have a large Class A large class which handles say Financial calculations uh Financial calculation logic and it's got stuff in there to check like are you an admin are you a customer are you somebody running a report um these different users may need to calculate things different into different ways depending on their job role so classes use classes so you should what you should do is break that up and use classes so that both the uh so with your admin one and your users in the other um you'll need lots of uh yeah using updates to logic of one user type might affect others accidentally so if you keep them separated that this is the user or the general user Financial calculation service that keeps it separate from the admin doing uh nasty things that you don't want the users to be able to see or vice versa uh open close is another interesting it's another weird one it basically means to try to avoid changing code which sounds great right once you write it don't touch it again um and if you need to tweak the business logic of a core of a given service class or a use case which we'll talk about in a minute instead of adding more control flow more if else's instead of editing the actual code think about adding delegation functions more interface implementations uh or use patterns like the visitor or strategy don't you basically don't touch the code list of substitution is also fun basic uh basically an interface implementation should do what you expect which kind of makes sense but it's fun to have it written out you know for example if you have a database uh and a method called find by ID on an interface and if you implement it and find by ID actually delete something that's not what you expected and that would violate that principle uh interface segregation it's pretty straightforward it basically means don't make huge classes used everywhere favor smaller interfaces and more use cases or service classes targeted towards specific situations and of course uh dependency conversion is basically just use interfaces as your base type like declaring a variable as a list instead of an array list or using interface classes in dependency injection in the next section uh component principles you've got code you're writing away using these solid rules but how do you organize your code um we you know a component in this case is the it represents the ideal boundary you bet you would need to partition your system into isolated independently developable developable structures that can be built on their own tested on their own and compiled on their own the components would then be turned around and handed out to individual teams to develop in as much isolation as you can from each other but they should and so they should also be Loosely coupled and if we were using domain driven design for example these components would send domain messages between them for communication but we're focusing this is clean architecture for now incidentally microservices are the ultimate expression of this isolated component principle a microservice represents programmatic distributed hard boundaries between your components they're not always the best choice for a given system and you know it's not the focus of the talk so right now we'll ignore this but you can tell I want to talk about it but we're not going to uh let's see so it's difficult to design your components from the top down meaning this goes back to The Balancing Act the guard rails if you try to start with a rigid structure this is my order component this is my user component a preordained set of components that just leads to just leads to more rigidity it's too too much guardrails you will almost certainly have no idea what the final form of your system will be and which components actually end up making it in which components get added which ones get removed and so the clean architecture sets the guardrails and lets your teams evolve their own components so these next two sections to me represent the most important bits of clean architecture I mean the previous two had lots of solid advice okay that was a really bad pun I'm gonna go I'll finish so we have these components uh the the next two bits are great so we we have these components we know we need to avoid cycles and point dependencies in word and also allow the growth of our components these Concepts culminate here if you search for clean architecture in your favorite search engine you'll see this image or something very nearly like it clean architecture it looks a little bit like the the hexagonal one we saw earlier right we have our core with our entities surrounded by use cases in Spring we might think of them as service classes uh the the controller is just outside of there and then our inputs and our outputs still farther away from the core Circle was fun and very colorful uh but let's look at more boxes and lines now and I apologize this is a very busy picture but to me this is still further what those rings represent here we have some sample Payment Processing happening and I apologize it's very tiny if you squint I don't think I can zoom in because it's a different monitor but what's Happening Here is a user request might hit that controller at the top which in turn calls the simple payment service down there it in turn calls to supporting service classes or repositories or Gateway objects and then they in turn call into the uh the actual concrete implementations of their of their of the represented uh systems there so you know I'll record the details in The Ledger I'll send on some emails I'll execute the actual payment transaction but each of those things are hidden away in their actual implementations and so those dotted lines there they represent then a component well I neglected to actually point out any entity classes or entities for example here these you know they would uh they would all go in that Center core component there now again notice the directionality of the the dependencies they all flow into the core component into the core Services the main thing to stress here is that without these repository and Gateway interfaces in the way our CoreLogic would start to have hard code written about postgres about simple email service stripe all baked within our Core Business logic right if that if that payment Gateway wasn't there what's stopping somebody from throwing in the stripe API client into the simple payment service that you know it's kind of gross it's also a maintenance nightmare trying to detangle those things uh all data transfer across component boundaries should be either direct method call or with a small data class use Java 17 record classes or kotlin data classes they're great for this another word for these are in other words for these data data transfer objects or dtos now an easy way to think about all these representations is that when you're moving into the core you want to use Simple data structures and then you have interfaces going outwards towards your external dependencies the primary rule of the clean architecture book is that the outer layers may only see what's happening in the inner layers the inner layers have no idea about the outside so our service classes have no idea about the controllers nor do they have any idea which database type we're using if any there's a beauty in Simplicity to this thought which is perfect the if you do it right your inner layers are immune to changes in your outer layers so uh and so Martin in his book also calls this a plug-in architecture the outer layers should just plug into the inner layers for as an example so here's an example of a UI right I mean I might have a user who's working with my fun react app uh and the react app is internally talking to my application which has a number of controllers and so the interesting thing here is that the back end this actual application itself is completely immune to any change that the front end decides to make they can change the colors they can change the CSS they could add you know they could replace react with angular you could just be an API it doesn't matter the actual business launch or whatever they do it's not going to affect my actual set of controllers like they could change the url or whatever they're calling and that they'll break but I'll be fine they'll be fine um and similarly the core of your your core of your system could be it could be immune to or should be immune to schema changes database choices new microservices that come around Etc which can be accomplished with lots of usage of interfaces in the repository pattern so going back to this for a minute like the actual UI is just plugging into the set of controllers that I have now lastly in this section I wasn't exactly sure where to add this because our components are all individually testable you should quickly develop a test Suite that you trust and having a repeatable set of tests allows you to safely clean your code I mean I personally believe that a good Suite of integration tests which cover your apps efficiently it's like a warm safe cozy blanket that you can hide under and keep you safe while you refactor your code it's nothing better um the next section uh now this this section number four is what this is what resonated with me the most and I hope it does for you all as well so building off the previous few points The Logical extension of everything we just talked about everything that's not in core is an implementation detail just the details so good architectures allow important decisions about the details to be deferred the database for example are we using redis what about queuing it doesn't matter we'll variator it out later in English we'd say something like you can kick that can down the road meaning you worry about it later procrastinate as long as you can just keep kicking that can down the road we'll figure out whatever we're going to do later start developing your app use an in-memory database for now eventually we'll figure out if we're going to use Dynamo or postgres or whatever we'll worry about that later so with that in mind uh for example your database is a detail right are we using postgres we're using my SQL we're using dynamodb we're using redis to store things doesn't matter not really I mean if whatever you choose you should make sure that you're using your the tool you chose appropriately but it doesn't matter so without our talk about interfaces it should be relatively simple to cut over to a new data store if needed your requirements change for example now will you ever actually change your database like you know we start with postgresume we decide oh we're going to swap the Dynamo you know no not really you might not ever actually do this but the point is you can the point is the encapsulation of the details uh of your data store whatever you choose just to keep it separate from the core and it just so happens that a side effect of properly encapsulating your data store details allows you to swap them out if you want to they just plug into your application uh you know quick sample code so my systems in core are hoping to receive as an example hypothetical situation uh my systems in core are hoping to receive an implementation of some Ledger repository and if I've done things correctly it's just a matter of creating a redis ledger repository and replacing that return value in my spring configuration so that the rest of the beans get injected with an implementation of a redis version of this repository but the core of the system doesn't know or care where that data is going uh so a quick discussion um I think at this point this database discussion out of all of these that I'm making is what again resonates with me actually the most now question for you all and I want you to be honest if your manager comes to you and says we need a new microservice or we need a new feature a new component how many of you out there immediately your first thought is immediately something like oh what what are the database tables going to look like anyone I see some Brave hands stop it don't do that it's not the right it's not the right choice so the database is not important not really it's just a it's just a mechanism for storage you should treat it like okay that's the thing I'm gonna put data there it's fine but it's not shouldn't be your first thought I mean it's important like I said to use to use whichever tool you choose properly but it's not your core business the rules are the thing the stuff that exists in core is your entities your services your business logic that's what's important whenever I walk through a problem with my co-workers or I give a system design interview and the first thing the other person talks about as a database schema you know I die a little bit inside you know are we going to use incrementing ins for primary key too great all right uh anyway next thing your environment is a detail your code must not know or care where it is being run is it on your laptop is in the QA environment is it in kubernetes is it ec2 instance is it an ECS is it like in a data center running a bare metal doesn't matter it doesn't matter your system shouldn't make a difference um let's see what else do we got there yeah avoid environment specific configuration and instead rather inject environment variables into a container or fetch config values dynamically with something like hash evolved um there can be many ways to talk to your system your core components and your systems shouldn't know or care about the input so input is a detail if an instruction is coming from HTTP from messaging from sqs from Kafka it doesn't matter your system just treats it like another another thing it doesn't know going back to the plug-in concept my controllers or let's say I had a message broker that was observing an sqsq they should simply plug into my inner core layers now this is done by leaving those concerns that are specific to the input within the inputs component so HTTP component contains API response objects maybe it knows how to deal with user authentication Etc the messaging component may know how to talk to a message broker convert data coming from the stream say like a protobuf message into some internal data structure now typically the input components are should be as small as possible parts of the data convert it to an internal object and then hand it back or hand it off to the to the core of your system and again you'll know you did it right if a controller and a message broker could receive and work with the same internal service class um lastly I know this is scandalous all right it's crazy to talk about this at a spring conference but spring too is just a detail uh do your best to prevent your framework details from leaking into your core code So speaking of spring uh let's this brings us to Spring and the jvm um so luckily for all of us it's remarkably easy to achieve these clean architecture rules within our chosen tools there are many features within spring within Gradle Java and yes kotlin too that help to enable a clean architecture and generally make it easy to set up the guardrails for our team members moving forward and so I'd like to take a minute or five to talk about a few of these particulars modules and sub-projects controllers messaging Etc so the clean architecture book calls for individual components with programmatic boundaries now this is remarkably straightforward on the jvm but it may not be your first instinct right with within your individual application consider architecting it as a multi-module or sub-project code base like normally when you first create a new app and there's that Source folder sitting right there Source main Java Etc try something else no it may seem cumbersome at first to do this but it's relatively easy to create sub modules within uh at least something Gradle I haven't necessarily done it in Maven but it's just right click new module in IntelliJ and you're Off to the Races and it may seem cumbersome to work this way with multiple Source folders but it's an excellent way to express the ideas uh the ideals within the book a module or a sub-project is an independently built independently tested packaged as a jar and it even has its old built its own build.gradle file or palm.xml for example and at a minimum I suggest three modules and they are core which as you might expect I've said that word I don't know how many times it contains your core business logic it's the most important part of your system it holds your entities your use cases which is basically your services lightweight dto objects interfaces and then lastly whatever I mention this people go well wait I need to have logging I need to record metrics and that's fine you can put those in there too as long as you use slf for J or micrometer those are very acceptable thing things to put in core because they too are compliant with this idea because they're just implementations right if I use log back log for Jade datadog graphite Prometheus those are just details to your logging in metrics functionality the details component uh you know you certainly don't have to call it that of course but generally this would be the place where you put everything related to your output adapters for example anything related to databases anything related to message Brokers third-party clients HTTP commands to talk to other microservices I also like to put data migration files within the resources folder of this component which again is going to feel a little weird because you move it separately from your main application or your main runtime spring basically and so that brings us to the app which is the third the third module that I propose starting out with it's also known as where spring lives it's the app configuration it injects props properties and runtime implementation of interfaces into the core module all those fancy interfaces and with implementations we talked about that's where they go injected into the core ad runtime this is the place of integration the place where it all comes together you know we end up programmatically having to register the use cases and service classes in core as beans and it's you know it's the last there it's the according to the book it's the dirtiest of all components it's the most unclean I know it feels weird again to say that at Spring conference calling it dirty I promise I'm not I love it uh uh so another aspect of this is as you start building out these modules uh you know I hadn't really done this or I hadn't done this when I first started my career but a nice feature of both Java and kotlin it gives you the ability to hide uh things that shouldn't be exposed outside of your component and so I apologize it looks really dark there um but one thing to grasp is when you're writing code not everything needs to be public like and I know we're writing Java and it's second nature public class Foo but you don't have to do that if you just say class shoe row mapper that shoe row mapper is Now package private it can't be used by anything else out of my package in kotlin I can label things as internal and so it can't if i label somebody's internal it can't be used outside of my jar file which is a component which is we're building multiple components becomes very straightforward and natural to start doing uh and as if I think it's Java 9 We Now can do create Java modules using using module.info which is a nice Step Up Above just doing package private everywhere so again those are three that we start out with plan on growing because again don't try to force a component structure Let It Grow organically and naturally one thing to keep in mind again is that if anything these patterns are a good are great to start when building an app and you may outgrow it but following these rules is a generally useful way to structure your code in a new application um I uh there's a there's a there's a couple teams at stavi who are working on a little model of app together and it has they've started they've been running with like a bird theme as part of the application so I said hey guys let's start with core and app and details and they're like yeah I see that's great and they immediately renamed after Main and then they started they went wild with the bird concept so they started calling the modules eggs and then they made sub eggs that deal with individual details they're calling shells and now they're talking about yolks and it's getting a little crazy but they're having fun right they're expressing themselves while still following the rules I set up and it's great and they're going to hear that in the future and they're going to watch this video and they're going to get mad but anyway uh input HTTP controllers and it's simple use spring web MVC it's great it's tested and true um you know nothing more to say there but again the control your controller classes should be as minimal as possible basically convert user input to some dto handle authentication authorization you know use filters for example um and then immediately call into core services and then on the way back out again map it into some sort of user-facing API object and again I know it's hard to read but and this is the briefest of examples it's a baby's first controller here but anything larger than this should be immediately suspicious of it because easy controller class is larger than like three four or five lines of code doing too much uh messages and queues I'm running low in time so I'm going to talk a little bit faster uh take a look at Spring integration and and its message template if you're using things like like Kafka or revitmq both messaging and HTTP are just input mechanisms you'll know again you'll know you've done this right if you can swap them in and out and if your service class doesn't know or care where the data is coming from whether it be message or whether it be a controller now below the controllers generally sit your service classes you know as I'm sure we all type second nature now spring service stereotype is a wonderful analog to the this this notion of what Robert Martin calls a use case um now to express what he means by use case it's it's a it's basically a single single responsibility service so I know and I've done this before too write a service class that tries to do everything an order service it might have a ton of methods in there instead prefer to make more of them and call them things like customer order query service it's more specific about what it's doing and what it's responsible for and it's more evocative it's a better name almost all of these will live in the core component and as a side effect of putting most of your objects in core and spraying out of your core is that uh you know you you your beans need to be declared outside uh of the spring component and you end up doing this quite a lot it can get repetitive I admit kotlin can make it a little easier there's something equals there I don't know all right profiles and environment variables so again where my code is running is just a detail your code must be agnostic not care not even know if it's currently running integration tests um no I know it's relatively common to use different config files per environment using the spring profiles to do like application Dash prod application Dash Dev you know and I've certainly done this myself I viewed I've utilized spring profiles to declare different values and values for my environment um based on if I'm running it locally in a tester etc etc and it seems nice on the surface but using profiles as environment specific properties kind of violates that every environment is a detail uh it's better than your code knowing but still um now regardless I'm sure we've all seen code that does stuff like this right if environment is Dev do X or Y if QA do this thing it's gross don't do it and if it does you know you've messed up if you ever catch someone writing code like this immediately fire them right immediately stack them it's terrible uh now testing I disagree with Robert Martin on this one he recommends not testing your details meaning don't test the database at least that was my interpretation in the book um however I want proof that the SQL the sequel I wrote works and continues to work uh given that the app component is the place of integration the integration test should go there you know I'm a big fan of test containers and we use a lot of AWS and so there's this wonderful tool called the local stack which we haven't used it it's delightful having a repeatable set of integration tests that cover my application again warm blanket it's wonderful I don't care what the book says it's great and it's so easy to do this jvm spring test containers you know my integration Suite runs my application it launches all of my containers I can trick the application to think well not trick because this actually is connected to a dynamodb for example it's actually connected to a redis it's actually connected to a postgres but it's agnostic to where they actually live just as it should be and then I can make use of spring Dynamic configuration to update uh the the the application after it started up to point to these uh these systems that are local to it and this can run on my laptop it can run in CI CD once the test pass it all goes away it's ephemeral and delightful I love it um let's see your core app should be talking to it to uh could be talking to a database a cache another service an external third party it doesn't know it doesn't care uh you know databases and stores are the data stores are tools and everything should go behind a repository interface implementation goes in the data the details module um all third party calls etc etc no this last bit may seem horrible to any spring jpa fans and this may be where I lose you all um one side effect of following these rules is that you should have no entity classes in your core module I know it's harsh right why because if you once you use the end of The annotation once you use the jpa repositories this now couples your domain objects to your database schema your core is now dependent on schema changes and if you're following the rules you know it's best to keep them in the details component and again this is where people start groaning and getting mad but it's being true all right I've got like two minutes let's see if this uh give a quick demonstrating a quick walk through some through some of this I may not have time to pull this off wish me luck all right here we go so the first thing oh I know [Music] okay and then we're going to mirror so bizarre so uh I created a little demo app which if I don't have time to walk through at all which I won't uh there's it's available on GitHub you can all go look at it later it's some truly horrendous code but architecturally it follows these patterns so you could use them as a reference and of course because this is spring and we've got uh these wonderful shoes which I forgot to wear today I will wear them tomorrow I promise I built a little shoe store app spring shoe store um it's a breakfast it's a it's a it's an example app that follows a lot of these principles that I just talked about and so as you can see and I can't zoom in here on the left but for those if those of us that can read really really tiny letters you can see Store App Store core and store details um store details has wonderful things like uh you know some dynamodb code some postgres code I can see there here I have a an order repository so if I'm the basic just to jump the gun here because I'm only got a minute the basic idea is this application you know receives inventory as shipments and it stores the the actual uh inventory objects in the in a redis queue for fun I don't know and it takes orders from customers and rights orders to a ledger inside of uh postgres um and you can see that I've encapsulated all of the actual database code for handling the orders here so I'm using jdbc template normally I'd use Juke but I didn't want to over complicate things and have too many other dependencies um you know just some simple jdb jdbc code to write to update Etc it's wonderful if I go into application you can see that the application the app itself has these config components which are basically doing all of those fun Bean annotations get shoe service get product variant service get Inventory management service Etc get order repository the uh and you can see for example here get order repository is returning an implementation of postgres order Repository um and if I it's got a bunch of tests so run these order tests for example I'm doing this live please don't break and I hope that it runs fast enough to with the Wi-Fi and everything so what's happening here oops I can't type when 100 people looking at me um so you can see in the background Docker was spinning up a bunch of containers and it should start shutting them down maybe not I'm going to keep hitting up and there it goes now they all clear down and my test passed it's wonderful um now if I wanted to because uh and I know you said you would never actually do this but it's a fun use case to see I happen to have a dynamodb order Repository in implementation of the same things that follow the that adhere to the uh standards laid out in the order repository interface which submit an order in list orders for users etc etc and so by simply swapping one repository out for another I can rerun these tests and I don't have time to get into it but proved but believe me that it does some stuff with calling out to red as in a database and adding things um so now some containers are sprung up and my test pass again and it should be that simple not that you actually do it but if you wanted to uh all right so that's all the time I've got for that particular thing so let me jump back to the slides so um now two more slides left now one of the big uh complaints that people have of following this pattern is that you know sure it it ends up adding a lot more files and a lot more mapping code I get it you know it's it's more but you need this discipline you need to follow these these rules having these dependency separation keeps your code isolated keeps it encapsulated you don't have to follow every single one of these rules but doing so leads you to um you know a a system to at least get you off the ground when you're first starting a software project it may grow and change over time but I I found that over the years adhering to these patterns is a great way to at least start an application such that it has that proper guard rail balance and if you get rid of it in the future you know that's fine but at least start with it and take some to Heart some of the recommendations it makes those that that dependency segregation will uh you know save you in the long run I promise so with that I don't have anything else and I'm way over time thank you very much for coming
Info
Channel: Spring I/O
Views: 33,710
Rating: undefined out of 5
Keywords:
Id: mbNzUkNjrnA
Channel Id: undefined
Length: 49min 53sec (2993 seconds)
Published: Tue May 23 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.