 So I'm going to get started. This is a pretty dense talk, so I need every minute I can get. If you go to my website, it's just my name.com. It'll have the slide deck. And if you wanted to follow along, there you can. But this is a pretty dense slide deck, so I'm going to go pretty quickly. So following along on the web would be great. Here, let's get started. So first, I want to tell you a little bit about myself. My name's Mike Appiezi. I'm a software consultant. I work at QuickLeft. QuickLeft is a mobile and web application company. I live in Boulder. I've been here for about three years. I love it here. I'm very excited to be at Rocky Mountain Ruby with you guys, presenting to you. I'm also really nervous because I know a lot of you guys, so I hope I do well. I wish myself luck. So building a complex domain is a difficult thing to do. And this is the book, Domain-Dorm Design. This was written in 2003 by Eric Evans. And it's a pretty dense book. Eric is the guy that pioneered this concept. And because he did pioneer the concept, this is the de facto book on the subject. So if you haven't read it, I highly recommend it. So how many here know what domain-driven design is? Good. It's about 30%. How many here have tried to use domain-driven design in a Rails application? Great. So probably, I'd say, 20%. This is interesting. So we're going to be building a Rails application using domain-driven design principles. So first, let's get some basics out of the way. What's a domain? A domain can be a doctor's office where you have doctors and patients and record keeping in between. A domain can also be a new idea, such as Twitter, which is a microblogging service with tweets, followers, and follow-ins. A domain can be anything, like even be collecting really cute cat gifts. So the point is that a domain is a subject matter that you're building software around. So I'm glad. Let's get that out of the way. So why does DDD exist? Well, let's look at an example. Say we're building a Rails application, we have a few models, and we have a small team, two developers, and we're really productive. We're training out future after future, and we feel awesome. That's kind of what made Rails popular in the beginning. Kind of feel like this guy. So over time, your Rails models get bigger. You have maybe 5,200. Your team also gets bigger, maybe quadrupled, and you begin to slow down. There's more defects than they used to be. Training out features becomes more difficult, and a lot of us have felt this pain. As a consultant, I see a lot of client applications that have gone through this where it's a big monolithic Rails app, and it's hard to rationalize it, and it's hard to get anything done. So we end up feeling like this turtle. So the point of domain-driven design is to address the fact that software becomes complex quickly, and to create structure to address that. So what this talk is not about, there's a lot of topics on DDD, and I just wanna make sure I set up expectations with everybody. It's not about a custom architecture for domain-driven design. It's not about design patterns specific to domain-driven design, such as repository pattern, and it's not about advanced topics such as what a bounded context is. Custom is expensive. If you're a startup, there's a reason startups like Rails, because it's fast. So we don't necessarily wanna go custom right away. We may have not even proven our market. We might just throw away the app in the end. So you gotta be very conscious about when we do these custom things. They're all great, but there's a time and place for it. We are gonna talk about is taking domain-driven design principles and applying it to a Rails application, and we're gonna talk about how to structure your Rails application in a way that will allow you to transition to a custom architecture later down the line. We wanna move fast. We want Rails to be fast, and we wanna be productive, and we wanna hit the ground running. So what is this? Anybody? Yeah, Rails. What does this tell us about our domain? Nothing, perfect. So we're gonna try to fix this. We're gonna talk through seven topics. The first one being how do we go about defining the domain and what that process is like. We're gonna talk about how communicating the domain is important, especially with a large team, in order to survive a large team. And then we're gonna talk about relationships, why they're important, and why they're not important. And we're gonna talk about aggregates, data access, value objects, and domain services. We're gonna talk about what all of those things are and how to implement them in a Rails app. So first, we need a domain. So let's pick a domain that we can build a Rails application around. I'm gonna pick the iOS app store. It's something that we're all familiar with. And let's pretend that Apple executives for name Scott came to me and he wants us to build this app store for him. So we're gonna sit in a comfort room together with a whiteboard, and we're gonna start brainstorming the ideas of what this app store is. So he's like, yeah, obviously we're gonna have apps. Developers can create apps. Any developer can, and they'll be able to submit several versions of an app. So as they make iterations, they create versions, and they can add one to six screenshots. And obviously there's customers that are gonna be purchasing this app, and customers are gonna install specific versions, and customers can leave comments on a version of an app. Okay, cool. So we kinda did this brain dump with Scott. We just let him get everything out of his head. We don't interrupt and we just draw it all out. And then we take a step back and then we start looking at this and refining it. So we're giving him a visual that we can work on. So I start asking about developers. I'm like, well, it's the only developers that can create apps. Well, what about companies like game companies? Could they create apps as well? Yes, yeah, companies can create apps as well, okay. So I put those two terms down there because the word developer wasn't enough to capture that concept. And I asked him, is there any difference between a developer and a company as far as how they create apps? He says, no, they're pretty much the same. They do the same thing. Can we think of a better word than developer or company that encapsulates both of those concepts? And I suggest, well, how about seller, right? They're both selling their apps. And he's like, yeah, seller will work. Cool, so we're gonna use seller. And then we look at version, and when he was talking about this, he was using the word version in release interchangeably. And I just threw down the first word that I heard. But I'm thinking about versions, and they have version numbers. And it's kind of awkward to say a version has a version number. It's kind of redundant. So maybe the release is the right word. So I talked to him with that and he's like, yeah, release is a better word. And I look at comments. I'm like, what else are you gonna leave on comments? Are you gonna leave ratings, possibly? And he said, yeah, we're gonna leave ratings. Okay, well, comment's not gonna suffice. How about the word review? They can capture both the concept of comments and ratings. Cool, so we've refined our domain and we went through this exercise of thinking about the language we're using and making sure that it's accurate for the domain they were trying to build software for. So to talk a little bit about the finer points of communication, there that we just demonstrated, there's two sides. You have your domain expert who's supposed to know about how your domain works. And then you have the software expert who's supposed to know how to build software for a domain. And you go through this iteration of, you go through iterations of brainstorming, drawing, diagrams, speaking out assumptions. It's very important to use natural language, speak out, because he's gonna brain dump on you so you have to fill in the gaps with language and then let him correct you when you're wrong and then you keep refining language. The two responsibilities are your domain experts are looking for things that are awkward or not just right for the domain. And then your developer is looking for things that they're using two terms to describe the same concept because when you build software, you can't use two words to describe concept. So we're defining a bigotus language and Martin Fowler describes it the best, I think, a common rigorous language between developers and users and the need for it to be rigorous since software doesn't cope well with ambiguity. So from your user to your product owner to your domain expert, to your tester, to your developer, to your designer, to your developer, all the way to your code, you wanna ensure one consistent language across the board. If you don't do this, what happens is you're gonna create a fragmented language. All the people in your team are gonna start doing translations to each other because we're not using the same language. Things are gonna get confusing. More worrisome is that your code is gonna start having different fragments of language and if you can't understand your domain and communicate, how are you supposed to build it? So if there's nothing else that you get out of this presentation, this is the most important thing. Make sure you have one language across the board. So if you make a change in your user interface and you're presenting something to your user and you made a change to terminology, then you need to make sure that everybody else in the team uses that terminology and you also need to refactor your code to update that terminology. It's the only way to make sure that you have control of a complex domain is to make sure that it's communicated efficiently and everybody understands it properly. So refactoring your code is very important. So we're gonna talk about relationships. We're gonna go back to our diagram again and we're gonna look at the relationship between an app and a customer. We're looking at the purchase relationship and I'm gonna show you what it looks like in Rails. We have on top, we have the app, inherits from ActiveRecord and it has many customers to purchases. Below we have customers, it has many apps through purchases. So we're questioning which one of these are really valid and if one of them isn't. So I'm looking at the customer has apps through purchases and it makes sense that a customer needs to know what apps they purchase, right? So if they wanna redownload it later, they need to get a list of their apps and they can select something to download. So that relationship sounds legit. Now what about apps? Do they really need to know about all of the customers that purchased it? And you might for statistics or I'm sure you could come up with a reason but this is gonna be our MVP and we wanna keep it pretty limited. There's no reason for us to get a listing of all the customers that created an app or that purchased an app. So we're gonna nix that, cool. And also we're gonna look at install kind of in a similar fashion. Does our server and our domain model need to be able to link a release with the customer and know exactly which releases customers installed? I have that same tone so I thought it was me for a second. So one way we can get around keeping this information is that all the releases or all the apps will be installed on a customer's device. So we can easily send a list of all the apps with the versions to the server and then the server can then look at the apps to figure out which versions have new updates and then send us back the apps that have updates. So in our domain model, we actually don't need that relationship either. And what about customers and reviews? Do we want customers to be able to view a list of apps that they reviewed and to look at the reviews? And not an iOS developer, so I don't know this for sure but I don't think that iOS has a mechanism for you to look at the reviews. So we don't need that either. So what we did here is we really distilled our domain down to just the relationships we need. And the benefit of that is we decoupled things that didn't need to be coupled. And we've then, whatever relationships are left, those are the important ones that actually matter to the domain. So when we're looking at an application and we look at a relationship, we'll know that has a specific purpose. That's there because it needs to be not just because it can be. All right, let's get into aggregates. And finally, we're gonna get into some code. Taylor, we're this guy. We're gonna submit a new release of an app. And let's just walk through this real quick. There's a big chunk of code here. So we're gonna create a new release. It looks like we're passing in a version number. So we're creating a release object. And then we're creating several screenshots and we're adding those screenshots to the release. And then we're setting the status of the release to submitted. And then finally, we're adding the release to our app. So what's a better way of handling this? This simple answer, and I'm sure you guessed it, is put it into a method. So we're gonna put it into a method called submit release. And the method name is going to describe the domain behavior that we're trying to represent. Also that the method is on the app. The app is in control of the release and the screenshots and all that because it makes sense, right? An app has releases and all those things. So it should be in control of managing the complexities of that. It's also simpler. Notice that we have a string. That's a version number. We don't have to worry about creating the release object and passing in the version numbers. We can just use a very short way of representing that. So it makes this very easy to look at at a glance if you're looking through code. And it also makes it easy to use. There's only one place that you can submit a release from is through this method. So that complexity will always be managed within that method. And now we're starting to see what an aggregate looks like. So in our example app is our aggregate and underneath it is responsible for managing things like the release, the screenshot and the review. And everything that you wanna do with screenshots, releases, reviews has to go through the app. So you wanna have a method on the app that manipulates those. You never manipulate them manually. And the reason you do this is you're decoupling the small subset of models from the rest of the system. And if you do this over and over again, you have all these small aggregates that then control their own complexities and then you're kind of breaking up your system into smaller components. So if we look at our app, we start seeing that we're building out all these different domain behaviors. We have submit release, approve release, flag for abuse, mark as staff favorite. And it's also explaining to us how our domain works, what's important to our domain. I can now look at this and get an idea of, oh, this is what's important to the domain and here's all the things I can do. It's awesome. All right, let's talk about the other side of aggregates, data access. We'll look at another example here. So I have an app and I'm looking for apps, so I have a Wear Clause, it's just a standard Active Record Wear Clause. I'm looking for apps that are less than a week old and have been purchased more than 10,000 times. So great, that's what it does, but why? It's very frustrating when I see code like this, like, yeah, I know what you're doing, but why are you doing it? So I give this a big fat, what? So what's a better way of doing this? It's really simple, Rails gives it to you, you should use it. It's called Scopes. The scope gives a name to the query that you have. Now we know that the meaning of this query is new and noteworthy. We want to know what the new and noteworthy apps are in the App Store. Another cool thing is Figleaf, Avdegrim created it and basically what it can let you do is privatize all those Active Record Methods, like the Wear Clause, so that you can't actually use it outside of the app. So it kind of reminds your developers that, hey, you should only be writing Scopes that have, she'll only be writing queries that are in Scopes. Now obviously developers will find a way to get around it if you want to, so this is not enforcing anything. It's a reminder, right? This is really, you can do whatever you want with great power comes great responsibility. So if we look at our app again, we have all the different ways we can access data. So why is the app important to us and how are they important to us and what are the different ways that are important? And we also have our behaviors down there to tell us the things that we can do with it. So we're creating one expressive point of entry for our aggregate and something else to note, aggregates are the domains only point of entry for data access. So if you want to get to release information or screenshots or whatever that is, you're always going to go through your app. So you only need data access on your aggregate. And that's pretty cool and I'll show you why. So if we structure our sub folders and our models folder like this, so you can't actually have a folder and a model with the same name because modules and classes are the same constant and all that business. So I'm using the plural for apps and then in there we have our app and then those are all the other objects that the app is in control of. And if we look at this, now we know where all our aggregates are. That's pretty cool. The first layer of our models directory shows us where all our aggregates are. Now we can look at the behaviors. We know what to look at to figure out where we retrieve data. And now we know what to look at when we want to make changes or add a feature. Cool, so the last piece we're going to talk about are value objects. And this is actually the most difficult part to talk about and the most lengthy. So I'm going to try to roll through it pretty quick. So there's two types of domain objects. There are entities and value objects. An entity is a thing. So this is the simplest way to describe it. An entity is a thing and a value object describes a thing. Let's look at an example. Say we have our entity as a customer object and our value object is a name object. So if I'm a customer, my name's Mike Aviesi. Okay, so if you, if my cousin is also a customer and his name is Mike Aviesi, we're still two unique different things, right? We have our own life cycles. We create counts at different times. We can deactivate our account. Just because our attributes are the same doesn't mean we are the same. And those are the, that's what makes an entity. It's unique, independent. It's an attribute that has a life cycle. It can change state. It's a complex thing that we're trying to represent. Now value object on the other hand, it just describes a thing. So if I am a customer object and I have a value object that's name and it has Mike Aviesi and I plop it on me. And if I take that and throw it away, create a new name value object with the name Mike Aviesi and plop it on me, doesn't make a difference. No, it doesn't make a difference. And that's what makes value objects unique. So because it doesn't make a difference, we can just throw them away and recreate them. And because we could do that, we can make them a mutable. Mutable is a really cool thing. And that just means that you can't change it after creation. So the reason a mutable is cool is because let's think about an entity, right? It can change state. You can create an entity or you can create an entity through a constructor and it sets up an initial state, has behaviors, those behaviors can change the state. But a value object, it only has one method. It's constructor and that constructor creates the state and then it's done. So they're very simple in terms of how they're built. They're very easy to think about. The other thing too is you're sending a value object through series of functions. Nothing can accidentally change its value. Where an entity, if it's a object that can change, those functions could change the value and then you get these weird side effects and value objects essentially can make your code safer. Also value objects don't reference anything, right? It's just an attribute that you use to describe something else. And the whole point of me bringing this up is because value objects avoid the design complexities of entities and that's a really good thing. If we have a hundred models and we can identify say 40% or 30% of them that should be value objects, well then we've identified things that will have simpler creation. They won't have side effects so they're less error prone and they won't be related to anything else because they shouldn't be. So we're reducing the complexity of our domain. We don't wanna unnecessarily add complexity to our domain. So what's what in our example of an app, aggregate? So all aggregates are entities, that's an easy one. There's this complex thing that builds things and it can change state. A release also has like a life cycle, you can create it and then when there's a new release there's no longer valid screenshots. Now to answer this one, we have to think about screenshots. Do they have a life cycle? I mean through the release I guess they do. Are they important on their own for any reason? Not really, it's just another way to add a description to a release. So those are actually value objects. And then if we look at review, let me do a little time check here. Okay I have about five minutes left so we're gonna have to go through this. So if this is a review on Amazon you can look at a list of reviews, you can edit them, people can comment on them. That would be an entity in Amazon. But in our app store we don't want any of that, it's just MVP, we want people to just create a review and if they wanna update the review then they just create a new one and behind the scenes we delete the old one. So we get to treat it as a value object. All right so what does the value object look like? It's just a plain Ruby object. It doesn't inherit from anything especially active record. And it has a constructor taking the attributes, it exposes the attributes as read only methods and it overrides equal equal and to make Ruby happy for fast comparisons in certain situations. I'll let you look this up. We need to alias eql and then we also need to define hash. So that's what a value object looks like. Another good thing for value objects are factory methods. So if you think about a string it has the method up case. What it does is it takes the original object and it transforms it into a capitalized version. So this is a very natural thing for value objects and those are natural behaviors. So we're gonna have one here that's next major version. It news up a new value object and then increments the major version. Cool so the tricky part that is hard to get a focus on or hard to understand to do is how do you persist value objects? So we're gonna talk about three ways. One is inline on the entities table. Two serialize on the entities table and three is in its own table and we'll go through each one of those. So inline on the entities table. We have a release and we're looking at the version number value object and we have a release table that has three attributes for major, minor, and build and that's where we wanna store those values but we want our domain model and our models to be separate objects. So how do we do that? So it's really simple. You create a writer method called version number. It takes in a version number value object and then all it does is take the attributes from your value object and assign them to your release attributes. And on the way out, reading out the version number, you're going to new up a new version number value object and pass in the attributes for the release. So that's how you expose value objects through a release. Another thing you can do is hide the attributes on the release, the version major, version minor, and version build because we wanna access versions through our version number methods, so we wanna hide those. Our second example, we have screenshots, it's an array and we want to be able to save those to one column called screenshots on the release table and we're gonna do the same thing except we're gonna use a JSON serializer and we're going to serialize the data into our release attribute and then deserialize it out of our release attribute. This is a very basic example. If you're using an array and you wanna be able to pop things on, you're gonna have to get a little bit fancier but this works, right? This is the basics of what you need. And third, if you wanna store a value object on a zone table, we'll look at review as an example and wot, wot, wot, we have to inherit from ActiveRecord. Kind of sucks but we're gonna try to do the best we can. So there's this really cool method on ActiveRecord though called read only bang. And what it does is when you call that method, it kind of locks your model so that if you try to change attributes and save it, it won't let you, it will raise a read only error. But it's kind of awkward because it still lets you set the attribute so you can still set the attributes and then you try to save it and then you get the error. So we're just gonna do a little thing here. We're gonna create, we're gonna override all the writer attributes and actually ActiveRecord is using method missing but just for simplicity. So we're gonna create all the writer attributes and then we're gonna raise error if anybody tries to use it as a reminder, hey, this is read only, don't use this. And we're gonna wrap that into a method called immutable. And then after we save the record, we're going to make it immutable. So that way you can change it during creation but once it's saved, you're done. It doesn't change anymore. And also in find, when we retrieve it from the database, it's also gonna be immutable so you can't change it. So it forces you to throw it away and create a new one when you actually need to make a change to the value object. And we're gonna implement equal equals again the same reasons. So last, let's talk about domain services. So we talked about aggregates. Aggregates have their own little world of complexity that they're controlling. What if you need to process a transaction between two aggregates? Well, what do you do? One aggregate isn't allowed to reach out of itself and affect anything else, right? So we need a third party to handle that for us and that's where domain services come into play. So let's look at an example. Say we wanna gift an app. We needed to do two things. We need to charge the gifter that's purchasing the app and then we need to assign access rights of the app to the giftee, right? So our customer object is an aggregate and even though we're just affecting two customer objects they're still independent aggregate instances. So they're still not allowed to reach over into each other. So we're gonna use the domain service to handle the strength action. So simply again, the domain service facilitates a transaction between two aggregates. So our model, something like this, we have a customer and it has purchases. We need to expand that a little more to separate the concept of purchasing and actually being able to use an app. So we're gonna introduce another model called accessRate and we're gonna take a look at in code what a domain service looks like. And it's gonna be a simple Ruby object. Its name describes the behavior that the domain is trying to fulfill. It doesn't inherit from anything and then it's gonna have an execute method. So it's just one operation. We're gonna take in all the things that we need to make this happen, this transaction happen. So we'll take the app, the giftee and the giftee, the giftee and the giftee are our customers and we create a purchase record for a giftee and we create an accessRate for a giftee and likely you'll wanna use database transactions and whatnot but you get the idea of how to structure these. So let's look at our Rails directory again. We can create a services folder that has all these domain services and now we get to see all of the cross-aggregate transactions. All of these cross-aggregate transactions. So in summary, we talked about how to continuously refine the domain with the domain expert. We talked about how to insist on having you pick up this language for seamless communication across your team. We talked about constraining relationships to only the ones that you need. We talked about creating aggregates to manage complexity and express domain behaviors and we talked about expressing the domain's intent through well-defined data access and we talked about creating value objects to eliminate unnecessary complexity and we talked about creating domain servers to express transactions between aggregates. So if we look at our Rails directory again, we started out with this. It just tells us it's Rails. And now we have this that actually tells us how our domain works. So over here, we have our aggregates. We know all the entry points of our application. We know where to retrieve data and we know where to enact domain behaviors. And we also know all the services and what domain behaviors are cross-aggregate transactions. So now we actually have a Rails application that expresses our domain. Thanks for listening. Again, thank you for a quick left for sponsoring me. We're hiring, so if anybody's interested in working at Quick Left, it's a really fun culture, come talk to me. And a special thanks to Paul Reiner, a virtual genius. He's been kind of a DDD mentor for me. I'm on the internet and I am also trying to find a few people to mentor. So if anybody's interested in mentorship, it doesn't matter what you want to learn. You could be an ultimate beginner or you could be advanced. If you're advanced, then maybe you'll be my mentor. And so go on my website and go to the mentor section if you're interested in that. So now, questions. Yeah, so the question was can I expand on how value objects keep us out of trouble? So it's really simple. You're boiling down an object to something that's extremely simple. So it's hard to mess it up, right? If it can change, then you can change a state. Then you gotta manage a state that changes. And you might have domain rules around, okay, if you have this, then you have to have that. So a value object allows you to put all those in one method and then you're done. It's just that one method that controls it. And the other thing is when you pass that value object through functions, it can't act, those functions can't accidentally change it. So you won't get these weird side effects. Whereas if you have an object that you can change, it went through 10 functions. And then when it came out at the end, it's different. And you're like, oh, God, why is this different? What happened? Oh, where did it happen? So, and it also doesn't have relationships. So you're not coupling it to anything. It's a good question. Oh, yeah, the question was how do you translate this for a API? And you mentioned that. It feels like it's more of a MVC type of thing. So I think it works perfectly fine as API. And I've used it several times that way. You're really just, as an API, you're really just getting to the nuts and bolts of how your domain works. And then you make changes in your domain and you do things and you just create your domain to represent that. In fact, I think it's actually easier. No? Okay, great. Thank you guys.