Hambone Blues Jam

Home Decoration Tips
RailsConf 2014 – Domain Driven Design and Hexagonal Architecture with Rails

RailsConf 2014 – Domain Driven Design and Hexagonal Architecture with Rails


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.

Leave comment

Your email address will not be published. Required fields are marked with *.