0:00:17.539,0:00:19.740 ERIC ROBERT: All right, hi. I'm Eric Roberts 0:00:19.740,0:00:21.429 and I'm here to talk to you all about 0:00:21.429,0:00:23.220 how you can use software design patterns 0:00:23.220,0:00:24.939 to put your Rails app on a diet 0:00:24.939,0:00:29.320 and make your tests run really, really fast. 0:00:35.700,0:00:38.320 Jokes aside, we will be telling you about[br]some 0:00:38.320,0:00:41.610 design patterns. Not so much making your tests[br]run 0:00:41.610,0:00:44.559 fast. But we're here to talk about domain-driven[br]design 0:00:44.559,0:00:46.540 hexagonal architecture in Rails. 0:00:46.540,0:00:48.790 I'm Eric Roberts. I'm a software developer[br]and I 0:00:48.790,0:00:52.509 work at a company in Waterloo, Ontario, Canada[br]called 0:00:52.509,0:00:56.570 Boltmade. I met Declan when we worked together[br]at 0:00:56.570,0:01:00.150 PrintChomp. Before that, I was a frontend[br]developer for 0:01:00.150,0:01:02.939 a number of years, and I'd worked around Rails 0:01:02.939,0:01:06.430 applications, but mostly at the view layer[br]until Declan 0:01:06.430,0:01:09.680 dragged me kicking and screaming into backend[br]development and 0:01:09.680,0:01:12.470 made me care about stuff like we're talking[br]about 0:01:12.470,0:01:13.829 today. 0:01:13.829,0:01:15.890 And this is the biggest crowd I have ever 0:01:15.890,0:01:17.930 presented in front of. So if you'll excuse[br]me, 0:01:17.930,0:01:20.420 I need to take a picture and email my 0:01:20.420,0:01:21.250 mom. 0:01:21.250,0:01:26.820 DECLAN WHELAN: OK. Hi everybody. Real pleasure[br]to be 0:01:26.820,0:01:29.320 here. My name is Declan. I'm the co-founder[br]of 0:01:29.320,0:01:33.320 a company called PrintChomp, and my story[br]is about 0:01:33.320,0:01:36.530 two years ago, I had a opportunity to launch 0:01:36.530,0:01:38.630 PrintChomp, and I was looking at technologies,[br]and I 0:01:38.630,0:01:41.030 decided that Ruby on Rails was really the[br]best 0:01:41.030,0:01:45.110 platform for us. The only challenge was that[br]neither 0:01:45.110,0:01:47.880 me nor anybody on my team knew Ruby nor 0:01:47.880,0:01:48.960 Rails. 0:01:48.960,0:01:52.170 So it was kind of a brave, maybe, decision, 0:01:52.170,0:01:54.710 but it's one that I don't regret. And one 0:01:54.710,0:01:56.590 of the things that drew me to the community 0:01:56.590,0:01:58.950 was the fact that, or, to the platform, was 0:01:58.950,0:02:05.260 the community around sharing, around the openness[br]of, of, 0:02:05.260,0:02:08.378 of sharing code and, also, a lot about, about 0:02:08.378,0:02:12.500 the test-focus, which has been pretty important[br]to me. 0:02:12.500,0:02:14.319 But the cool part, I think, about it, was 0:02:14.319,0:02:16.700 we intentionally took on a lot of technical[br]debt, 0:02:16.700,0:02:18.799 because I knew that, I knew that I would 0:02:18.799,0:02:20.439 not know enough about our domain. It was a 0:02:20.439,0:02:22.340 new domain for me, printing, and I also did 0:02:22.340,0:02:24.790 not know enough about Ruby. I did not know 0:02:24.790,0:02:28.959 about, enough about Rails. So, very intentionally[br]decided to 0:02:28.959,0:02:30.999 do our best, the best that we could, knowing 0:02:30.999,0:02:33.159 that we would end up, very likely, with a 0:02:33.159,0:02:36.249 pretty heaping mound of technical debt. 0:02:36.249,0:02:39.599 And that turned out to be true. And has 0:02:39.599,0:02:43.980 anyone had that experience? I don't know.[br]Yeah. So, 0:02:43.980,0:02:45.629 but recently we had an opportunity to build[br]an 0:02:45.629,0:02:47.849 API, and that was really exciting for me.[br]And 0:02:47.849,0:02:48.900 I know there are gonna be some other talks 0:02:48.900,0:02:51.730 about APIs here later, and I realized that[br]I 0:02:51.730,0:02:54.870 had, I had two kind of competing things that 0:02:54.870,0:02:57.480 could come together. The first was, I had,[br]I 0:02:57.480,0:03:00.319 had logic in our application that I needed[br]to 0:03:00.319,0:03:02.340 share in our API. How was I going to 0:03:02.340,0:03:03.779 do that? 0:03:03.779,0:03:07.489 And all of our code was sprinkled through[br]various 0:03:07.489,0:03:10.559 bits of our controller and model logic. Secondly,[br]I 0:03:10.559,0:03:13.430 wanted a mechanism to, to have a strategy[br]for 0:03:13.430,0:03:17.799 eliminating the technical debt, and what I[br]turned to 0:03:17.799,0:03:21.510 was domain-driven design hexagonal architectures,[br]and I want to 0:03:21.510,0:03:24.069 share what we've learnt along the way of doing 0:03:24.069,0:03:26.069 that, and, and where we're going. So I hope 0:03:26.069,0:03:29.900 you're able to learn something from what we've[br]done. 0:03:29.900,0:03:32.689 E.R.: That's great Declan. But what are they[br]gonna 0:03:32.689,0:03:33.629 get out of it? 0:03:33.629,0:03:34.069 VIDEO 0:03:34.069,0:03:37.370 ??: Here's a good idea. Have a point! Makes 0:03:37.370,0:03:40.769 it so much more interesting for the listener! 0:03:40.769,0:03:43.659 D.W.: Well, that was from Sandy Metz. He said, 0:03:43.659,0:03:45.109 you know, if you're gonna have a talk, you 0:03:45.109,0:03:46.779 should really have a point, and I think Steve 0:03:46.779,0:03:50.079 Martin said it pretty funnily. And I guess,[br]our, 0:03:50.079,0:03:52.659 our number one point is that, there is complexity 0:03:52.659,0:03:55.760 in the software that we build. There's complexities[br]in 0:03:55.760,0:03:57.400 the problems that we're solving. And we need[br]to 0:03:57.400,0:04:00.290 embrace that complexity and embrace it in[br]such a 0:04:00.290,0:04:02.349 way that we tackle it and deal with it. 0:04:02.349,0:04:05.599 And deal with it head-on. And by doing that, 0:04:05.599,0:04:07.730 then we end up having more joy and fun 0:04:07.730,0:04:09.599 in our work because it's not just about getting 0:04:09.599,0:04:11.969 this functional piece to work, it's about[br]really trying 0:04:11.969,0:04:14.809 to understand our domain and model it and[br]express 0:04:14.809,0:04:16.430 that in our code and make our code as 0:04:16.430,0:04:18.639 expressive as possible. 0:04:18.639,0:04:21.829 And the second thing that I really realized[br]was 0:04:21.829,0:04:23.690 I knew that our code was a mess, and 0:04:23.690,0:04:26.590 I knew some point refactorings that we could[br]do, 0:04:26.590,0:04:28.860 but I didn't know how to, how to, you 0:04:28.860,0:04:30.690 know, what did the end look like? If it 0:04:30.690,0:04:33.780 was refactored significantly, what would it[br]look like? What 0:04:33.780,0:04:36.840 would the shape look like? What would the[br]namespaces 0:04:36.840,0:04:39.420 be? What would the classes be doing, et cetera. 0:04:39.420,0:04:43.380 And domain-driven design and hexagonal architecture[br]helped me envision 0:04:43.380,0:04:45.100 and share with my team what it might look 0:04:45.100,0:04:45.970 like. 0:04:45.970,0:04:49.870 The third thing that, that, that we would[br]like 0:04:49.870,0:04:52.290 you to take away from this talk is that 0:04:52.290,0:04:55.180 there's a lot more to just being a Rails 0:04:55.180,0:04:58.700 developer, the, the ideas and patterns, if[br]you will, 0:04:58.700,0:05:01.340 that we, that we're talking about are, some[br]of 0:05:01.340,0:05:03.340 them have been written by some of those people 0:05:03.340,0:05:08.120 that DHH had in his slide, including James[br]Coplain. 0:05:08.120,0:05:09.950 But a lot of those patterns have been around 0:05:09.950,0:05:12.250 for a long time. SO those patterns and those 0:05:12.250,0:05:15.820 ideas and those practices will serve you well[br]beyond 0:05:15.820,0:05:18.650 Rails. They would work in a node application.[br]They 0:05:18.650,0:05:20.680 could work in a desktop application. They[br]can work 0:05:20.680,0:05:23.260 in a wide variety of areas. So by getting 0:05:23.260,0:05:26.400 some familiarity with these concepts, you're[br]able to transfer 0:05:26.400,0:05:30.090 those skills to things beyond Rails. 0:05:30.090,0:05:33.180 And so I want to ask Eric just to 0:05:33.180,0:05:34.810 walk us through kind of some of the, some 0:05:34.810,0:05:36.720 of the pain that we had with our initial 0:05:36.720,0:05:39.240 Rails development and see if it resonates[br]with you. 0:05:39.240,0:05:43.580 E.R.: All right. So everyone knows what this[br]is. 0:05:43.580,0:05:47.550 It's a Rails folder structure. And it's really[br]great 0:05:47.550,0:05:49.470 when you get started with Rails. You have[br]these 0:05:49.470,0:05:53.460 folders. OK. I, I logically, my things fall[br]into 0:05:53.460,0:05:56.300 these areas. Controllers, models, and views[br]are really what 0:05:56.300,0:05:59.240 we're focusing on right now. 0:05:59.240,0:06:01.110 But if the responsibility of your code doesn't[br]start 0:06:01.110,0:06:05.110 with M, V or C, what do you do 0:06:05.110,0:06:07.610 then? And we, and we find it kind of 0:06:07.610,0:06:10.050 goes like this. You have, you have these areas 0:06:10.050,0:06:12.970 of responsibility and you have something that[br]doesn't really 0:06:12.970,0:06:14.560 fit, and you don't know where it goes, so 0:06:14.560,0:06:16.210 you just put it on somewhere and things get 0:06:16.210,0:06:18.170 a little bigger. 0:06:18.170,0:06:20.060 And continuing on, you keep doing this, things[br]get 0:06:20.060,0:06:23.520 bigger, and finally, like, the line between[br]all of 0:06:23.520,0:06:26.150 these things is, is blurred. You don't know[br]what's 0:06:26.150,0:06:29.500 what. It's hard to extract reusable parts[br]from all 0:06:29.500,0:06:31.810 of this, because the, the responsibilities[br]are split across 0:06:31.810,0:06:32.390 all these things. 0:06:32.390,0:06:34.310 And you kind of end up with methods like 0:06:34.310,0:06:37.720 this. This is one method. It starts on the 0:06:37.720,0:06:40.360 left and ends on the right. It's about ninety 0:06:40.360,0:06:43.370 lines. 0:06:43.370,0:06:46.200 It's not from PrintChomp. It's from another,[br]another project 0:06:46.200,0:06:49.190 that I've worked on. And without setting,[br]it doesn't 0:06:49.190,0:06:51.210 really matter what it does. The point is it's 0:06:51.210,0:06:55.330 ugly and you know, it is actually about setting 0:06:55.330,0:06:59.750 prices on properties and the date ranges are[br]available. 0:06:59.750,0:07:03.060 So I have a question. We need to add 0:07:03.060,0:07:05.140 sales tax to the prices. 0:07:05.140,0:07:09.970 Anybody care to take on that refactoring? 0:07:09.970,0:07:12.410 It's pretty horrible. But nobody, nobody sits[br]down and 0:07:12.410,0:07:14.270 says, I'm gonna write a ninety line method[br]today 0:07:14.270,0:07:19.590 that does property prices with date ranges.[br]The initial 0:07:19.590,0:07:23.330 spec was probably something a lot simpler.[br]And because 0:07:23.330,0:07:25.639 it's in that spot now, the next person who 0:07:25.639,0:07:27.170 comes, comes and looks and that method and[br]goes, 0:07:27.170,0:07:28.500 oh, well OK, well if I just type this 0:07:28.500,0:07:31.710 little bit more it'll, it'll do that now. 0:07:31.710,0:07:33.870 And this was a little bit of a train 0:07:33.870,0:07:36.570 wreck. At some point you probably, the people[br]working 0:07:36.570,0:07:39.300 on this, including myself, probably should[br]have realized, you 0:07:39.300,0:07:41.330 know, forty-five lines might have been the[br]time to 0:07:41.330,0:07:45.790 split it up. Maybe twenty. Maybe less. 0:07:45.790,0:07:47.889 But this is, nonetheless, what we ended up[br]with. 0:07:47.889,0:07:51.120 Or, you end up with Rube Goldberg machines[br]for 0:07:51.120,0:07:53.260 sharpening pencils. 0:07:53.260,0:07:56.210 So there's a lot of well-known patterns that[br]can 0:07:56.210,0:07:58.480 help you out with this. Has anyone read this 0:07:58.480,0:08:03.210 blog post? Yeah. Anybody use any of the patterns 0:08:03.210,0:08:04.150 in them? 0:08:04.150,0:08:07.430 They're pretty great, right. We use a lot[br]of 0:08:07.430,0:08:09.450 them, and I'm, we're gonna tell you about[br]some 0:08:09.450,0:08:13.230 of them here. But the one thing you've, so 0:08:13.230,0:08:16.300 you've extracted all of these things into[br]small responsibilities. 0:08:16.300,0:08:18.230 Your models, your controllers and your views.[br]They're all 0:08:18.230,0:08:19.250 small again. 0:08:19.250,0:08:22.310 But, OK, so what? I have a bunch of 0:08:22.310,0:08:24.889 little objects that all go, they all know[br]too 0:08:24.889,0:08:27.780 much about each other. They don't fit in any 0:08:27.780,0:08:31.720 logical structure. They don't, how, how does[br]this all 0:08:31.720,0:08:35.019 fit together? You've made, like, an awesome[br]first step, 0:08:35.019,0:08:36.940 in that you have small little pieces that[br]you 0:08:36.940,0:08:40.250 can use. But where do you go from there? 0:08:40.250,0:08:44.360 And we think that looks kind of like this. 0:08:44.360,0:08:49.500 We think that your domain concepts, services,[br]entities, should 0:08:49.500,0:08:53.170 be in the middle, and everything else is outside. 0:08:53.170,0:08:56.670 Your database is an extra concern, your views[br]are 0:08:56.670,0:08:58.889 an extra concern. The web, and you know, when 0:08:58.889,0:09:01.499 you're designing the API, as Declan talked[br]about a 0:09:01.499,0:09:03.309 little bit, it's an extra concern. So you[br]can 0:09:03.309,0:09:05.550 really focus in on the middle of what your 0:09:05.550,0:09:06.550 application actually does. 0:09:06.550,0:09:09.339 I'm gonna let Declan talk a little bit more 0:09:09.339,0:09:09.959 about that 0:09:09.959,0:09:13.839 D.W.: Has anyone here heard of domain-driven[br]design? Oh, 0:09:13.839,0:09:16.009 quite a few people. Awesome. How many people[br]have 0:09:16.009,0:09:19.889 actually, you know, intentionally used it[br]and, and have 0:09:19.889,0:09:22.529 worked with it? So, a, a number. SO cool. 0:09:22.529,0:09:25.680 And it can be quite, it can be quite, 0:09:25.680,0:09:29.910 it can be quite daunting. And this book by 0:09:29.910,0:09:31.850 Eric Evans is kind of what kicked it off. 0:09:31.850,0:09:34.100 And this book was I think written in 2005, 0:09:34.100,0:09:36.499 and it is a really, really great book, but 0:09:36.499,0:09:39.269 it is actually quite difficult to read. But[br]it's, 0:09:39.269,0:09:41.319 I think it might be the only technical book 0:09:41.319,0:09:43.040 I've read twice. 0:09:43.040,0:09:44.449 And that's partly cause it was really good[br]and 0:09:44.449,0:09:47.600 partly cause it was rather difficult to get[br]through 0:09:47.600,0:09:49.259 some of it. Did anyone else read this book 0:09:49.259,0:09:51.709 and have a similar experience? like, it's[br]really great 0:09:51.709,0:09:53.249 stuff and you kind of read and it and 0:09:53.249,0:09:55.100 you go, wow, what does that mean to me? 0:09:55.100,0:09:55.480 Right? 0:09:55.480,0:09:57.920 And we, at the end, at the end, we'll 0:09:57.920,0:10:00.050 be posting our deck, and we'll have some references 0:10:00.050,0:10:02.230 to other material that, that I've actually[br]found to 0:10:02.230,0:10:05.170 be a bit more addressable or a little more 0:10:05.170,0:10:07.350 consumable. 0:10:07.350,0:10:10.550 But what Eric Evans talked about was really[br]tackling 0:10:10.550,0:10:13.490 complexity, and he talks about the, you know,[br]the 0:10:13.490,0:10:18.339 critical complexity is really understanding[br]the domain. What are 0:10:18.339,0:10:22.319 the business rules that take place inside[br]our systems? 0:10:22.319,0:10:23.680 If we're gonna add sales tax, what are the 0:10:23.680,0:10:25.990 business rules for that sales tax? 0:10:25.990,0:10:27.899 What are the, what are the rules around who 0:10:27.899,0:10:30.740 can buy what? And by putting those insides[br]and 0:10:30.740,0:10:33.540 thinking of them as the domain of our system, 0:10:33.540,0:10:36.339 then we're able to have our outside layers[br]just 0:10:36.339,0:10:42.649 be, if you will, relatively thin facades,[br]which allows 0:10:42.649,0:10:45.779 us to reuse that logic across APIs, across[br]other 0:10:45.779,0:10:48.529 applications. We don't have to duplicate all[br]of those 0:10:48.529,0:10:50.509 business rules. 0:10:50.509,0:10:52.619 And the way that he proposes to do that 0:10:52.619,0:10:56.480 is through ubiquitous language. And this is[br]a picture 0:10:56.480,0:10:58.920 of the tower of Babel and if anybody knows 0:10:58.920,0:11:01.819 the story, it's where everyone in the world[br]was 0:11:01.819,0:11:04.850 speaking a different language and, I once[br]worked on 0:11:04.850,0:11:09.240 a project that was a financial transaction[br]processing system, 0:11:09.240,0:11:12.139 and when I inherited it, the guy who was 0:11:12.139,0:11:15.579 proceeded me had this, was a model train aficionado, 0:11:15.579,0:11:18.559 so he had the idea of model trains. So 0:11:18.559,0:11:22.199 every transaction was like a car, and the[br]payment 0:11:22.199,0:11:25.689 engines were, were train tracks. And he had[br]all 0:11:25.689,0:11:28.829 of these metaphors around transaction processing[br]and trains, and 0:11:28.829,0:11:30.369 was written in Java. 0:11:30.369,0:11:31.910 So when I was asked to add a new 0:11:31.910,0:11:33.970 feature, fix a bug, I had to understand the 0:11:33.970,0:11:35.879 business domain, and then I had to kind of 0:11:35.879,0:11:39.059 understand, how did that translate into train[br]speak, and 0:11:39.059,0:11:40.360 then I had to go look at the Java 0:11:40.360,0:11:43.369 code to figure it all out, right. So the 0:11:43.369,0:11:46.240 coding was easy. The hard part was really[br]trying 0:11:46.240,0:11:48.350 to understand what was being asked and how[br]did 0:11:48.350,0:11:50.589 the code express that, right? 0:11:50.589,0:11:52.149 And that's what Eric Evans is talking about[br]with 0:11:52.149,0:11:55.209 ubiquitous language. We want to have the language[br]that 0:11:55.209,0:11:58.670 we speak with domain experts should be readable[br]in 0:11:58.670,0:12:01.459 the code. If I'm order, if I'm a customer 0:12:01.459,0:12:04.300 and I can purchase a product, there should[br]be 0:12:04.300,0:12:06.259 a class called customer. There should be a[br]class 0:12:06.259,0:12:08.319 called product. And there should be a verb[br]in 0:12:08.319,0:12:11.119 there that's somewhere that says purchase,[br]et cetera. 0:12:11.119,0:12:14.019 So that there's minimal translation between[br]the domain experts' 0:12:14.019,0:12:16.769 language and the language that my code is[br]written 0:12:16.769,0:12:18.959 in. Ruby gives us a great opportunity to do 0:12:18.959,0:12:22.369 that, but this is much more difficult in,[br]in 0:12:22.369,0:12:26.679 other more statically typed languages. 0:12:26.679,0:12:28.189 So that's kind of the, the key thing to 0:12:28.189,0:12:30.999 take away from domain driven design is to[br]try 0:12:30.999,0:12:34.230 to have your, your concepts expressed in code[br]that 0:12:34.230,0:12:37.230 are meaningful in the words people use. 0:12:37.230,0:12:38.449 If they use the word customer, you should[br]have 0:12:38.449,0:12:40.540 a customer class. If they use the word user, 0:12:40.540,0:12:44.199 you should have a user class, et cetera. 0:12:44.199,0:12:46.170 But beyond that, it also has some kind of 0:12:46.170,0:12:50.339 key, I, I dare not say it, patterns? Now, 0:12:50.339,0:12:53.929 I'm almost nervous now to say it. 0:12:53.929,0:13:00.929 But let me be clear. This isn't science. It's 0:13:01.970,0:13:05.819 not, right. Because these patterns were not[br]dreamed up 0:13:05.819,0:13:10.519 in academia ivory towers. These patterns that[br]we talk 0:13:10.519,0:13:15.170 about were empirically derived from people[br]intentionally doing what 0:13:15.170,0:13:17.899 we do, which is write code every day, intentionally 0:13:17.899,0:13:20.279 thinking about, how does that, how does those,[br]how 0:13:20.279,0:13:22.119 do those things fit together, and what is[br]the 0:13:22.119,0:13:23.920 essence of what I'm doing? 0:13:23.920,0:13:25.999 And can I extract that into words that I 0:13:25.999,0:13:28.809 can use to communicate with other people?[br]The beauty 0:13:28.809,0:13:30.569 of patterns is I can talk about a value 0:13:30.569,0:13:33.339 object, and if you know what a value object 0:13:33.339,0:13:35.529 is and I know what a value object is, 0:13:35.529,0:13:38.119 we can have a much richer conversation than,[br]oh, 0:13:38.119,0:13:40.079 I have to have an object whose state is, 0:13:40.079,0:13:42.119 whose identity is defined by the state of[br]its 0:13:42.119,0:13:44.139 attributes and then, you know, we can have[br]a 0:13:44.139,0:13:45.420 much richer conversation. 0:13:45.420,0:13:48.160 E.R.: You might say you ubiquitous language. 0:13:48.160,0:13:50.420 D.W.: Yeah. So we're gonna touch a little[br]bit 0:13:50.420,0:13:53.209 on some of these patterns. But, but the idea 0:13:53.209,0:13:59.230 is that these are not academic ivory tower[br]concepts. 0:13:59.230,0:14:01.420 These are empirically driven from people who've[br]worked in 0:14:01.420,0:14:03.429 the field. And if we're gonna, if we're gonna 0:14:03.429,0:14:06.329 be successful as an organization, while I[br]fully agree 0:14:06.329,0:14:08.019 that we need to go out and write code 0:14:08.019,0:14:09.920 and we need to read code, I totally think 0:14:09.920,0:14:12.269 that's true, but we also have to have more 0:14:12.269,0:14:15.509 effective ways of communicating knowledge,[br]so that we're not 0:14:15.509,0:14:17.550 all learning the same things from each other[br]over 0:14:17.550,0:14:19.300 and over. WE can learn more easily from each 0:14:19.300,0:14:22.699 other. And that's what these patterns are. 0:14:22.699,0:14:27.100 And the next kind of piece that we're gonna 0:14:27.100,0:14:29.689 talk about is hexagonal architectures. And[br]it's more than 0:14:29.689,0:14:33.449 just that. I think we've alluded to a little 0:14:33.449,0:14:36.239 bit. It's really the idea of, you're going[br]to 0:14:36.239,0:14:42.399 have this core domain in the middle, and in, 0:14:42.399,0:14:45.119 inside your code, surrounding your outside[br]core are gonna 0:14:45.119,0:14:49.319 be some application level code that expresses[br]the rules 0:14:49.319,0:14:51.480 of your application. And I would draw it slightly 0:14:51.480,0:14:53.949 differently, perhaps, but, and then we have[br]adapters on 0:14:53.949,0:14:56.910 the outside that adapt that code to web calls 0:14:56.910,0:15:01.119 or database calls or SNTP, or in my case, 0:15:01.119,0:15:01.629 APIs. 0:15:01.629,0:15:03.999 And so that's the way that we want you 0:15:03.999,0:15:05.639 to, that's the way that we are starting to 0:15:05.639,0:15:08.129 approach the work at PrintChomp is thinking[br]about it 0:15:08.129,0:15:10.550 in those constructs. And now we want to jump 0:15:10.550,0:15:13.860 to some specific patterns and, and show you[br]some 0:15:13.860,0:15:16.350 real code that actually, you know, brings[br]these to 0:15:16.350,0:15:19.660 life. So Eric, you want to talk about form 0:15:19.660,0:15:19.970 object? 0:15:19.970,0:15:21.769 E.R.: Sure. Unfortunately, I can't tell you[br]what your 0:15:21.769,0:15:24.139 domain is or what necessarily goes in the[br]middle 0:15:24.139,0:15:27.059 of that hexagon. But I can give you some 0:15:27.059,0:15:29.879 ways to keep other things out of that hexagon 0:15:29.879,0:15:32.269 that you don't need to be concerned with. 0:15:32.269,0:15:34.239 One thing that I've been doing lately is form 0:15:34.239,0:15:37.389 object. One of the really cool things, you[br]know, 0:15:37.389,0:15:40.350 if you run Rails scaffold, some model name,[br]you 0:15:40.350,0:15:43.069 get a form that you submit, creates the record, 0:15:43.069,0:15:47.209 edits the record. And that's pretty great. 0:15:47.209,0:15:49.110 But how do you do that when you don't 0:15:49.110,0:15:53.220 have a direct one-one mapping with an ActiveModel[br]record? 0:15:53.220,0:15:56.829 So instead of instantiating an ActiveModel[br]record, I've taken 0:15:56.829,0:15:59.959 to instantiating, I'm gonna call it a form[br]object. 0:15:59.959,0:16:01.600 There's a lot of names for a lot of 0:16:01.600,0:16:03.410 different things. But this is, this is what[br]I've 0:16:03.410,0:16:04.549 been calling it. 0:16:04.549,0:16:08.009 So here's the actual thing that I was building. 0:16:08.009,0:16:10.049 On the left hand side, you see that you 0:16:10.049,0:16:11.999 select a ticket price. On the right hand side, 0:16:11.999,0:16:15.230 your name, email address, and your payment[br]details. And 0:16:15.230,0:16:20.059 this is actually two ActiveRecord models in[br]my database. 0:16:20.059,0:16:22.939 The, the passengers over there on the right,[br]you 0:16:22.939,0:16:25.459 can add a passenger and keep adding it. 0:16:25.459,0:16:27.160 And if you've ever worked with nested attributes,[br]probably 0:16:27.160,0:16:31.139 know it's not always that fun. So this is 0:16:31.139,0:16:35.389 not using nested attributes. It's done like[br]this. I 0:16:35.389,0:16:39.239 have a class TicketForm that includes ActiveModel::Model,[br]which is 0:16:39.239,0:16:41.910 how I get nice things, like that magic initialize 0:16:41.910,0:16:45.699 method of validators. 0:16:45.699,0:16:47.839 And the passengers method, if I don't have[br]any, 0:16:47.839,0:16:50.110 returns me a passenger new. That's how it[br]puts 0:16:50.110,0:16:53.170 the name and email address for that first[br]passenger. 0:16:53.170,0:16:54.759 And then tickets, I get out of this by 0:16:54.759,0:16:59.379 taking my passengers and mapping them into[br]new objects. 0:16:59.379,0:17:01.399 In the controller, it looks a little bit like 0:17:01.399,0:17:04.819 this. So you just pass your params off into 0:17:04.819,0:17:06.750 that. You get it back out and you have 0:17:06.750,0:17:10.890 tickets. So instead of if ticket dot save,[br]I 0:17:10.890,0:17:16.099 do if ticket_form valid and ticketCharger[br]charges successfully, then 0:17:16.099,0:17:18.390 we've had success. And ticketCharge takes[br]care of charging 0:17:18.390,0:17:20.709 my tickets and knowing that, there was only,[br]cause 0:17:20.709,0:17:22.819 there's only one charge for all the tickets,[br]right. 0:17:22.819,0:17:24.930 You're paying all at once. No point to split 0:17:24.930,0:17:26.450 that up. 0:17:26.450,0:17:28.700 So that's a really useful thing that I've[br]found 0:17:28.700,0:17:31.790 to, to help when my mappings aren't just totally, 0:17:31.790,0:17:33.250 I don't want to just take a record of 0:17:33.250,0:17:35.990 the database, put it in, or update it. 0:17:35.990,0:17:37.360 And now Declan's gonna tell you a little bit 0:17:37.360,0:17:38.340 about request objects. 0:17:38.340,0:17:40.300 D.W.: Yeah. In this case, has anyone used[br]a 0:17:40.300,0:17:42.450 form object or something like that? So quite[br]a 0:17:42.450,0:17:43.650 few people. Cool. 0:17:43.650,0:17:46.500 Has, has anyone done, used something called[br]a request 0:17:46.500,0:17:50.760 object? Sort of? I was hoping that I invented 0:17:50.760,0:17:53.490 this. So maybe I haven't. I don't know. OK. 0:17:53.490,0:17:56.320 But, request object is now in, you know, think 0:17:56.320,0:17:58.320 of an API as, we're trying to have a 0:17:58.320,0:18:02.730 similar behavior to the form object, except[br]we're trying 0:18:02.730,0:18:05.170 to take, remember, we're trying to take complexity[br]out 0:18:05.170,0:18:06.770 of our controllers and out of our models and 0:18:06.770,0:18:10.780 put them into more, to simplify our systems. 0:18:10.780,0:18:13.870 So the idea with the request object is that 0:18:13.870,0:18:15.240 we're gonna pull that code out and put it 0:18:15.240,0:18:18.510 into the, into the controller. And now the[br]request 0:18:18.510,0:18:21.210 object is going to receive the request. And[br]the 0:18:21.210,0:18:23.150 code looks sort of like this, right. 0:18:23.150,0:18:26.520 There's, I, I've done this slightly differently[br]than Eric's, 0:18:26.520,0:18:28.500 Eric's way, in terms of, of, the kind of 0:18:28.500,0:18:29.880 the core of the class. The core of this 0:18:29.880,0:18:33.310 class is using a gem called Verdis, which[br]does 0:18:33.310,0:18:38.800 something similar to ActiveModel::Model, except[br]this allows you basically 0:18:38.800,0:18:41.950 to have a plain-old Ruby object and, the way, 0:18:41.950,0:18:44.720 and, the cool part is, that, or, I think 0:18:44.720,0:18:47.620 it's cool. You can, you can declare attributes,[br]like 0:18:47.620,0:18:49.900 this. So I have actually, in my domain layer, 0:18:49.900,0:18:51.710 I have something called the customer and the[br]billing 0:18:51.710,0:18:54.230 and the shipping. And if, if somebody supplies[br]me 0:18:54.230,0:18:56.990 those three things, then I can complete an[br]order 0:18:56.990,0:18:58.790 in my system. 0:18:58.790,0:19:01.810 We've, there are a couple of pieces missing[br]here, 0:19:01.810,0:19:03.980 but we wanted to keep it, fit in a 0:19:03.980,0:19:07.230 slide. But that's basically it. And we have[br]the 0:19:07.230,0:19:10.580 validation. And the cool part about this is[br]that 0:19:10.580,0:19:12.440 when a request comes in, we just, I just 0:19:12.440,0:19:15.430 put in a before loop in the controller that 0:19:15.430,0:19:18.380 has a before filter that just basically instantiates[br]the 0:19:18.380,0:19:23.740 request object using some, some reflection[br]to figure out, 0:19:23.740,0:19:25.920 you know, what is the, what is the controller 0:19:25.920,0:19:29.660 and what is the action being asked, inferring[br]the 0:19:29.660,0:19:32.870 request class, instantiating it, and then[br]just passing it 0:19:32.870,0:19:34.900 what used to be the params hash. 0:19:34.900,0:19:37.140 But now it's actually a rich object that I 0:19:37.140,0:19:39.740 can have validations on, et cetera. So the[br]net 0:19:39.740,0:19:42.490 effect of this is that, why would I bother 0:19:42.490,0:19:45.520 doing this, right? Well, now the controller,[br]you know, 0:19:45.520,0:19:48.100 the complexity around validating the request[br]is at the 0:19:48.100,0:19:50.490 boundary of my system. That doesn't need to[br]leak 0:19:50.490,0:19:55.640 into the rest of my system. It's almost like, 0:19:55.640,0:19:57.150 you know, does anybody use, you know at the 0:19:57.150,0:19:58.680 beginning of your methods, you want to put[br]the 0:19:58.680,0:20:01.100 guard clauses to catch the exceptions coming[br]in at 0:20:01.100,0:20:02.440 the beginning of your methods, so that the[br]rest 0:20:02.440,0:20:03.790 of your method is simpler? 0:20:03.790,0:20:05.690 This is doing the same thing, except it's[br]doing 0:20:05.690,0:20:08.050 it at, at a higher level abstraction, at the 0:20:08.050,0:20:10.110 API request level. And I, this has worked[br]out 0:20:10.110,0:20:11.950 really well for us. So I, I quite like 0:20:11.950,0:20:13.820 that one. 0:20:13.820,0:20:14.750 Yeah. 0:20:14.750,0:20:18.230 E.R.: Great. Service objects are another one[br]that we've 0:20:18.230,0:20:21.410 been using. I think DHH actually had one in 0:20:21.410,0:20:23.420 his presentation. I'll give you a hint, he[br]didn't 0:20:23.420,0:20:27.300 like it. 0:20:27.300,0:20:29.900 He used one, it, it's something instantiated[br]by the 0:20:29.900,0:20:32.670 controller. We've been looking at controllers[br]a little bit 0:20:32.670,0:20:35.820 as just like, OK, I've received this thing,[br]pass 0:20:35.820,0:20:37.440 it off to somebody else, and then do something 0:20:37.440,0:20:40.080 with the result. So we want to keep out 0:20:40.080,0:20:42.770 that procedural code from our controllers,[br]and service objects 0:20:42.770,0:20:46.020 are one way you can do that using a 0:20:46.020,0:20:49.370 order service to create orders, we've encapsulated[br]all of 0:20:49.370,0:20:53.700 the logic about how to create an order in 0:20:53.700,0:20:54.270 this one area. 0:20:54.270,0:20:56.070 So if you want to, you can use it 0:20:56.070,0:20:57.760 from somewhere else, right. You don't have[br]to hit 0:20:57.760,0:21:01.760 a controller action to create an order. It's[br]reusable 0:21:01.760,0:21:06.870 and extendable. And it has Declan's magic[br]repository object 0:21:06.870,0:21:10.440 in there that we'll get to in a bit. 0:21:10.440,0:21:14.270 And the controller, again, just, it's very[br]simple. Do 0:21:14.270,0:21:17.900 this thing, on success do this, on failure,[br]do 0:21:17.900,0:21:18.310 this. 0:21:18.310,0:21:20.620 So those are a few of the patterns that 0:21:20.620,0:21:25.920 we've been using to help us with this. So 0:21:25.920,0:21:27.900 now what? 0:21:27.900,0:21:34.550 What's, what's the elephant in the room? Anybody? 0:21:34.550,0:21:41.550 How do you get them into Rails? Yeah. 0:21:45.430,0:21:47.980 Yeah. That's the one we were thinking of. 0:21:47.980,0:21:50.520 D.W.: That's, ActiveRecord. Yeah. I mean,[br]I mean, the 0:21:50.520,0:21:52.960 end, and how do you get into Rails. Yes, 0:21:52.960,0:21:55.890 there are some interesting challenges around[br]that. But I'm 0:21:55.890,0:21:57.900 gonna flip you back to the architectural slide[br]and 0:21:57.900,0:22:00.750 just point out that you see what's happened[br]here 0:22:00.750,0:22:04.190 is that we're trying to view the application[br]pieces 0:22:04.190,0:22:07.190 of our solution here being really on the perimeter 0:22:07.190,0:22:09.740 of our core system. And the core system composed 0:22:09.740,0:22:13.980 of services that may be servicing API or application 0:22:13.980,0:22:16.210 requests. 0:22:16.210,0:22:19.260 We have some services that may be invariant[br]across 0:22:19.260,0:22:21.460 any call, and those would be at the very 0:22:21.460,0:22:23.620 center. And we have things called entities[br]which are 0:22:23.620,0:22:26.440 on the inside. And the key part that's, that 0:22:26.440,0:22:28.760 we haven't talked about, which actually I'm[br]planning the 0:22:28.760,0:22:32.250 most difficult part in this is this repository[br]which 0:22:32.250,0:22:33.800 is the bottom, which is the r in the 0:22:33.800,0:22:35.570 bottom right hand corner. 0:22:35.570,0:22:37.870 And it's job is to talk to the ActiveRecord 0:22:37.870,0:22:39.730 model which is in the green, and create an 0:22:39.730,0:22:42.280 entity object which is the blue object, and,[br]and 0:22:42.280,0:22:45.480 how does that actually work? So I'm gonna[br]show 0:22:45.480,0:22:48.500 you a bit of code, and this is, this 0:22:48.500,0:22:50.860 is the first time I've shown my Ruby code 0:22:50.860,0:22:52.420 in public. So please be kind. 0:22:52.420,0:22:55.290 And there are probably way better ways to[br]do 0:22:55.290,0:22:58.650 this. but this is the repository that I've,[br]I've, 0:22:58.650,0:22:59.710 there are some other methods here, but I just 0:22:59.710,0:23:01.980 wanted to fit on what happen, you know, show 0:23:01.980,0:23:03.810 you the simple one. 0:23:03.810,0:23:05.690 So this is what a save looks like. So 0:23:05.690,0:23:08.830 the save takes a domain object and it converts 0:23:08.830,0:23:11.140 it through something called a mapper, which[br]maps the 0:23:11.140,0:23:15.440 domain object onto a ActiveRecord object,[br]which is called 0:23:15.440,0:23:18.610 a record here. Then I, then it calls record 0:23:18.610,0:23:22.400 dot save, assigns the id and returns the response. 0:23:22.400,0:23:25.060 So that's pretty straightforward. All that's[br]really happened is, 0:23:25.060,0:23:26.390 and I really tried to do this and so 0:23:26.390,0:23:28.290 far I've been able to, I don't want to 0:23:28.290,0:23:31.010 have domain dot save. I want to have repository 0:23:31.010,0:23:34.400 dot save domain, so that there's no persistence[br]that's 0:23:34.400,0:23:38.250 leaking into my domain objects. Persistence,[br]I want to 0:23:38.250,0:23:40.830 be a secondary concern to what that object[br]is 0:23:40.830,0:23:41.520 really doing. 0:23:41.520,0:23:44.140 And so far, it's worked, although sometimes[br]it's caused 0:23:44.140,0:23:48.040 me some difficulty. The method messing down[br]there is 0:23:48.040,0:23:51.710 kind of cool. At least I think it's cool. 0:23:51.710,0:23:54.500 And what it's doing is it's introducing a[br]scope 0:23:54.500,0:23:55.970 object, which I'll show you in a, well here's 0:23:55.970,0:23:57.900 the scope object. That's OK. 0:23:57.900,0:23:59.650 And it creates a scope object, and this was 0:23:59.650,0:24:01.630 the trickiest code that I had to write, but 0:24:01.630,0:24:03.900 what it does, what it, the end result of 0:24:03.900,0:24:06.970 this code is, that allows you to chain call 0:24:06.970,0:24:11.090 any of your ActiveRecord find methods of your[br]scopes 0:24:11.090,0:24:13.400 and chain them together. So you can now use, 0:24:13.400,0:24:16.030 so in other words, with this logic, wherever[br]you 0:24:16.030,0:24:22.010 have like ActiveRecord dot, you know, find[br]where id 0:24:22.010,0:24:24.720 greater than 122 is activated and so on, wherever 0:24:24.720,0:24:26.310 you might have a chain like that, you can 0:24:26.310,0:24:28.570 still use that chain now because of this scope 0:24:28.570,0:24:30.430 class with your domain objects. 0:24:30.430,0:24:33.740 Except, instead of getting back your ActiveRecord[br]object, you're 0:24:33.740,0:24:38.000 getting back its domain representation. Does[br]that make sense? 0:24:38.000,0:24:38.600 yeah? 0:24:38.600,0:24:41.180 OK. And then the mapper is what maps them 0:24:41.180,0:24:43.950 across, right. And some cases, the mapping[br]is like 0:24:43.950,0:24:47.440 really, really simple. What's the next slide[br]here? Oh, 0:24:47.440,0:24:49.660 yeah. Yeah. There it is right there. 0:24:49.660,0:24:51.830 So the mapper, because of the way vertice[br]works 0:24:51.830,0:24:55.030 and the way ActiveRecord works with attributes,[br]you can 0:24:55.030,0:24:57.940 almost just instantiate one from the other[br]just passing 0:24:57.940,0:25:00.280 the attributes back and forth. So it's actually[br]quite 0:25:00.280,0:25:04.040 easy except when it's not easy, and then what 0:25:04.040,0:25:06.740 happens, what I'm doing now is just, wherever[br]I 0:25:06.740,0:25:09.260 have something that doesn't fit this model,[br]I just 0:25:09.260,0:25:12.030 subclass this mapper with a custom mapper[br]and override 0:25:12.030,0:25:13.650 those methods, more or less. 0:25:13.650,0:25:17.360 And that's the part that I think there, would 0:25:17.360,0:25:19.580 be more expressive ways to do that mapping[br]and 0:25:19.580,0:25:22.220 that's what I'm starting to look at now. But 0:25:22.220,0:25:25.160 so far this has actually worked pretty well. 0:25:25.160,0:25:27.830 And it's allowed me to completely separate[br]the way 0:25:27.830,0:25:30.540 I think about persistence from the way I think 0:25:30.540,0:25:32.930 about my domain object. SO we before we, before 0:25:32.930,0:25:34.910 I had this, we had, used to have an 0:25:34.910,0:25:37.160 order class, and I kid you not, and I'm 0:25:37.160,0:25:39.660 in, I'm a Rails noob so you can shoot 0:25:39.660,0:25:44.650 me, but it had forty-eight attributes in the[br]ActiveRecord::Model, 0:25:44.650,0:25:45.540 right. 0:25:45.540,0:25:48.380 That is now represented by about eight classes[br]that 0:25:48.380,0:25:50.960 separate out all the different aspects of[br]the order, 0:25:50.960,0:25:56.970 like the shipping, the billing, et cetera,[br]et cetera. 0:25:56.970,0:25:58.920 So that, but I think this mapping is, is, 0:25:58.920,0:26:01.690 is, is one of the more challenging parts.[br]And 0:26:01.690,0:26:05.180 then finally, you now I've, then you quickly[br]run 0:26:05.180,0:26:09.890 into things like, oh, well what happens if[br]I 0:26:09.890,0:26:12.240 get the record. I get the record, I save 0:26:12.240,0:26:13.640 it, then I save it again. I have to 0:26:13.640,0:26:15.070 be able to keep track that I've saved it 0:26:15.070,0:26:16.470 once already. 0:26:16.470,0:26:18.380 So that I don't have multiple copies around.[br]And 0:26:18.380,0:26:20.490 this is an identity map. In fact, an identity 0:26:20.490,0:26:25.250 map was built into Rails and I think it 0:26:25.250,0:26:26.950 might, I don't, I don't work in Rails 4, 0:26:26.950,0:26:29.030 but I think it might be taken out or, 0:26:29.030,0:26:31.480 or changed slightly. But actually, on the[br]next slide 0:26:31.480,0:26:33.330 I'll show you what the identity map looks[br]like, 0:26:33.330,0:26:34.900 and this, actually, I stole from Rails and[br]made 0:26:34.900,0:26:36.510 it a bit simpler. 0:26:36.510,0:26:37.830 But all it's doing is just making sure that 0:26:37.830,0:26:42.150 there's a unique instance on, on a per-API[br]request 0:26:42.150,0:26:46.160 call for each entity object. So that I, I 0:26:46.160,0:26:47.780 can, and it actually serves as a really cheap 0:26:47.780,0:26:50.890 cache, but that's not what it's intent is. 0:26:50.890,0:26:54.130 So, so that's kind of where, where we're,[br]where 0:26:54.130,0:26:57.430 I'm going now, is most of those service object, 0:26:57.430,0:26:59.050 form object, request, those all kind of work[br]well 0:26:59.050,0:27:00.980 for us, and I'm not looking at what would 0:27:00.980,0:27:04.190 a repository pattern look like fleshed out.[br]And that 0:27:04.190,0:27:06.080 is, by far, the most challenging piece. 0:27:06.080,0:27:08.630 But I heard a question about where you put 0:27:08.630,0:27:11.040 things. You can really just put them anywhere.[br]You 0:27:11.040,0:27:13.630 know. That's the thing, like, Rails just seems[br]to 0:27:13.630,0:27:17.820 be, there's a part that's, I just felt constrained. 0:27:17.820,0:27:20.520 Like, Rails didn't give me any guidance on[br]where 0:27:20.520,0:27:22.300 do I put a service socket. Well, you can 0:27:22.300,0:27:24.260 really put it anywhere you like. You could[br]put 0:27:24.260,0:27:26.030 it on the auto load path or you can 0:27:26.030,0:27:28.520 put it where Rails might expect to see it. 0:27:28.520,0:27:30.270 But it, you can just create a services directory 0:27:30.270,0:27:32.070 and put your service objects there. Rails[br]will find 0:27:32.070,0:27:34.890 it. It's not hard. But Rails doesn't really[br]kind 0:27:34.890,0:27:36.710 of tell you what to do. So it doesn't 0:27:36.710,0:27:39.390 make it easy to do these, to think of 0:27:39.390,0:27:42.000 it, but it actually, it's not hard. 0:27:42.000,0:27:45.090 And but that's where we're going with that,[br]and 0:27:45.090,0:27:46.940 we'll, we'll give you a link to it, to, 0:27:46.940,0:27:48.970 I've started a Git Repo where I plan to 0:27:48.970,0:27:51.100 share some of these ideas, and if you're interested 0:27:51.100,0:27:53.380 in sharing ideas with us on that, we'll, we'll 0:27:53.380,0:27:58.020 be happy to talk to you or, or join 0:27:58.020,0:27:59.730 us on the, on the Git Repo. 0:27:59.730,0:28:04.820 E.R.: Right. So what's the point, again? At[br]the 0:28:04.820,0:28:10.580 start, we had these three things. Embrace[br]complexity. Getting, 0:28:10.580,0:28:14.640 getting the solution to work is only part[br]of 0:28:14.640,0:28:18.090 the fun, right? It's, it's your first draft,[br]as 0:28:18.090,0:28:20.490 DHH said, talking about writing. You get,[br]you do 0:28:20.490,0:28:24.630 it over and over. And getting things to work, 0:28:24.630,0:28:27.950 that ninety-line method I showed earlier,[br]that worked. But 0:28:27.950,0:28:31.050 does anyone want to go back and use that 0:28:31.050,0:28:33.580 again? It's no, it's no fun to revisit, unless 0:28:33.580,0:28:36.530 you're improving it, which, I did do eventually[br]and, 0:28:36.530,0:28:40.990 and now it's much nicer to work with. That, 0:28:40.990,0:28:44.400 that's fun. At least, we think that's fun.[br]Getting 0:28:44.400,0:28:46.600 beyond the problem and getting it to a level 0:28:46.600,0:28:49.340 where we actually don't mind going into our[br]code, 0:28:49.340,0:28:52.700 extending things, and changing things. 0:28:52.700,0:28:54.890 And we do that by breaking them up into 0:28:54.890,0:28:57.550 the smaller parts with the patterns that we've[br]talked 0:28:57.550,0:28:57.980 about. 0:28:57.980,0:29:00.830 Knowing where you're going. In Alice in Wonderland,[br]paraphrase, 0:29:00.830,0:29:03.320 they said if you don't know where you're going, 0:29:03.320,0:29:06.550 any road will take you there. We think it's 0:29:06.550,0:29:08.460 important to know where you're going and pick[br]a 0:29:08.460,0:29:10.120 road that you think will take you there. It 0:29:10.120,0:29:12.030 doesn't have to be our road. These are things 0:29:12.030,0:29:14.850 that helped us, and, and we think they're[br]good 0:29:14.850,0:29:17.390 ideas. But it's not gonna solve every problem[br]for 0:29:17.390,0:29:21.220 everyone. These aren't rules. Like Declan[br]said, they're not 0:29:21.220,0:29:22.830 science. You can't just take them and throw[br]them 0:29:22.830,0:29:25.380 on and expect that your code will magically[br]get 0:29:25.380,0:29:28.320 better by going on a diet. 0:29:28.320,0:29:31.230 And be more than just a Rails developer. Not 0:29:31.230,0:29:33.810 that it's bad to be a Rails developer, but 0:29:33.810,0:29:40.810 these things apply across, across languages[br]and stuff. Like, 0:29:41.370,0:29:45.370 don't just learn Ruby. Learn, learn beyond[br]that. So 0:29:45.370,0:29:49.360 as Declan mentioned, we have this GitHUb that[br]we've 0:29:49.360,0:29:51.980 set up, and right now it's just a readme. 0:29:51.980,0:29:54.540 We don't actually have the code in their yet. 0:29:54.540,0:29:56.300 We'll likely put some of the code we showed 0:29:56.300,0:29:58.920 today, especially the repository bits. 0:29:58.920,0:30:02.180 But we'd love to continue the discussion via[br]issues, 0:30:02.180,0:30:05.750 pull requests, whatever. That would be awesome.[br]And, and 0:30:05.750,0:30:07.000 we'd love to talk to you about some of 0:30:07.000,0:30:10.530 it throughout this week as well. In fact,[br]I'm 0:30:10.530,0:30:15.130 gonna also mentioned, we have reading. You'll[br]be able 0:30:15.130,0:30:18.070 to get these slides after, so I'm just gonna 0:30:18.070,0:30:19.070 gloss over them. 0:30:19.070,0:30:21.690 These are a couple of the books that we've 0:30:21.690,0:30:23.210 taken a look at to help us out with 0:30:23.210,0:30:23.940 these things. 0:30:23.940,0:30:26.420 D.W.: I think the crowd has spoken and it's 0:30:26.420,0:30:28.990 time to wrap up. If you want to come 0:30:28.990,0:30:31.370 up and talk, I'm happy to answer your questions. 0:30:31.370,0:30:32.870 Thank you.