ERIC ROBERT: All right, hi. I'm Eric Roberts and I'm here to talk to you all about how you can use software design patterns to put your Rails app on a diet and make your tests run really, really fast. Jokes aside, we will be telling you about some design patterns. Not so much making your tests run fast. But we're here to talk about domain-driven design hexagonal architecture in Rails. I'm Eric Roberts. I'm a software developer and I work at a company in Waterloo, Ontario, Canada called Boltmade. I met Declan when we worked together at PrintChomp. Before that, I was a frontend developer for a number of years, and I'd worked around Rails applications, but mostly at the view layer until Declan dragged me kicking and screaming into backend development and made me care about stuff like we're talking about today. And this is the biggest crowd I have ever presented in front of. So if you'll excuse me, I need to take a picture and email my mom. DECLAN WHELAN: OK. Hi everybody. Real pleasure to be here. My name is Declan. I'm the co-founder of a company called PrintChomp, and my story is about two years ago, I had a opportunity to launch PrintChomp, and I was looking at technologies, and I decided that Ruby on Rails was really the best platform for us. The only challenge was that neither me nor anybody on my team knew Ruby nor Rails. So it was kind of a brave, maybe, decision, but it's one that I don't regret. And one of the things that drew me to the community was the fact that, or, to the platform, was the community around sharing, around the openness of, of, of sharing code and, also, a lot about, about the test-focus, which has been pretty important to me. But the cool part, I think, about it, was we intentionally took on a lot of technical debt, because I knew that, I knew that I would not know enough about our domain. It was a new domain for me, printing, and I also did not know enough about Ruby. I did not know about, enough about Rails. So, very intentionally decided to do our best, the best that we could, knowing that we would end up, very likely, with a pretty heaping mound of technical debt. And that turned out to be true. And has anyone had that experience? I don't know. Yeah. So, but recently we had an opportunity to build an API, and that was really exciting for me. And I know there are gonna be some other talks about APIs here later, and I realized that I had, I had two kind of competing things that could come together. The first was, I had, I had logic in our application that I needed to share in our API. How was I going to do that? And all of our code was sprinkled through various bits of our controller and model logic. Secondly, I wanted a mechanism to, to have a strategy for eliminating the technical debt, and what I turned to was domain-driven design hexagonal architectures, and I want to share what we've learnt along the way of doing that, and, and where we're going. So I hope you're able to learn something from what we've done. E.R.: That's great Declan. But what are they gonna get out of it? VIDEO ??: Here's a good idea. Have a point! Makes it so much more interesting for the listener! D.W.: Well, that was from Sandy Metz. He said, you know, if you're gonna have a talk, you should really have a point, and I think Steve Martin said it pretty funnily. And I guess, our, our number one point is that, there is complexity in the software that we build. There's complexities in the problems that we're solving. And we need to embrace that complexity and embrace it in such a way that we tackle it and deal with it. And deal with it head-on. And by doing that, then we end up having more joy and fun in our work because it's not just about getting this functional piece to work, it's about really trying to understand our domain and model it and express that in our code and make our code as expressive as possible. And the second thing that I really realized was I knew that our code was a mess, and I knew some point refactorings that we could do, but I didn't know how to, how to, you know, what did the end look like? If it was refactored significantly, what would it look like? What would the shape look like? What would the namespaces be? What would the classes be doing, et cetera. And domain-driven design and hexagonal architecture helped me envision and share with my team what it might look like. The third thing that, that, that we would like you to take away from this talk is that there's a lot more to just being a Rails developer, the, the ideas and patterns, if you will, that we, that we're talking about are, some of them have been written by some of those people that DHH had in his slide, including James Coplain. But a lot of those patterns have been around for a long time. SO those patterns and those ideas and those practices will serve you well beyond Rails. They would work in a node application. They could work in a desktop application. They can work in a wide variety of areas. So by getting some familiarity with these concepts, you're able to transfer those skills to things beyond Rails. And so I want to ask Eric just to walk us through kind of some of the, some of the pain that we had with our initial Rails development and see if it resonates with you. E.R.: All right. So everyone knows what this is. It's a Rails folder structure. And it's really great when you get started with Rails. You have these folders. OK. I, I logically, my things fall into these areas. Controllers, models, and views are really what we're focusing on right now. But if the responsibility of your code doesn't start with M, V or C, what do you do then? And we, and we find it kind of goes like this. You have, you have these areas of responsibility and you have something that doesn't really fit, and you don't know where it goes, so you just put it on somewhere and things get a little bigger. And continuing on, you keep doing this, things get bigger, and finally, like, the line between all of these things is, is blurred. You don't know what's what. It's hard to extract reusable parts from all of this, because the, the responsibilities are split across all these things. And you kind of end up with methods like this. This is one method. It starts on the left and ends on the right. It's about ninety lines. It's not from PrintChomp. It's from another, another project that I've worked on. And without setting, it doesn't really matter what it does. The point is it's ugly and you know, it is actually about setting prices on properties and the date ranges are available. So I have a question. We need to add sales tax to the prices. Anybody care to take on that refactoring? It's pretty horrible. But nobody, nobody sits down and says, I'm gonna write a ninety line method today that does property prices with date ranges. The initial spec was probably something a lot simpler. And because it's in that spot now, the next person who comes, comes and looks and that method and goes, oh, well OK, well if I just type this little bit more it'll, it'll do that now. And this was a little bit of a train wreck. At some point you probably, the people working on this, including myself, probably should have realized, you know, forty-five lines might have been the time to split it up. Maybe twenty. Maybe less. But this is, nonetheless, what we ended up with. Or, you end up with Rube Goldberg machines for sharpening pencils. So there's a lot of well-known patterns that can help you out with this. Has anyone read this blog post? Yeah. Anybody use any of the patterns in them? They're pretty great, right. We use a lot of them, and I'm, we're gonna tell you about some of them here. But the one thing you've, so you've extracted all of these things into small responsibilities. Your models, your controllers and your views. They're all small again. But, OK, so what? I have a bunch of little objects that all go, they all know too much about each other. They don't fit in any logical structure. They don't, how, how does this all fit together? You've made, like, an awesome first step, in that you have small little pieces that you can use. But where do you go from there? And we think that looks kind of like this. We think that your domain concepts, services, entities, should be in the middle, and everything else is outside. Your database is an extra concern, your views are an extra concern. The web, and you know, when you're designing the API, as Declan talked about a little bit, it's an extra concern. So you can really focus in on the middle of what your application actually does. I'm gonna let Declan talk a little bit more about that D.W.: Has anyone here heard of domain-driven design? Oh, quite a few people. Awesome. How many people have actually, you know, intentionally used it and, and have worked with it? So, a, a number. SO cool. And it can be quite, it can be quite, it can be quite daunting. And this book by Eric Evans is kind of what kicked it off. And this book was I think written in 2005, and it is a really, really great book, but it is actually quite difficult to read. But it's, I think it might be the only technical book I've read twice. And that's partly cause it was really good and partly cause it was rather difficult to get through some of it. Did anyone else read this book and have a similar experience? like, it's really great stuff and you kind of read and it and you go, wow, what does that mean to me? Right? And we, at the end, at the end, we'll be posting our deck, and we'll have some references to other material that, that I've actually found to be a bit more addressable or a little more consumable. But what Eric Evans talked about was really tackling complexity, and he talks about the, you know, the critical complexity is really understanding the domain. What are the business rules that take place inside our systems? If we're gonna add sales tax, what are the business rules for that sales tax? What are the, what are the rules around who can buy what? And by putting those insides and thinking of them as the domain of our system, then we're able to have our outside layers just be, if you will, relatively thin facades, which allows us to reuse that logic across APIs, across other applications. We don't have to duplicate all of those business rules. And the way that he proposes to do that is through ubiquitous language. And this is a picture of the tower of Babel and if anybody knows the story, it's where everyone in the world was speaking a different language and, I once worked on a project that was a financial transaction processing system, and when I inherited it, the guy who was proceeded me had this, was a model train aficionado, so he had the idea of model trains. So every transaction was like a car, and the payment engines were, were train tracks. And he had all of these metaphors around transaction processing and trains, and was written in Java. So when I was asked to add a new feature, fix a bug, I had to understand the business domain, and then I had to kind of understand, how did that translate into train speak, and then I had to go look at the Java code to figure it all out, right. So the coding was easy. The hard part was really trying to understand what was being asked and how did the code express that, right? And that's what Eric Evans is talking about with ubiquitous language. We want to have the language that we speak with domain experts should be readable in the code. If I'm order, if I'm a customer and I can purchase a product, there should be a class called customer. There should be a class called product. And there should be a verb in there that's somewhere that says purchase, et cetera. So that there's minimal translation between the domain experts' language and the language that my code is written in. Ruby gives us a great opportunity to do that, but this is much more difficult in, in other more statically typed languages. So that's kind of the, the key thing to take away from domain driven design is to try to have your, your concepts expressed in code that are meaningful in the words people use. If they use the word customer, you should have a customer class. If they use the word user, you should have a user class, et cetera. But beyond that, it also has some kind of key, I, I dare not say it, patterns? Now, I'm almost nervous now to say it. But let me be clear. This isn't science. It's not, right. Because these patterns were not dreamed up in academia ivory towers. These patterns that we talk about were empirically derived from people intentionally doing what we do, which is write code every day, intentionally thinking about, how does that, how does those, how do those things fit together, and what is the essence of what I'm doing? And can I extract that into words that I can use to communicate with other people? The beauty of patterns is I can talk about a value object, and if you know what a value object is and I know what a value object is, we can have a much richer conversation than, oh, I have to have an object whose state is, whose identity is defined by the state of its attributes and then, you know, we can have a much richer conversation. E.R.: You might say you ubiquitous language. D.W.: Yeah. So we're gonna touch a little bit on some of these patterns. But, but the idea is that these are not academic ivory tower concepts. These are empirically driven from people who've worked in the field. And if we're gonna, if we're gonna be successful as an organization, while I fully agree that we need to go out and write code and we need to read code, I totally think that's true, but we also have to have more effective ways of communicating knowledge, so that we're not all learning the same things from each other over and over. WE can learn more easily from each other. And that's what these patterns are. And the next kind of piece that we're gonna talk about is hexagonal architectures. And it's more than just that. I think we've alluded to a little bit. It's really the idea of, you're going to have this core domain in the middle, and in, inside your code, surrounding your outside core are gonna be some application level code that expresses the rules of your application. And I would draw it slightly differently, perhaps, but, and then we have adapters on the outside that adapt that code to web calls or database calls or SNTP, or in my case, APIs. And so that's the way that we want you to, that's the way that we are starting to approach the work at PrintChomp is thinking about it in those constructs. And now we want to jump to some specific patterns and, and show you some real code that actually, you know, brings these to life. So Eric, you want to talk about form object? E.R.: Sure. Unfortunately, I can't tell you what your domain is or what necessarily goes in the middle of that hexagon. But I can give you some ways to keep other things out of that hexagon that you don't need to be concerned with. One thing that I've been doing lately is form object. One of the really cool things, you know, if you run Rails scaffold, some model name, you get a form that you submit, creates the record, edits the record. And that's pretty great. But how do you do that when you don't have a direct one-one mapping with an ActiveModel record? So instead of instantiating an ActiveModel record, I've taken to instantiating, I'm gonna call it a form object. There's a lot of names for a lot of different things. But this is, this is what I've been calling it. So here's the actual thing that I was building. On the left hand side, you see that you select a ticket price. On the right hand side, your name, email address, and your payment details. And this is actually two ActiveRecord models in my database. The, the passengers over there on the right, you can add a passenger and keep adding it. And if you've ever worked with nested attributes, probably know it's not always that fun. So this is not using nested attributes. It's done like this. I have a class TicketForm that includes ActiveModel::Model, which is how I get nice things, like that magic initialize method of validators. And the passengers method, if I don't have any, returns me a passenger new. That's how it puts the name and email address for that first passenger. And then tickets, I get out of this by taking my passengers and mapping them into new objects. In the controller, it looks a little bit like this. So you just pass your params off into that. You get it back out and you have tickets. So instead of if ticket dot save, I do if ticket_form valid and ticketCharger charges successfully, then we've had success. And ticketCharge takes care of charging my tickets and knowing that, there was only, cause there's only one charge for all the tickets, right. You're paying all at once. No point to split that up. So that's a really useful thing that I've found to, to help when my mappings aren't just totally, I don't want to just take a record of the database, put it in, or update it. And now Declan's gonna tell you a little bit about request objects. D.W.: Yeah. In this case, has anyone used a form object or something like that? So quite a few people. Cool. Has, has anyone done, used something called a request object? Sort of? I was hoping that I invented this. So maybe I haven't. I don't know. OK. But, request object is now in, you know, think of an API as, we're trying to have a similar behavior to the form object, except we're trying to take, remember, we're trying to take complexity out of our controllers and out of our models and put them into more, to simplify our systems. So the idea with the request object is that we're gonna pull that code out and put it into the, into the controller. And now the request object is going to receive the request. And the code looks sort of like this, right. There's, I, I've done this slightly differently than Eric's, Eric's way, in terms of, of, the kind of the core of the class. The core of this class is using a gem called Verdis, which does something similar to ActiveModel::Model, except this allows you basically to have a plain-old Ruby object and, the way, and, the cool part is, that, or, I think it's cool. You can, you can declare attributes, like this. So I have actually, in my domain layer, I have something called the customer and the billing and the shipping. And if, if somebody supplies me those three things, then I can complete an order in my system. We've, there are a couple of pieces missing here, but we wanted to keep it, fit in a slide. But that's basically it. And we have the validation. And the cool part about this is that when a request comes in, we just, I just put in a before loop in the controller that has a before filter that just basically instantiates the request object using some, some reflection to figure out, you know, what is the, what is the controller and what is the action being asked, inferring the request class, instantiating it, and then just passing it what used to be the params hash. But now it's actually a rich object that I can have validations on, et cetera. So the net effect of this is that, why would I bother doing this, right? Well, now the controller, you know, the complexity around validating the request is at the boundary of my system. That doesn't need to leak into the rest of my system. It's almost like, you know, does anybody use, you know at the beginning of your methods, you want to put the guard clauses to catch the exceptions coming in at the beginning of your methods, so that the rest of your method is simpler? This is doing the same thing, except it's doing it at, at a higher level abstraction, at the API request level. And I, this has worked out really well for us. So I, I quite like that one. Yeah. E.R.: Great. Service objects are another one that we've been using. I think DHH actually had one in his presentation. I'll give you a hint, he didn't like it. He used one, it, it's something instantiated by the controller. We've been looking at controllers a little bit as just like, OK, I've received this thing, pass it off to somebody else, and then do something with the result. So we want to keep out that procedural code from our controllers, and service objects are one way you can do that using a order service to create orders, we've encapsulated all of the logic about how to create an order in this one area. So if you want to, you can use it from somewhere else, right. You don't have to hit a controller action to create an order. It's reusable and extendable. And it has Declan's magic repository object in there that we'll get to in a bit. And the controller, again, just, it's very simple. Do this thing, on success do this, on failure, do this. So those are a few of the patterns that we've been using to help us with this. So now what? What's, what's the elephant in the room? Anybody? How do you get them into Rails? Yeah. Yeah. That's the one we were thinking of. D.W.: That's, ActiveRecord. Yeah. I mean, I mean, the end, and how do you get into Rails. Yes, there are some interesting challenges around that. But I'm gonna flip you back to the architectural slide and just point out that you see what's happened here is that we're trying to view the application pieces of our solution here being really on the perimeter of our core system. And the core system composed of services that may be servicing API or application requests. We have some services that may be invariant across any call, and those would be at the very center. And we have things called entities which are on the inside. And the key part that's, that we haven't talked about, which actually I'm planning the most difficult part in this is this repository which is the bottom, which is the r in the bottom right hand corner. And it's job is to talk to the ActiveRecord model which is in the green, and create an entity object which is the blue object, and, and how does that actually work? So I'm gonna show you a bit of code, and this is, this is the first time I've shown my Ruby code in public. So please be kind. And there are probably way better ways to do this. but this is the repository that I've, I've, there are some other methods here, but I just wanted to fit on what happen, you know, show you the simple one. So this is what a save looks like. So the save takes a domain object and it converts it through something called a mapper, which maps the domain object onto a ActiveRecord object, which is called a record here. Then I, then it calls record dot save, assigns the id and returns the response. So that's pretty straightforward. All that's really happened is, and I really tried to do this and so far I've been able to, I don't want to have domain dot save. I want to have repository dot save domain, so that there's no persistence that's leaking into my domain objects. Persistence, I want to be a secondary concern to what that object is really doing. And so far, it's worked, although sometimes it's caused me some difficulty. The method messing down there is kind of cool. At least I think it's cool. And what it's doing is it's introducing a scope object, which I'll show you in a, well here's the scope object. That's OK. And it creates a scope object, and this was the trickiest code that I had to write, but what it does, what it, the end result of this code is, that allows you to chain call any of your ActiveRecord find methods of your scopes and chain them together. So you can now use, so in other words, with this logic, wherever you have like ActiveRecord dot, you know, find where id greater than 122 is activated and so on, wherever you might have a chain like that, you can still use that chain now because of this scope class with your domain objects. Except, instead of getting back your ActiveRecord object, you're getting back its domain representation. Does that make sense? yeah? OK. And then the mapper is what maps them across, right. And some cases, the mapping is like really, really simple. What's the next slide here? Oh, yeah. Yeah. There it is right there. So the mapper, because of the way vertice works and the way ActiveRecord works with attributes, you can almost just instantiate one from the other just passing the attributes back and forth. So it's actually quite easy except when it's not easy, and then what happens, what I'm doing now is just, wherever I have something that doesn't fit this model, I just subclass this mapper with a custom mapper and override those methods, more or less. And that's the part that I think there, would be more expressive ways to do that mapping and that's what I'm starting to look at now. But so far this has actually worked pretty well. And it's allowed me to completely separate the way I think about persistence from the way I think about my domain object. SO we before we, before I had this, we had, used to have an order class, and I kid you not, and I'm in, I'm a Rails noob so you can shoot me, but it had forty-eight attributes in the ActiveRecord::Model, right. That is now represented by about eight classes that separate out all the different aspects of the order, like the shipping, the billing, et cetera, et cetera. So that, but I think this mapping is, is, is, is one of the more challenging parts. And then finally, you now I've, then you quickly run into things like, oh, well what happens if I get the record. I get the record, I save it, then I save it again. I have to be able to keep track that I've saved it once already. So that I don't have multiple copies around. And this is an identity map. In fact, an identity map was built into Rails and I think it might, I don't, I don't work in Rails 4, but I think it might be taken out or, or changed slightly. But actually, on the next slide I'll show you what the identity map looks like, and this, actually, I stole from Rails and made it a bit simpler. But all it's doing is just making sure that there's a unique instance on, on a per-API request call for each entity object. So that I, I can, and it actually serves as a really cheap cache, but that's not what it's intent is. So, so that's kind of where, where we're, where I'm going now, is most of those service object, form object, request, those all kind of work well for us, and I'm not looking at what would a repository pattern look like fleshed out. And that is, by far, the most challenging piece. But I heard a question about where you put things. You can really just put them anywhere. You know. That's the thing, like, Rails just seems to be, there's a part that's, I just felt constrained. Like, Rails didn't give me any guidance on where do I put a service socket. Well, you can really put it anywhere you like. You could put it on the auto load path or you can put it where Rails might expect to see it. But it, you can just create a services directory and put your service objects there. Rails will find it. It's not hard. But Rails doesn't really kind of tell you what to do. So it doesn't make it easy to do these, to think of it, but it actually, it's not hard. And but that's where we're going with that, and we'll, we'll give you a link to it, to, I've started a Git Repo where I plan to share some of these ideas, and if you're interested in sharing ideas with us on that, we'll, we'll be happy to talk to you or, or join us on the, on the Git Repo. E.R.: Right. So what's the point, again? At the start, we had these three things. Embrace complexity. Getting, getting the solution to work is only part of the fun, right? It's, it's your first draft, as DHH said, talking about writing. You get, you do it over and over. And getting things to work, that ninety-line method I showed earlier, that worked. But does anyone want to go back and use that again? It's no, it's no fun to revisit, unless you're improving it, which, I did do eventually and, and now it's much nicer to work with. That, that's fun. At least, we think that's fun. Getting beyond the problem and getting it to a level where we actually don't mind going into our code, extending things, and changing things. And we do that by breaking them up into the smaller parts with the patterns that we've talked about. Knowing where you're going. In Alice in Wonderland, paraphrase, they said if you don't know where you're going, any road will take you there. We think it's important to know where you're going and pick a road that you think will take you there. It doesn't have to be our road. These are things that helped us, and, and we think they're good ideas. But it's not gonna solve every problem for everyone. These aren't rules. Like Declan said, they're not science. You can't just take them and throw them on and expect that your code will magically get better by going on a diet. And be more than just a Rails developer. Not that it's bad to be a Rails developer, but these things apply across, across languages and stuff. Like, don't just learn Ruby. Learn, learn beyond that. So as Declan mentioned, we have this GitHUb that we've set up, and right now it's just a readme. We don't actually have the code in their yet. We'll likely put some of the code we showed today, especially the repository bits. But we'd love to continue the discussion via issues, pull requests, whatever. That would be awesome. And, and we'd love to talk to you about some of it throughout this week as well. In fact, I'm gonna also mentioned, we have reading. You'll be able to get these slides after, so I'm just gonna gloss over them. These are a couple of the books that we've taken a look at to help us out with these things. D.W.: I think the crowd has spoken and it's time to wrap up. If you want to come up and talk, I'm happy to answer your questions. Thank you.