---
title: 'php[tek] 2017: Workshop: Domain-Driven Design Deconstructed'
source: 'https://youtube.com/watch?v=MIpgMYJlxOc'
video_id: 'MIpgMYJlxOc'
date: 2026-06-17
duration_sec: 8170
---

# php[tek] 2017: Workshop: Domain-Driven Design Deconstructed

> Source: [php(tek) 2017: Workshop: Domain-Driven Design Deconstructed](https://youtube.com/watch?v=MIpgMYJlxOc)

## Summary

Domain-Driven Design (DDD) is a software development approach that focuses on understanding and modeling the core business problem. It emphasizes creating a shared 'ubiquitous language' between developers and domain experts, which is then directly reflected in the code. This workshop provides a practical introduction to DDD, using the example of building software for a brewery.

### Key Points

- **Definition of DDD** [8:00] — DDD is about focusing on core business problems, developing collaboration with stakeholders, and modeling objects in an understandable and maintainable way.
- **Ubiquitous Language** [29:05] — A shared language between developers and domain experts, ensuring everyone uses the same terms for the same concepts.
- **Event Storming** [52:21] — A collaborative modeling technique using sticky notes to discover domain events and build a shared understanding of the problem space.
- **Value Objects** [66:28] — Small, immutable objects that represent a specific value in the domain and are always valid. They encapsulate validation and business rules.
- **Entities** [66:50] — Objects with a distinct identity and a lifecycle that can change over time. They are mutable and have state.
- **Aggregates** [67:24] — A cluster of entities and value objects treated as a single unit for data changes, ensuring consistency.
- **Bounded Context** [45:25] — A boundary within which a particular ubiquitous language applies, separating different parts of the domain.
- **Hexagonal Architecture** [107:44] — An architectural pattern that separates the core domain logic from external concerns like frameworks and databases.
- **CQRS (Command Query Responsibility Segregation)** [119:44] — A pattern that separates read operations from write operations, often using separate models for each.
- **Event Sourcing** [122:49] — A persistence pattern where the state of an entity is derived from a sequence of stored events.

## Transcript

All right, I assume everybody can hear
me. Do I need to talk a little louder?
Are we good?
Yeah. All right, great. Um, hi everyone.
Um, I've met some of you before. Uh, but
I'm Andrew Cassell. Uh, PHP developer.
I've been developing for about 10 years.
Um, I'm kind of local here. Uh, I live
in H. turn, which is about 10 miles away
or approximately 8 days travel uh during
DC rush hour.
Um
we're for a uh a nonprofit that's called
the Marine Spill Response Corporation
and uh we clean up uh oil spills. We're
a uh nonprofit um we're the largest in
the United States uh that does what we
do and we have all kinds of equipment
and people who are trained uh if there's
an environmental disaster to respond.
Uh I work um like I said at doing PHP
development there. I do mostly
applications. We also build their
website and I work as a uh part of a
three-man team. So um three developers,
small team. Uh, but I would put our
three developers up against a team of 10
uh, any day.
Uh, I'm also the organizer of DCPHP. Uh,
so if you're local, uh, we'd love to see
you at our meetups.
Um, today, uh, hopefully it's going to
be a lot less formal than most
presentations. Um, this is a workshop,
so I intend it to be interactive. Um,
the video is going to be recorded. If
you guys need to refer to the slides
later, uh, I'll put them up on the
joined end. I'll also post them a link
to Twitter. Uh joined in is the uh
conference rating system that you guys
should uh you know write this talk
afterwards. Uh so the video is the
slides are going to be recorded but
nothing is going to be video recorded.
Um you know so you guys don't have to
worry about being up on camera or
anything like that if we as we do uh
stuff together here. Um I'm going to be
you know kind of presenting here for a
little while. Then we'll do some work
and then we'll present a little bit and
do a little work again. But while I'm
presenting because I want to try to keep
this informal, it's kind of a, you know,
small enough group we can do this. Just
raise your hand and if you have a
question, I'll stop and try to answer
your question and stuff like that. Um, I
really don't want to go any further
unless you're not, you know, you're
understanding something.
So, um, for also for today, you can
assume that all the code you're going to
see and do is around is PHP 7 or 7.1.
Uh, but that doesn't mean you can't use
PHP 5. have been doing this longer than
you know PHP5 is what we did it uh you
know for a long time before PHP7 came
out. So all of the code that you know
the examples and stuff you're going to
see the only differences will be type
hinting uh on the returns and stuff like
that that uh you know you'll see from
PHP7 but other than that nothing changes
to go back to PHP 5. Uh we're also going
to take a break in the middle um right
around 11 when they um the other session
gets out just you guys use the bathroom
get another cup of coffee or whatever.
Uh also you know as we're doing
exercises if you need to leave you know
whatever that's fine too. So
to get into you know why why are we here
today? Um why you know why are you here?
Why am I here? Because you know I'm here
for the same reasons. I want to become a
better developer. I want to be uh a
better resource for my organization. Um
you know I believe in becoming becoming
a craftsman meaning having better code
craftsmanship raising the level of my
code and becoming a more mature
developer and becoming a more mature
developer means um better understandings
of the problems that you're trying to
solve. Becoming uh you know more of a
value uh for the company that you work
for or for your customers.
One of the best ways that I know to
become a better developer is through
domain driven design
and domain domain driven design. It's
often referred to as DDD uh in the the
you know cool kids on the street call it
that. Um but it's a very very large
topic and there's no way I can teach you
everything in about two and a half hours
we have today. Uh but I'm going to try
to show you what I think are the
fundamentals and insights that everyone
uh can benefit from hearing. So um you
know anytime a presentation starts with
a survey or a definition definition I
immediately hate it. Uh but I am going
to start with a survey and a definition
because I want to kind of find gauge
this because it's gonna be you know kind
of a workshop. I want to know um couple
of questions here. So does anybody know
BDD or has experience with DDD at all?
One person. Okay. Do okay.
>> You've read it. Okay. So this will be
this will be fun. Um
yeah, it's going to be a very beginner
um introduction. I think it'll be good
for you and then I'm going to give you a
ton of resources at the end so you guys
can go and learn more and you know
obviously answer questions. So let me
tell you what DD is not. It is not drink
drive and develop but we are going to be
talking about beer today. So please
drink responsibly and code responsibly.
No. Uh so today's talk and today's
workshop is on common sense software
development and when I mean common sense
software development I mean you know
doing things that make sense as as it
says um you know to as every
presentation should steal a quote from
Steve Jobs um design is not just what it
looks like it's and feels like design is
how it works. If you look at the
circuitry of an Apple computer, you
know, the old ones, they're they're
beautiful compared to most, you know,
computers of the time. Uh, and your code
should be beautiful, too. There's not
much more effort to be put in in order
to make your code, you know, more repres
more more presentational.
So, um, because software to me is first
and foremost about problem solving. Now,
Freddy Krueger is an excellent problem
solver. He is designed to kill and is
very effective at it. But what I'm
saying is people will enjoy your
software much more if it's more like
James Bond. Still designed to kill
people, okay? But, you know, you're
going to want to hang out with James
Bond at a bar more, you know, and drink
a martini and things like that. So, you
want to get the job done, but do it in
an aesthetically pleasing manner to both
you and your clients.
So, the bulk of the information I'm
going to talk about today are from these
two seminal books on domain driven
design. Uh the first one domain driven
design uh was a book by Eric Evans. Uh
he wrote it in 2004. Um you can kind of
think of this uh as the Old Testament of
domain driven design. Uh the first time
I read it, I did not understand it at
all. Maybe I'm a little dense, but it's
a very very theoretical book and it but
once you understand more of the you know
experimental side of things and you go
back it makes a whole lot more sense. Uh
the second book uh was by Von Vernon.
People call it the uh the red book, you
know, kind of the new testament of
domain driven design. And this goes into
much more practical application of how
to uh build your code uh using domain
driven design. There's been plenty of
books afterwards once everybody got on
the gravy train of domain driven design.
Um you know, if you want to learn more
about the techniques and things like
that or um you know, if you just you
were like me in high school and just
read the cliff notes, the domain driven
design books on the end in PHP and do
domain driven design quickly. um those
were, you know, good good uh source
material to get going quicker.
But, um first off, like when I'm talking
about the code and patterns and stuff
like this, none of this stuff is new.
Nothing that the domain driven design
people um created was new. It was all
looking at patterns that had already
existed in software development for a
long time, whether that's repository
patterns and object-oriented program um
things like that. And you know, most of
this comes from books that are even
older than the books that I just talked
about.
Uh, a couple people that I want to thank
real quick from the PHP community is
Matteas Reyes and Bo Simonson. They've
been um, you know, fantastic uh, pushers
of DDD and PHP and couldn't do this
presentation without them. Okay, so for
those of you who haven't heard about do
main driven design, this is your
definition. So if you read the Wikipedia
definition of it, um, you kind of get an
idea. uh DDD is about focusing on the
core business problems that you're
trying to solve, developing a valuable
collaboration with the other people who
have a stake in the process or in the
project that you're you know you're
building and then trying to model uh the
objects in your software in a way that
is both understandable and maintainable.
So DDD is really meant um to help you
tackle really complicated problems much
like putting together IKEA furniture but
for software. Um so you know we're going
to be trying to figure out the model,
how to model what the business is trying
to do it and then figure out how to
apply that model to code.
So, if you don't have a good grasp on
object-oriented programming um so far, I
apologize. Uh but this is going to be
very object-oriented uh heavy. And when
I mean object-oriented programming, I
don't mean how they teach you
object-oriented programming about how
you know dogs drive and cars bark and
those kind of things. Um this is
object-oriented programming how it was
meant to be. If you go back and read
some of the older books on
object-oriented programming from small
talk and things like that, um you're
gonna this is where the DDD people, you
know, read this and applied it to new
software practices.
So to summarize DDD, we're going to be
talking about encapsulation. So
encapsulating business rules and things
like that in logic, immutability,
um something that's going to make your
software much easier to maintain and
write, modeling. So, we're going to talk
about modeling objects and entities and
uh stuff like that and behavior and
encoding the behavior of the business or
the process that you're trying to model
in into your software.
So, when I when I say the word domain,
I'm talking about the problem we're
trying to solve. So, or the problem
space is it's kind of a universal word
in the sense it's either the problem or
the this problem space. So, for me, my
domain is, you know, oil spill response,
but that's probably not very exciting to
you guys. So, we're going to talk about
something I'm much more excited about,
which is beer. So, uh I'm sure many of
you have been to a brewery. Um do any of
you home brew or veryware know how to
brew beer?
Great. So, we got at least a couple of
domain experts in the room here. Um I've
been brewing for 15 years and I'm 34.
So, do the math.
I I found out you could buy all the
ingredients in college and you know they
don't stop you.
Um so we're gonna we're going to try to
model the process of uh you know doing a
brewery. I sent you all a handout out um
it's got some stuff in it about brewing
and whatnot. Um but my first job out of
college uh or sorry in college still as
a co-op was in the beverage industry. I
worked for a company that built um
high-speed packaging equipment uh for
Budweiser and Heineken and stuff like
that and they could package 300 cases of
beer a minute. So it's an industry that
I you know familiar with um on both
sides you know and so we were we were
writing and I can't show you the screens
that we were writing but um you know we
were writing human machine interfaces uh
you know for those big giant machines.
Again, I hope that, you know, this
presentation is both going to get you
wanting to do domain driven design and
brew your own beer at home. But I I you
know, domain driven design is very safe.
You don't have to worry about it. But
brewing can be dangerous. There's hot
liquids, there's steam, things like
that. Please, if you're going to go into
it, be safe. Be careful. Um care about
your safety very much. Uh if you want to
learn how to brew, first thing is grow a
very big beard. Uh no, that's actually
not necessary. I can't. So, but I'm
doing fine. Um there is a book that's
out there that's called How to Brew.
It's kind of the seinal uh knowledge
that's out there. But uh on the first
page of your handout, I've got this
diagram and you kind of refer to that uh
today, you know, as we do some of the
process modeling, things like that. Um
this is my favorite diagram of the
brewing process. Um because one I think
it it shows you that you know the things
are going into the process but it also
um has two features that I really like
is that you know it everything goes
through the process of milling the grain
and mashing the grain which is like when
you you put the grain in at 150 degrees
158 degrees of water that's like 65 C if
anybody's international or not
imperialist. um to put the the grain in
the mash ton um which is this container
right here and it steeps for like an
hour depending on how long you're
needing to do it. You separate the grain
from the water and that's called spent
grain comes out and then um you take the
liquid that's left over from the grain
and then that's what you boil and that's
what you throw in the hops which is what
gives beer that bitterness and that that
flavor. You boil that, you cool it down,
you put it in a a fermenttor, which is
either a giant stainless steel vessel or
a bucket. You put um an air lock on it,
so you separate it from the outside
environment, so no back bad bacteria and
things like that get in. You add your
yeast, which is usually um a brewer's
yeast, which you can buy at a, you know,
a shop, and it usually fermentss maybe
two weeks, three weeks, depending on
what you're making. Um, some beers you
can make at room temperature. Others
like loggers you need to do cooler. So,
there's some, you know, there's a lot of
things you can you can do with this
process to make different types of beer.
And then usually it comes out of the the
ferment into a keg or into bottles and
is carbonated either using force
carbonation or if you add a little bit
more sugar back into the beer, you can
get uh, you know, it can condition in
the bottle so it can carbonate it. So,
yeast when they eat sugar, there's two
things that come out. There's CO2 and
alcohol. few and a few other things, but
for the most part CO2 and alcohol, which
is, you know, the things we really like.
But the reason I really like this
diagram is that, you know, as the
process goes through this way, on one
end you get beer, and on the other end
of the spent grain, you get bacon. So,
those are two really good things.
So, just to show you some of the
ingredients that um you know, these are
this is barley. Um I've got some the
ingredients up front you guys can take a
look at. Um then you you know malt the
barley as I said you know it look a bit
closer look after it molts it turns you
know brown a little bit toasted. This is
a picture of the mash tun where they're
mixing the grain and the water. These
are hops. Um you don't usually see hops
in wild most places. So they're grown in
big farms for the most part. Um that's
what a hop looks like. Um you know
they're about that big or so. And then
it goes into the boil kettles. So you
can either do loose leaf hops like this
or you can buy it in pellets. So, this
is um boiling the the liquid that came
off of the grain that was separated from
the grain with the hops. And that helps
extract some of the flavors and stuff
from the hops. And then you cool it
down, add your yeast, which is uh the
little bugs, which we want bugs in our
beer, but not in our software. And then
put it in a ferment, and out comes beer.
So, you know, and everybody hopefully
likes beer, but if you don't, I
apologize. Uh these are a couple beers
that I brewed. Uh so you know why am I
talking so much about beer? Well, we're
going to be making beer brewing software
here today and there there are plenty of
examples of uh beer brewing software. Um
this is one that I use a lot. Uh it's
called uh brewer's friend. It's okay. Um
this is a recipe uh that I made uh for
the conference here. It's the Northeast
IPA.
So um we will be uh I'm not going to
have any during the workshop because you
know I want guys to stay focused. But I
do I did bring some bottles of it with
me. I also have my heavites
uh with me as well so we can sample that
later. There's plenty of software out
there already. So we got some good
examples to look at if we need to. Uh
beer smmith is probably the most widely
used. Um it is very complicated and I
realize that picture is kind of blurry
but it doesn't matter because there are
so many knobs and switches and
everything in the software. It's
ridiculous.
Um this is uh supposed to be the easy
water calculator. um very very many
fields in an Excel spreadsheet to fill
out. Um this is the not easy uh water
calculator. So when you're putting your
water in your beer, you want to kind of
adjust um you know various minerals in
it and stuff like that to uh try to
figure it out. And you tend to have to
use like multiple pieces of software all
together. Uh and then you know people
can get um 16,000 views on YouTube for
how do you use the two pieces of
software together because it's really
complicated. Um you know and I mentioned
Brewer's Friend. This is one that I've
used that's not terrible. Um, there's
another one, a brewer wall that's more
like calculations and stuff like that.
It's kind of useful. Um, so I've got in
my head the last like month or so. I'm
going to write a new piece of software
and I'm kind of using it to do some, you
know, the examples here and you know, I
told my wife I need time to work on
software, but it's for the conference.
Don't, you know, it's not for this
stupid hobby of mine. Um, that costs way
too much money. But anyway, uh, so
beeriously is what I'm calling it. And,
um, hopefully you learn if you're
writing beer software. I don't if you've
seen this XKCD comic or not, but it
talks about programming seal with the
amount of blood alcohol concentration.
And they call it the bomber peak. So, if
you get it right around a 0.12,
you're um, you're an excellent
programmer. So, that's what I would
highly recommend everybody try. So,
that's enough about about how you know
the process of brewing and stuff like I
just want to get you guys familiar with
it. Um, but you know, if you're going to
build software to manage a brewery and
you know, say that, you know, you've
walked into a brewery and they've asked
you, okay, you're going to build
software for you. Now, most developers
when they start thinking about software,
um, you know, they start thinking about
the data that the software is going to
have. So, they're they're worried about,
okay, we're going to need a recipe table
because obviously this brewery needs
recipes. We need a we need a hop table
because we're going to store like, you
know, data about hops and things like
that. need a grain table. You know,
we're identifying the data and stuff
like that. And that's kind of how our
developer repa reptile brains work. You
start thinking about data and the
infrastructure and things like that. You
know, okay, we need a hop inventory uh
database. You know, are we going to use
NoSQL or MongoDB? You know, these kind
of questions start popping into your
head. I don't know if it's the ADB
kicking in or if it's just all
developers. But I mean, I think that's,
you know, kind of how our brain starts
working. We just start flying off the
handle. We haven't even, you know,
thought about the problem we're trying
to solve. We're already starting to
think table table. You know, what's the
events? Oh, we're going to use events,
things like that. We have a Q. We need
uh you know, are we going to get rabbit
MQ? How are we going to do these cues
and things like this? You know, maybe if
you're a Symphony developer, you're
going to um you're already thinking
about, okay, roll user, how are we going
to do the permissions, things like that?
You know, I can't stress this enough,
but you know, and this is kind of what
the beginning of this presentation is
about is just slow down. Try to
understand the problem you're trying to
solve. Um, most of us, and I'm, you
know, guilty myself, is we don't
actually take the time to try to figure
out what the problem is. We have, we're
all these, you know, super intelligent
beings that have brains that work at a
thousand kilometers an hour. Um, but,
you know, you have to stop and think
about the problem you're trying to
solve. So, you know, what I'm saying is
that the imple implementation is
important. How you implement your code,
what frameworks you use, stuff like that
is important, but that's often the first
thing we start thinking about as
developers or it's how we identify the
developers. I'm a Symphfony developer.
I'm a Laravel developer. I'm a Zen
developer. No, we're we're here to solve
problems. It doesn't matter which one of
these frameworks they use. They're all
fine except for one. I'm not going to
tell you which one. No, just kidding.
Um, see, I could already get people
hating if I just picked one. No.
So, um, yeah, there's this saying uh
people uh don't want to buy a
quarterinch drill. They want a
quarterinch hole. They they don't want
to hire you. Honestly, they don't want
to pay you money. Why would they want to
pay you money? What they want is they
want their problem solved. So, you have
to identify the problem space. you know,
you have to work with the people that
you're going to be working for and
actually identify is what you're trying
to solve a problem in the first place.
Put your, you know, the value that you
can give to a company, you know, to the
right place. Make sure you're solving
the right problem. And remind yourself
that unless you work for yourself in
your own consultant, which then, you
know, your money is made in being part
of the problem. Remind yourself that
your your job is to make a pile of money
or save a pile of money for your company
through software. So maybe writing your
own DI container, writing your own
framework, writing your own session
management, doing password resets and
stuff like that and not focusing on the
core business problems is dragging the
company down a little bit. So you know
really just relying on frameworks to do
the things that frameworks do well is
very important because you're going to
have enough problems dealing with your
own code and dealing with the problems
of the business.
I like this tweet. I just saw this the
other day so I added it. But um you know
despite what newcomers think
understanding a language framework or
even algorithms is not the hard part of
building software and I agree with that
and I would remind everybody that you
know we're all probably all using PHP
today. That's probably not going to be
the case in 20 years. Hopefully I know I
still want to be doing my job in 20
years. Might be even PHP might be
another language. You don't know. I mean
the tools change. We we've all seen the
tools change in our careers in our
lifetime.
the practices and how you approach
problems that doesn't change. You get
better and better at this and you learn
a way of meth a method of approaching
problems. Your your skills can apply to
any language or any job. There's a a
whole methodology of I don't want to you
know bore you too much um but there's
this whole methodology of trying to
figure out what is the problem you're
going to solve. It's jobs.org and you
know he has this whole spiel that he
does like a telemarketer or something
like that about you know milkshakes that
you can hire them. Um but you know
really the whole the whole idea is um
you know why why is the person using
your software Monday at 8 a.m. you know
try to understand them and get in their
mindset and not try to just you know
build feature feature feature feature
feature feature that may not be actually
what they need try to under understand
the problem that they're doing or the
problem that they have you know and try
to put yourself in their shoes. One way
you can do that and this is from you
know from them the jobs to be done is
the five W's and the problem is here if
you're the people that you're working
for are probably not software developers
they're probably not software designers.
So you know if you show them something
and the first thing they see they're
like wow that's awesome you know you've
impressed them sure but maybe you
haven't actually solved their problems.
So
what we want to do is go from somebody
saying like wow that's great to of
course that's how it should be. So that
that's where we want to end up with with
our software is of course that's how it
should be.
Otherwise you might end up with you know
a beautiful app but it delivers artisal
toast or something like that something
silly. We want to solve a real problem
here. So an example of the five W's.
Like let's say you walked in the brewery
and I'm going to suggest that you do a
drink for every Y here as part of the
plan here. Um but say you know the
brewer says you know oh I need a list of
grain in inventory. So, okay, you know,
if you're the the quick and easy
developer, you're just going to say,
"Yep, I'll give you a list of out of the
database. Select all." You know, you
know, whatever. But, you know, if you're
thinking, you're going to ask that
brewer, why do you need this list? And,
you know, the brewer is going to say,
"Well, because, you know, I need to be
able to sort the list of grain to see
the inventory to see which one I'm
running low on." Well, if you ask him
why again, he's going to say, "Well,
because, you know, when we formulate a
recipe, we need to check and see how
much of a particular grain we have on
hand." Then I have to figure out, okay,
how much do I need to brew that recipe?
I'm going to do the little math in my
head and figure it out. So, at this
point, maybe you're thinking, oh, you
know, I can build a search mechanism out
of that list. I can list the inventory
right there. But if you don't keep
asking why, you're not actually going to
get to a better solution. So, if we ask
them a why, well, why don't we
automatically calculate how much grain
is needed to order? you know on the
brewery recipe creation process that's
you know handled by the brew master um
you know in the ordering may be a
completely separate department but you
know if you ask the questions why you
might figure out that you know why can't
we just automate the process why can't
we say when like you know recipes
designed and it's we're scheduled the
brew session we just automatically order
the stuff if you don't ask the why why
why you know you start up with you know
what is maybe the simplest solution it
will take you least amount of time but
you end up with automation and what is
real powerful software that's going to,
you know, save the businesses tons and
tons of time, perhaps tons and tons of
money.
So, you know, DDD, just to kind of
phrase it all over, is not about the
patterns. It's not about, you know,
you're going to hear things about CQS
and event sourcing, all these fancy
things in DDD. They don't matter. Um,
it's not about adding tons and tons of
complexity to problems. If you have a
simple problem, you know, keep it
simple. So, DDD is not for every
project. It's not even for every part of
a software application perhaps you know
um we're trying to keep a um things
simple you know as Kiss keep it simple
stupid but so don't use what I'm you
know kind of teaching you here for you
know things that are simple crud like
you have a data entry form and you need
to display that like don't don't bother
this you know use Laravel or something
like that it's going to be much faster
um you know don't use it for content
sites or websites like we have like I
said I I build applications for the
company I work
We didn't use DDD at all on our website.
Like it, you know, a little bit for some
of the stuff, like pieces of it, but for
the most part, it was just, you know,
it's content. It's not it's not worth
it. Um, you know, don't use it where
you're doing functional programming
things like that. Um, if you take it to
the extreme, you end up with, and this
is a great GitHub project to go check
out. It's enterprise quality coding, the
Fsbuzz Enterprise Edition. I think it
solves the FSBuzz problem in like 8,000
lines of code or something. I can't
remember exactly but and you know just
to kind of iterate it or sorry to
finalize it you know that DDD is not the
one true way to build software that is
obviously Ruby on Rails.
Okay so all of the normal solid
principles apply for doing good code. Um
you know if you don't you not have a
good grasp on this you know maybe you
spend a little time and read about it.
But what we're concerned about today is
the quality of the software and quality
software. Um if you look at you know
research about you know what is good
quality software. Carnegie Melon uh has
a great uh training program for that. Um
we talk about you know what are the
things that make good software good and
I'm going to pick like five out of these
you know big giant uh list of software
quality. And you know that's what I
think are the most important is
correctness meaning the software you
know works as expected testability
usability
maintainability and modifiability. So
you know just what I think are the five
most important things about good
software. And we're going to compare a
DDD app domain driven design app against
you know your usual CRUD app what mo
most of us learned how to develop uh
doing
uh Mattas Reyes who I mentioned earlier
has a lot of great blog posts but this
is a good one. It's CRUD is an
antiattern um you know designing your
software to modify data in a database
but not creating software for behavior
um will cause you problems over time.
Um, people in the DDD
world, you know, they don't they don't
call it CRUD apps. They have another
name for it. It's called the big ball of
mud app. And I'll get into kind of
explaining that in a minute. Um, but if
you were to um, you know, compare like
what kind of apps you should use in DDD,
even in a brewery versus CRUD, like I
said, if you're going to do recipe
building and brewing sessions and
calculations, that's all going to be
good stuff for DDD. The crud mud side of
things might be the inventory because
it's just, you know, it's basically
read, update, and delete the beer menu.
You're just adjusting prices and things
like that. It's probably not very
important to the business um to, you
know, have good modeling and good
practices in place.
So, who is responsible um for this this
software being good? You know, who's
going to be responsible for this? Well,
it's both you and the domain experts.
you know, Lord Business here or, you
know, as we call them, the the product
owners, the domain experts, you know, so
in the brewing case, you're going to be
talking to the brew master. You're gonna
be building a software for him or her.
That's the person whose problems you're
trying to solve.
Now, you're going to have to work with
that person because, you know, maybe he
or she is not, you know, a big software
developer, doesn't know what's possible.
You guys are all very intelligent. Very
intelligent. And you know what is
technically feasible in software. They
they don't know what's possible both
from Yeah. that that's completely
impossible. We could never do that to
yeah, that's going to take me 10
minutes. And they're absolutely amazed.
You know, they have they have no clue.
They're not technology experts. And you
also use the internet a lot. So, you see
um you know, more software, great
software, you kind of know what software
you like to use, what is good software,
what is bad software.
But you know when you're talking to
these um these domain experts and I like
this tweet a lot and it says you know
what the biggest strategy in software
engineering um you know talking crud uh
to the people you're you're meeting with
um we open the blue book which I
mentioned was the you know the first
book like in page 24 of a it's 500 pages
it's roughly 500 page book so page 24
you open basically right up after the
preamble the first topic that comes up
is ubiquitous language and ubiquitous
language is the ultimate power of domain
driven design. That's if you leave here
today, there's going to be like two big
things that I'm going to try to, you
know, focus on. Ubiquitous language is
probably the number one. It's not
necessarily the most important, but it's
probably the most important. Um, this
ubiquitous language is a shared language
that your development team and the
domain experts agree to speak. Everyone
on the same page, same word. What a word
means means the same um to them and you.
Um the power of DDD here is
understanding the business mindset, the
language and solutions, understanding
how the business implements the
solutions to its problems. So like I
mentioned that process of brewing beer.
If you don't understand that process,
you're not going to be able to write
software that builds that. You know, if
you're just editing fields in a
database, you're not actually building
software that's going to solve problems.
Um you know, some examples of language
that you're going to try to share with a
brewer. You know, brewers call them
sessions or session beers. We have
session database sessions, things like
that. You know, user sessions and
cookies. You know, if we start saying
sessions and they start saying sessions
and we're not saying the same word, it's
going to be confusing later. Um,
you know, this this ubiquitous language
again, it's one of those things that,
you know, as tools change, when you
switch from PHP to JavaScript, stuff
like that, this will stick around. This
is lifetime guarantee stuff. I mean,
once you learn how to use this and apply
it effectively, um, you know, it's going
to be a skill that you can keep forever.
Um you know as von Vernon says in the
red book DDD is primarily about
discussing listening understanding
um it's about discovery you know finding
out what what is going on in the problem
and creating in your codebase a
centralized knowledge uh for the
business. So your code should accurately
reflect the business's problems and the
solutions to those problems and you're
going to see speak the same language. So
everything that's in the you know how
the business works should be in your
code. you know, it should be a one to
one mapping. Um, you know, so the what
I'm saying kind of is that domain driven
design in the ubiquitous language should
be speaking an idiomatic language. So
whatever the business calls something is
what you should be calling something.
Um, I'm guilty of this too, but you
know, you have a you have a user table
probably in your database somewhere. So
you have a user object. Well, they don't
call them users. The only people who
call things users are drug dealers and
developers.
you know,
they refer to people as brewers or
employees or tasters or customers. So,
you know, is it really that much hard
effort on your part to rename the class
to brewer? I mean, it's not that big of
a deal. Um, so when you go to talk to
them, you're saying the same words that
they're saying. I mean, it's it's so
much simpler. Um, guys, I'm sure aware
aware of David Letterman. He has, you
know, the top 10 lists. Um this is the
binary version of the top you know two
reasons that programming is hard is
naming things cache invalidation and off
by one errors.
So the end result of this is that um the
naming things should be really easy like
you should never have that problem
again. If you understand the business's
problem you talk to them and you share
the same language they they are pretty
much going to name the things for you.
You'll you know be able to apply this
directly to your code.
So, I'm not picking on Laravel at all
here. Um, they're fine. But, you know,
most of us developers have at one point
probably written code like this where
you're just pulling stuff off of the
database. Um, you know, find or fail
where name equals something. Take one
get recipe hops array contains hop ID.
If, you know, we have this if check in
this if not right here, this exclamation
point. Hops array attach hop ID and then
maybe some data. And then you have to
save. Now, imagine if you showed a
brewer this code. You know, their
reaction would probably be, "Huh? What?"
You know, you don't um take a hop
off the hop repo. You know, they go and
they get it out of the grain out of the
storage, the cooler. They don't take it
and then get it. Like, that doesn't make
any sense. Um, you know, the recipe
doesn't have a hops array. That's that's
not something that means something to
them. They don't attach a hop to a
recipe. that's that's not how they're
thinking. But for some reason, we've
written code like this and that's just
how we've siloed ourselves in the world.
We've completely ignored how everybody
else thinks.
So kind of, you know, and this is like
it's going to seem to you completely um
simple because it is,
but when they say to you, we need to be
able to add a measured amount of grain
to a recipe, you should have a method on
your recipe object called add grains.
Sorry, pushed the wrong button. You
should have a method called add grains
or add grain.
You know, we need to be able to
calculate an estimated uh that's alcohol
by volume for a recipe. They'll say ABV.
So, you know, if you need to kind of not
remember, you can't remember what ABV is
all the time. Maybe they have some, you
know, specific words that they use, put
those in a method together. You know,
recipe, get estimated alcohol by volume
ABV. You know, put the words that
they're using in your methods and in
your objects.
you know, we need to know how much
winter wheat is in our storage silo
number four. Well, we'll go to the sto,
you know, you can say new storage
facilities. Okay, that's, you know, we
have to do that kind of stuff because
that's how peachy works. We have to have
some, you know, constructs and things
like that. But they could probably
understand this. You know, you're going
to you look at the storage facilities
and find the storage facility by name,
silo number four, and then storage, how
much grain of type, new grain type,
winter wheat. So, you know, they can
kind of get this. I think it's starting
to get more technical, but it's still
much more understandable with somebody
just looking at this.
You know, things get much more
complicated, you know, in this kind of
brewing software and I'm sure in your
software packages, you know, we need to
add 02 ounces of a 12 ounce 12% alpha
acid per gallon of Simco hops to recipes
at a 7day after the boil is complete.
And that's called dry hopping. So, um,
you know, the bottom here kind of
showing you as things get more
complicated, but it's still readable.
If you're probably already, you know,
your brain's already thinking, but um
can you imagine this is not very
difficult code to write. Like there's
not much change between what I showed
you before and what I'm showing you now,
but it's all in the naming and how
you're going to apply things. And later
when you're like they're like, "Oh, we
want to see how that dry hopping thing
works in the software." You go, "Oh,
yeah. Let me find the dry hop method."
You know, search your whole entire
codebase for dry hop. It's all going to
come up.
Oh, by the way, if if there's anything
you're not understanding, on the back of
that packet, I have a glossery. So like
the last page if I'm using words in
brewing industry that you know like dry
hopping that's that's adding uh hops in
after the fermentation. So boil is
already done. Beer's almost ready to
drink. You're adding the hops in right
before it's uh consumed. So um but you
know maybe if you're using be hot or
girkin languages and things like that
you're already doing this but you don't
realize it. You're putting these in your
scenarios and features. You know a user
should be able to dry hop a recipe. User
adds dry hops. You know right there is
where you know that's your method names.
you're not realizing it, things like
that. But if you're not, you know,
you're just applying this, you know, you
know, if you go in here and you, you
know, in BOD, if you like tell it to
generate a step definition, it's
actually going to generate a step
definition like a user should add, you
know, dry hopping, you know, whatever.
And that's exactly what the method
should be, you know, on your objects
later.
And as I mentioned, this ubiquitous
language, it's something that you both
agree u to speak. So they call it dry
hop. You know, you're going to have a
method called dry hop. Maybe they have
they're like, "Oh, we just, you know,
get a bunch of ingredients. We throw
them together. So you have your
ingredient, you know, uh adding it to a
boil. We add we added it to a boil
kettle, you know, it's just different
ways that you know, some of you know the
people that you're working with talk,
you know, and what they speak, maybe
where you're from, you know, what
country, you know, maybe you wanted to
use some of the words that are local,
but understand their language, speak the
same language and apply it directly to
your code. And, you know, we're going to
get into um some of like building
business rules and things like that
using, you know, this language. So like
the last one, you know, we could have a
recipe add to mash ton and there's no
reason you should put be putting hops in
that part. It's just the grain. You
know, we're going to build some of these
log this logic in here to you know, fail
on things like that.
So you know, you're going to have um you
know, this wouldn't be all all one
method, but you know, just to kind of
you know, summarize the whole thing
about naming your stuff. Um, you know,
for the most part, a brewer should be
able to understand this and figure out
what is going on. Even if they're not a
software developer, it pretty much reads
like English. And, you know, when you're
talking about them about the software,
um, I don't know how many do does
anybody here speak a foreign language?
Like, does anybody learn Spanish or
German? So, I learned German. And when I
first started learning the language um
you know you have to think about what is
the German word for tree you know it's
it's bal town and bal that means you
know whatever you so you have to think
about the word what is tree eventually
as you get better at speaking German you
stop thinking about what is the English
word for this thing now it's just like
in your software if you're speaking the
same language as the person that you're
developing software for. I don't have to
think, oh, you know, the the hops uh
array is actually the number of hops in
the recipe. Like, I don't have to do
this translation in my head. You're
speaking the same language. It's going
to flow easier and be much easier to
understand later.
So, if if you're comparing, you know,
what is a CredMod app versus DDD? um
kind of these objects and this naming
and stuff like that. You know, your
Cudmud app is going to have users and
strings and integers and floats and
arrays of OM objects and things like
beer inventory repository interface. Um
DDD app, you know, you should expect to
see objects called brewer, like a brewer
or brewers, like a collection of brewers
would be, you know, brewers. You're
gonna have an object called recipe name
that represents the name of the recipe.
Things like IBUS, which are how bitter a
beer is, um alpha acids, things like
that. You're gonna expect to see these
objects.
um in a thing in a in the in the
software. Now, one last topic on the
ubiquitous language before we start
getting into you know actually doing an
exercise is the um natural
identification of things in your
software. Now hopefully if you're not
using UU ids as your database I you know
definitely recommend
stop start using UU IDs for all your
primary keys on your database. make your
lives so much easier uh as you're
developing software. But those UU IDs,
as I'm sure you're aware, are big j long
giant strings that don't mean anything
to anybody else other than us. So those
are our primary keys of our database.
Most of what you're developing software
for probably already has natural
identification built in. So if you're,
you know, building software for a
library and books, you know, there's
already an ISBN number on there. You
don't have to use that necessarily as
your database primary key, but all of
your lookups and things like that should
be done based on the natural
identification. Um, it's going to make
understanding your software later a lot
easier. Now, you're still going to have
queries that are by ID, you know, in
controllers and things like that, but
when you're actually building uh
business logic and looking for things,
try to use natural identifiers. So, if
we're looking at a bag of hops, which I
brought some bags and ingredients and
stuff like that for you guys to look at
later, you know, there's already some
natural identifiers on here. They've
already got a lot number here in the
bottom of the package. So you can
identify off of that. The name is a, you
know, a unique name. Centennial hops is
a unique, you know, flavor of hops. So
you know, look at your business and,
you know, when you're when you're
finding things out of the repository for
people, you know, you can find it by
name by, you know, the string that you
take in or things like that. You don't
have to use UIDs everywhere in your
software.
All right. To sum up all of this, and
this is kind of an older book, structure
of inter uh interpretation of computer
programs. One I was probably supposed to
read in my computer engineering
uh one of my classes at Penn State, but
you know, I went to a state school, so I
I learned more about beer than I did
programming probably. Um but the most
important thing, and this is a great
quote, is programs must be written for
people to read and only incidentally for
machines to execute. I've been at the
same place that I worked for for 10
years, and I looked back at the code
that I wrote
last week. No, I mean seven, eight years
ago and it's just it's it's almost
garbage. It's pretty much throw away
because it's all ORM based. It's
database driven. Modifying any of that
is terrifying. Like it some most of it
doesn't have tests because that was
before we started testing everything. Um
but you know to try to make a change it
it could take you you know a week to
make a very very small change because
nothing is encapsulated. Everything is
just, you know, scripts everywhere and
doing updates and reads and deletes and,
you know, you delete one thing one
someplace and you don't make the updates
in the other and stuff gets haywire that
bad things happen. You know, I'm going
to try to push you guys into a place
where um, you know, building software
that's, you know, better and, you know,
it's more correct. It's going to stay
correct longer and be, you know, easier
to change and things like that. All
right, real quick. you know, as the
business evolves, um, you know, the
language evolves, too. Um, so, you know,
if they decide they're going to start
changing the names of things, um, change
them in your software. You know, move
along with the business. Don't, you
know, you can't just, you know, stay,
you know, stuck in the the,
you know, 20 years ago. You know, we
don't we don't brew like we brewed in
the 1600s. Um, because, you know, people
got sick all the time of stuff like that
from infections. Um, but oddly enough, u
beer is very, very safe to drink. almost
cannot be poisoned um from beer. The
only thing you have to worry about
really is um
what is it? Uh whatever. It's a very
minor infection. It's in none of my
beer. Uh I should have wrote it in my
notes here. Um but anyway, uh you know,
beer, the alcohol in beer kills almost
everything. So, it's very safe to drink.
Um so, even you know, back in the 1600s,
that's why people drank beer so much
because it was, you know, safe. The
water was terrible. Beer was still safe
to drink. But processes have changed.
You know, we have giant stainless steel
fermenters now. We don't they don't use
barrels, you know, from the 1600s to
make their beer still. There probably
are places that do, but most breweries
don't. So, evolve with your company.
Evolve, you know, with technology. You
know, you're you're going to see things
in the technology side of things that's
going to change how the business is
going to operate. It's your duty to go
to the business experts or the domain
experts and update the ubiquitous
language there, too. You know, if you're
operating a theater, you're not going to
have a ticket counter forever. You know,
now there's online ordering, things like
that. You have to go to the business and
they're going to they're going to have
to change, too. You know, that's
ubiquitous language. It's a two-way
street. It's not one way them telling
you what to do. It's, you know, both
ways.
All right. This is I promise the last
thing about ubiquitous language. And
when I mean ubiquitous, it's not across
the entire universe. It's not, you know,
what they call a hop may not be the same
thing across the entire universe. It's
not even across the entire organization.
It could just be that one project that
you're working on with one team in the
organization. So, you know, if you have
many u parts of your brewery or
whatever, marketing and finance and
production and inventory, what they
refer to as a hop may be something
completely different. So, for marketing,
it's a you know, it's a flavor. It's
they're going to market it as something
that's flavorful and things like that.
For finance, it's an expenditure. You
know, it's just something we have to pay
for it. It's awful. You know, for
production, that's an ingredient. You
know, they're building, they're making
the beer. They're, you know, they're
dumping it in. For the inventory people,
it's just, you know, a quantity of a
thing at a location. That's all they
care about. So, you know, when you're
building the inventory system, you're
going to refer to those hops
differently. You're going to have
different methods and you're going to
namespace like PHP has now, which is
great. You can namespace those hop
things into the inventory namespace. So,
you're going to separate your
application either through namespacing
or if you're big enough maybe
microservices. Not something I would
recommend you run out and try to do as
microservices. It gets very complex. But
even if you have what you know they call
the majestic monolith big giant
application that has like I do you know
we have I don't go I don't know how many
classes we have in lines it's huge but
we still break it down you you still
have to solve one little problem at a
time so when you go to talk to the
inventory people you're talking about
hops you know you might still be
referencing databases that are similar
that are used shared across data that's
shared across but you're going to build
separate code base you're going to build
stuff to their language that it's
understandable by them and
understandable you and you're speaking
the same language at the
Because if you're like me and I've you
know I've been there for 10 years like I
said I built software for somebody five
years ago and then five years now we
come and we look at it and try to
understand it again it's going to be a
lot easier if it's written in this
language.
What I'm talking about in domain driven
design is they call it bounded context.
So, like I said, we're going to
separate, we're going to silo our code,
into the recipe section, into the
inventory section, the brew house
section, because in what they call the
big ball of mud, that's the CRUD app
that's just got data and updates and
sets and everything everywhere going on
that's totally incomprehensible or
spaghetti code or shanty town code,
however you want to look at it. Um, you
know, that that's what you end up with
if you don't keep things separate. Um
the way that I recommend that you do if
you're when you're going to separate
your code and you know obviously you
know the brew house is going to have to
talk to the inventory system. You know
if you do that spaghetti code you got
stuff you know flying everywhere objects
calling methods and other sections and
stuff like that. If you do that and you
go to update your system it's not going
to be modifiable because you want to
change say you know it's been five years
and you haven't touched the inventory
system you want to make an update. Well
if you've got all these methods that are
being called in the brew house from the
inventory system now you have to touch
both code bases.
Keep everything siloed, keep everything
separate. And the way to do that is to
pass messages between the different
domains. And you know, you can do that
however you you know, you feel like you
use, you know, if you're using Symphony,
use the event publisher, build your own
queue, you know, do what do whatever you
know, your implementation, you know,
makes it easiest for you to do. But
you're going to pass these as messages
or events. You're going to publish an
event and let for people, you know,
listen. And, you know, in DDD, we call
this a a domain event, you know. So,
we're going to pass these domain things
that happen in say in inventory, you
know, uh uh ingredients received. You
know, it's going to fire up an event and
now the brew house knows, hey, the
ingredients have been received. We can
start brewing. So, we're going to pass
this message of ingredient that
ingredients were received. Brew House is
going to pick that up and then move on
to its next thing. All right,
we're just about ready to stop talking
and start doing, but I wanted to stop
real quick. Does anybody have any
questions uh about the ubiquitous
language and what we've been talking
about? We're going to start how do you
actually you know work with a domain
expert and build this ubicous language
through an exercise. But does anybody
have any questions before we do that?
>> Yeah. Um, so what what you want to do
there, um, so if you're trying to build
like a generic app that works for
multiple people, is that what you're
saying? Like, so you have like let's
just say it's a purchasing app and you
have three companies that want to use
it, something like that, right? Um, what
you'll typically want to do is build
your interfaces like class interfaces in
the domain language. So what you
understand is the domain, your
understanding of the domain. Those will
be your interfaces. So when you're
applying or using those interfaces when
I if I don't know experience level here
of classes but you have you know you
have classes which are concrete objects
you have interfaces which just define
what a what a class can do. So you want
to build those interfaces in your domain
language. Then you'll probably have to
build custom classes that you know
implement things differently but
implement that interface so you can kind
of keep things separate and you actually
you know namespace those implementations
per the client name. They're that
specific. Um, we have things in our
company where certain rules are for only
certain regions in the company. So, you
know, we want to put that in there
because you want to know why the heck is
this doing this? Well, it's because they
asked us to do it, you know. So, we know
who asked us kind of thing. So,
>> yeah,
pretty much.
Yeah. Um, definitely naming things like
that. But what I'm saying is like if you
have an app where like they're they're
typing in uh let's say like in a brewery
they have a QA program where they just
record values like oh how much
carbonation was in this? How much acid,
you know, what was the pH? Things like
that. You don't really have to build
that domain driven design. You can let
Laravel or Symphony or Doctrine generate
forms for you generate stuff. You know,
they type it in, they hit save. There's
not really any business logic there.
It's just recording. So that would be an
instance where don't it's a little bit
more overhead to do DDD stuff because
you're going to spend more time
modeling. You're going to spend time you
know figuring out the language and stuff
like that. If it's not it doesn't matter
just do it. I mean it's a balance.
>> Yeah.
same thing.
>> Yeah. I mean, I look at, you know, cons
like p publishing versus subscribing. Uh
like kind of like what you're saying,
like you're you're pushing to an API. um
that that is code that you're writing
and you're you're responsible for
testing that code when you're consuming
APIs and things like that. Um publishing
an event out to the system kind of like
you know wiping your hands clean and
walking away um from it. So it tends to
if you have very separate systems it
tends to work better for things that are
happening that other systems need to
respond to. Now again like you're saying
you may have an API to the inventory
system or a service that can you know if
the brew house needs information from
the inventory system. Yeah. Build what
they call um so I'll get into this
hopefully later hexogonal architecture.
And it talks about what they call ports
and adapters. And really what you want
to do is you have a very simple
interface that's between the inventory
system and the brew house. So your
methods very clear. It's kind an API or
you know again it's a microser you're
going to separate that into a completely
different web server. But you want to
have these very tiny classes that
transfer those messages across just like
publishing an event. So you know you can
still talk to other domains but it's
done through a very restricted channel
so that you know when one changes and
the other needs to be updated you don't
have to update both sets code bases. You
just have to update the adapter.
Okay.
No, I haven't because I'm usually I
would usually defer to the business
because it's their problem you're
solving. I mean, it's, you know, you can
convince them otherwise.
All right. Um,
any other questions? I'm going to pass
out some treats. There's a tradition in
the brewing industry for uh these nut
rolls. So you guys that ask questions,
you get a anybody allergic to peanuts
that I just asked a question.
Yeah. Yeah. No, too late. I'll have
extras at the end. All right. So, we got
a few minutes before the the first the
break here.
Um what we're going to build the
ubiquitous language through is an
exercise. It's called eventtorming. And
uh this was done by Albert created by
Alberto Randolini.
Um but when you you're building this
ubiquitous language and this this thing
you want to go out and actually meet
with the domain experts. You know having
somebody spec pass you the specs is not
the way to build software. You know a
million tickets in Jira and have a
burndown chart is not the way to build
proper software. Um this is me out on
one of our ships you know meeting with
the crews and stuff learning how uh to
build software. UX professionals, they
call this ethnographic research. It's
fancy word for saying get yourself out
from behind your fancy desk that goes up
and down and get out there and learn.
You know, in a brewery, that's that's
actually probably pretty fun, but on a
ship, it's just smelly and dangerous.
So,
and it makes you seasick. Um, you know,
you'll hear this thing about user
experience talking about personas. You
know, don't worry about personas. You
shouldn't be thinking about personas.
You know, I love this example of
personas. It's like, oh, you know, we're
going to build software for a brewer and
he loves poetry or she has a library
science degree and just got a job at the
brewery. No, you know what? Actually,
these are all the same people. Um,
they're just all in different makeup and
disguises. Um, yeah, that that persona
doesn't matter quite as much. It's kind
of something that's been created by UX
professionals so that developers don't
have to talk to the clients. I think um
you know, a lot of times like you build
these personas, you end up building
either a racist or a stereotypical piece
of software. You know, I I love this
example here, but you know, it's a
computer, but why does it have to be
pink? Because it's for a girl. It
doesn't doesn't make any sense. Or
you're building software for women. It
doesn't need to be pink. You know, um I
read a great quote. It's like, please
remove age, gender, ethnicity, locations
from personas. None of that makes any
difference. As these guys that have nut
rolls, no, it didn't matter where you
came from, what your age is, what your
experience is. You're hungry and you
wanted a nut roll. That's why you wanted
to ask that question.
Solve the problem that you have. I mean
there's a great book that's user
experience that's uh talking about user
research and
uh you go read that about getting in the
mental model of the person when you're
meeting with a stakeholder and building
ubiquitous language you know talked
about the five wise you know this
understanding the business problem when
we do this exercise I don't want anybody
to be to using what I call the seven
dirty words of meeting with a domain
expert that's just like George Carlin
words but these are worse session
repository abstract interface class
database foreign key those are really
words that nobody else understands
except for all of us. Don't use those
words. All right. So, what you're going
to do is you're going to get your your
sticky pads out and your pens and
markers. And we're not going to build
any kind of user interfaces yet. We're
just going to try to understand the
problem. And the reason you use sticky
pads, markers, and pens is that not
everybody else is as good as using
computers as we are. You know, they're
maybe not as technically inclined. You
know, I had a meeting one time. I was
scrolling through software so fast. The
guy said, "Hey, you're making my eyes
roll back in my head. I can't read. you
know, as fast as you can. So, we're
going to get low tech here. We're going
to use sticky pads and pens, markers,
and stuff like that. And we're gonna
build uh a model of the problem that
we're trying to solve. And you have
colored things um and your your little
stickies there. We're just going to
create like basically a legend of what
things are. We have a domain event,
which I'm going to explain, a command, a
model, and an actor in your software. A
domain event is something that happens
in the software. It's past tense. um
like beer was brewed, grain was added,
hops were added, brewer changed email
address, things like that, things that
happened um you know or will have
happened in the past once the software
is working and you just start building a
chain of these events um as you go
through. So recipe was created, the
grain bill was added to the software,
you start building a huge chain of
events. Eventually the chain gets so
long you realize, oh, we're talking
about recipes and brewing. That's where
you can start identifying your about in
context when things get too far and too
big and complex. So, you know, we split
it down. We just look at maybe we'll
just look at the the recipe context
here. Um, so these are the things you're
starting to get about building a recipe.
You're going to be adding grain, adding
yeast, adding hops. You know, as you you
know, go through the I'm just going to
speed through this here, but um you
know, you look through the um the
different events that happen in the
system. So, I'm done talking promise for
a while. Why this is not going forward?
Yeah. All right. Promise done talking.
Um, you don't have to listen to anymore.
We're going to take a little quick
break. Let's, you know, try to give it
like five minutes or so. Get your
coffee, use the bathroom, whatever. Uh,
come back and we're going to start
working on this uh uh event storming.
When you go back to your companies and
and do this, I'm going to do the same
thing. um recommend that you know as you
start the session you put up the first
orange cards which are the events that
have happened in the system. You write
the first ones get the ball rolling. You
may not be right that's fine but at
least if everybody's just sitting there
staring at each other they're not going
to know what to do. So I've created um
the first four things in your um you
know if you're looking at this diagram
that I gave you earlier. These are the
first four things that happens. You know
brewery plans a session they make a
yeast starter which you don't have to
worry about too much. You know they
measure the grain and then the grain
they mill. That's the first one of the
first things there is the grain is mil
here and then it's put in the mesh mesh
in. So those are the events that we're
going to talk about and you guys are
going to come when you get back we'll
we'll start working on that. So soon as
you get back just start working
I'll be around to answer questions.
reason, you know, we're writing these in
the past tense and present tense is um
that's how they're going to translate to
the code. Other than that, there's not
really a really good reason. So,
examples of foods, you know, if you're
looking at the the brewing stuff, it's
like a brew session was started. You
know, start a brew session, you know,
add grain to recipe, add hops to
ferment, those are the things you're
going to be adding. Now, um so, you
know, recipe was created, your command
is going to be called create recipe. You
know, the grain bill was added, the
command is going to be called add grain.
you know, water profile is selected.
However you guys have, you know, kind of
come up with this, you know, you you
guys are your own domain experts. So,
however you did it is correct. So, go
ahead, take the blue stickies, translate
your event into a command. Should take
you just a couple minutes.
Can keep adding your commands if you
need to. But, um, moving on, the next
thing is the models. So now is when you
can kick in your developer brain a
little bit and start thinking about the
objects and stuff that you're going to
be relating to. So this is the things
that the commands are done upon and that
might be both you know in your case the
um the model that you're looking at like
the recipe or the ingredient or the
brewer. It might be related stuff you
know so if you're doing the recipe
context create recipe that deals with
the recipe adding grain that might be
the recipe and the grain kind of you're
just trying to figure out what's all
involved. So take a couple minutes and
work that out.
You don't need to write grain 17 times
if you can remember. You know,
it's not necessarily the data. It is the
the things that you're acting upon. So,
you know, you're you're interfacing with
a brew kettle that could be on there.
You know, your objects that it's the
things in the world that your software
is trying to model. Don't don't worry
about okay, we need to know for grain.
We need to know its name and its weight
and its things like that. Don't worry
about that kind of stuff.
Okay, so I think you guys are you're far
enough long on that one. Um the last
thing and we're kind of actually done
because the la the last one is the actor
and that is you know who did the
command, who did the action. Um, in this
case, you know, we probably have like
one person, the brew master or the
brewer. So, it's probably going to have,
you know, the same person on every task.
So, I'm not going to make you um, you
know, put green sticky on each one. Um,
but, you know, I do have to congratulate
you guys. Guys, diagrams are pretty
pretty pretty good. Um, they're
terrible. No, I'm just kidding. Um, so
you covers kind of, you know, building
the um ubiquitous language.
Um, does anybody have any, you know,
questions about the process? The only
thing I I would say that I've noticed,
you guys did a good job naming all your
stuff. Um, some of the things you're
going to have problems with in code. So,
if you called something grain and then
you had an object that's called mil
grain. Now, that's fine when you're
building this, but you're going to have
a problem. You're going to have problems
transitioning objects with PHP. It's
difficult unless you separate them into
different bounded contexts. When you you
have an object called grain and you have
a function called mill, you can't change
that type of object. So it can't
translate into a new kind of object. You
have to create another object called mil
grain gets a little complex. So
sometimes you may want to store the
state of the grain as a you know a
property of that object not having a new
object called mil grain. But you know it
might be important enough that maybe
they're dealing in one section with just
milk grain. So you may have an object
called milk grain. It's possible. Does
anybody have any questions about this?
Yeah. Uh sure. It so it's basically the
who did it. So, um, later when we look
at the command, it's going to be the
person who, you know, punched the button
on the software that said add grain. You
know, the grain was added. It's going to
be tied to the event. You know, they're
going to they're going to be a model in
your in your somewhere in your software.
You're going to have an object called
brewer. You know, that's going to be the
person logging into your software and
using it. So, you know, they're they're
not exactly a bounded context. They're
not really part of any of uh one
specific particular bounded context.
what's called a supporting valid
context. So they're going to show up
everywhere in your software. There's not
really a way to avoid that. Um other
than you can if you know it's complex
enough, you may want to create a
inventory user interface or something
like that. So that way you have a
limited set of functions on the user in
that um area of the software. That may
help keep things a little bit better
organized. Or if you have very distinct
teams that are working on different
parts of the software, you're more
likely to do that where you have your
user which is like a symphony user
Laravel user and that might end up
implementing like 15 different
interfaces from different namespaces.
That answer your question.
Yeah.
>> Yeah.
Event storming. Event storming. Yeah. Um
there's other ways you can do it and and
I'll kind of touch at those real quick.
Um you know, you guys started building
diagrams. You can see they're already
getting kind of large. Um you're going
to want to break those down into bounded
context like I mentioned as the thing
gets larger. Um you know, you start
circling them off. If you're doing it,
do this on a whiteboard or a big giant
long sheet of paper so you can you start
doing it over and separate the stuff
out. Um, you know, these, like I
mentioned, these events are going to be
pushed around to the different contexts.
So, like the inventory context needs to
know when a brew session is planned to
make sure the ingredients are there or
something like that. You know, we're
going to order the grain form
automatically or something like that.
And it might be, you know, multiple
events from the same bound context that
are coming to it. Um, if you're really
having problems understanding the
process, the business process and stuff
like that, um, there's a couple other
techniques you can use from user
experience side of things. Uh, one's
called business origami and um, learned
about this at 2011 uh, in 2011 at UX
week. Um, there's a handsome guy in the
background there,
but basically you make these fancy
little cards uh, and you actually
physically um, let me jump ahead a
little bit here. You'll see. But like if
you're building like an e-commerce
thing, you actually physically move the
people around the um, you know, origami
uh, store so to speak buying things. So
that way you can identify which cards to
to write on the process. So it might be
um useful to actually build a physical
model of the real world in paper or
something like that in order to figure
out oh what cards you know do we to have
in our um event storming session.
There's other ways to do this too like
they call it user story mapping and it's
kind of the same thing. You build like a
big long list of um events and then you
try to find you know the different
things that you're going to do and break
it up into you know planned releases and
stuff like that. But I you know I think
it's the idea here is that we're going
to identify the behavior first. You know
people think about software they don't
think about database models like I was
mentioning. They think about what does
the software need to do for me. So
they're thinking that you know I need to
be able to add grain to this. I need to
be able to you know do things. Those are
the events. So starting with the events
and starting with the things that have
to happen in the software is a much
better way to to build software other
than starting with the databases and the
data. Um so I would give this you know a
correctness and usability much higher as
compared to you know CRUD code.
All right. So, we're going to start
talking about actual code.
How do you implement this ubiquitous
language and how do you, you know, make
software that is maybe completely
different from how you guys are writing
it today. And um you what I mentioned
earlier is going to be heavy on the
object-oriented uh side of things. Um
but it's hopefully not too difficult to
understand. In domain driven design, we
have pretty much four basic uh kinds of
entities and objects in our system. We
have value objects which are going to
represent um small things in our system,
pieces of information. Domain events,
those are the orange cards that you put
in your event storming uh session there.
Those going to translate exactly to
objects in your system. Entities, those
are your yellow ones, those are the
models. Um you know that you had a you
know you have a grain model that's going
to become an entity. Aggregates um those
are your fancy um models and I'll get
into that. They basically contain child
entities. So
um you know a value object like I
mentioned is the smallest pieces of your
software. Um biggest thing about them is
they're going to be immutable. Um they
have no identity. I'll get into that.
But examples of value objects in the
brewing thing is like a hop name, an
amount paid, a temperature, things like
that. They're they're values. They're
not um things that have uh things that
are stangeable about them. Entities are
things that are identifiable and they're
mutable and they have a life cycle and
they're going to operate on those
smaller value objects and I'll get into
that later. But an example of models
might be a line item in a in invoice.
You know, you could change the quantity
or change the price. You know, that that
that could change over time or you could
delete the line item. So that has a you
know life cycle. Brewers, the names
change. They come and they go, they're
fired. They're hired. Water chemistry,
you know, you might have a model for
your your water chemistry. Well, your
city water changes, you know, daily. So,
if you're measuring it, you're going to
be constantly updating your your water
supply.
An aggregate. So, an aggregate is
basically a fancy entity, a bigger
entity, and it is responsible for
managing smaller entities, and it's
primary job. It's going to have business
logic for sure. Um, but it's going to be
the um larger, more identifiable objects
in your thing. So if you had um things
on your uh eventtorming session like
brew session um you know that's going to
be controlling some things as far as
what state we are in the process and
stuff like that. the recipe recipe is
gonna if you flip to the the recipe that
I gave you in the packet there it has
hops it has grains it has yeast you know
it's got to contain all these other
models so it's got to manage and it's
got to hold them all together to build
one giant thing called a recipe that's
an aggregate
but if of all those things value objects
are by far the most important
so um the value objects again we were
talking about out of kind of this the
brewing You might have an email address
for a brewer. I don't know. That's kind
of an easy example. A recipe name. Um
the date we brewed the beer on. That's
an example of a value in the system that
is immutable. You can't change when we
brewed it on. The weight of grain you
might have, you know, that's a 13 pounds
is 13 pounds. You can't you can change,
you know, how much grain you've weighed
from 13 to 15 pounds, but the value of
13 pounds does not change. It's that is
13 pounds. You know, 70 degrees uh
Fahrenheit is 70 degrees Fahrenheit.
There's no changing that. If you change
to 71 degrees, that's as a new
temperature.
And all these value objects in the
system are going to come directly from
the ubiquitous language that you know
when somebody if they call it, you know,
uh the date we brewed on, yeah, you
might want to shorten that to brood on
in your code or something like that. But
if they call it a recipe name, it's a
recipe name in your code.
And we're going to have classes that map
directly to um the classes or we're
going to have classes that map that you
look at this language directly to the
objects in our software. So if you're
talking about temperature in the brewery
that you're building software for it
does everything in Fahrenheit, well
you're going to have a class called
degrees Fahrenheit that's going to
represent that. Um there's no reason to,
you know, build it out in Kelvin. There
just brewers don't talk in Kelvin. They
don't, you know, think about in, you
know, Kelvin or even maybe degrees
Celsius depending where they're from. So
have a degrees Fahrenheit class because
that's how they're going to class in
your software because that's how they're
going to have to put information in.
They're they're going to want to use
degrees Fahrenheit and everything.
They're not going to have to convert.
And you know these objects are not just
about creating uh classes that do static
typing, meaning type hitting in your
functions. That's not the point of them.
Um there's studies that have shown that
you know just the variable names is
enough to keep you from swapping
parameters. That's not the whole point.
Um the point of value objects is to
reduce complexity and reduce um checking
in your software. So value objects I
mentioned are immutable meaning they do
not change. Um there's no in there's no
factor of time involved in a in a value
object. You know the temperature doesn't
change over time. 70 degrees Fahrenheit
is 7 degrees Fahrenheit regardless of
when it is. The other reason um you want
to have as many objects in your system
uh immutable in your software is because
PHP passes objects by reference and if
you pass an object to a function that is
mutable. So um that function could
change something on that object and you
don't know that. So when that object
that you pass in could be changed. Now
if it's an immutable object like a value
object and you pass that to that
function you don't have to worry about
that object being changed. So if I pass
70 degrees Fahrenheit to a function, I
don't have to worry about it coming back
as 68 degrees. It's not possible. That
object is locked and it's immutable.
There's no possible way to do it.
The other way um the other thing that
helps you in your software here is your
your value objects will always be in a
valid state. Now they don't have state,
but what I mean by that is they're
always valid. So 70 degrees Fahrenheit
is a valid temperature. Now if anybody
remembers from high school physics,
there's something called absolute zero.
you know, you can't be below what 273
degrees Celsius. You know, 300 degrees
negative Celsius is not a valid
temperature. So, we're going to prevent
that uh in this uh value object class.
So, anywhere you deal in temperatures,
you know, it's always a valid
temperature.
Value objects become like the thin candy
shell uh in your application where
everything stops and keeps everything
from hitting the nice um creamy center
in the middle of your application. the
earlier you can solve problems in value
objects um the easier you're going to
have time uh building software
if you're not doing software uh you're
not doing test-driven development now
value objects are a gateway drug to test
driven development they um are by far
the easiest to develop doing test driven
methods
#my tests don't pass
so what are value objects they're like I
mentioned they're just small little
classes that have one purpose they're
plain PHP objects
Um they don't necessarily have to
inherit from anything or be part of
something else. They're called call them
popos. I mean a plain old PHP object. Um
they're all their properties are
private. There's no public way to change
anything about them. Um there's no
setters on them. Like I said, you can't
change anything about them. They're
mutable. U they don't hold references to
mutable objects. Um, so you know like uh
a grain for example, you can't have that
as a value object because the grain
could actually change like different
acids and stuff like that or enzymes
could be different in the grain. So you
can't hold a value object and have an
entity inside of it that can change.
It's not possible. That value object if
it's 70 degrees Fahrenheit, like I said,
should be 70 degrees Fahrenheit forever.
There's no change to it. The biggest
difference um in domain driven design
code versus most code is um we're going
to throw as many exceptions as possible
in the construction of these objects.
And exceptions are going to be your
friend. You're going to catch these
exceptions when you need to to react to
it. But for the most part um you know a
lot of your validation is going to come
from throwing exceptions in the
constructor.
And like I said, value objects only
depend on scalers and other value
objects. Um so like I said, you can't
pass an ID to a value object and hold
it. um 70 degrees Fahrenheit. To hold
that, you need maybe a float that's 70
and a unit that's Fahrenheit. So there
might be two values you need to hold.
Those don't change. Um that's all the
dependencies that this this value object
is going to have. Um and they're not
dependencies that you have to inject
either. Um so you know, sure, if you're
following Solid Principles, you know,
dependency injection, things like that,
if you have repositories and services
and stuff like that, you don't want to
just be littering those willy-nilly in
your code. That's that's a bad practice.
Value objects are a little bit
different. Um they're pretty much like
scalar types. Like do you worry about
having integers in your code anywhere or
strings? No, you don't. You don't worry
about that. These are pretty much just
an extension of the scalar types. So
don't worry about saying new temperature
in anywhere in your code. It's it
doesn't have any dependencies. It's
designed that way. Uh it's going to be
perfectly fine to have those new uh
things happening in your code. You may
um you know value object gets very
complex. You may need a factory which
you know if you guys are familiar with
patterns you may need a factory to build
that uh value object sure you might want
to inject that in that case but for the
most part value objects you just put
them wherever you need to put them and
use them. All right enough talking about
value objects let's look at an example
and I put this example in the packet
that I put out just so you guys can see
it a little bit closer. Um but let's
talk about you know the business rules.
Uh we're going to apply those directly
to our code. So the brewers have told us
that yeah we name all our recipes and
they have to be nice creative witty
names um that we put up on the board and
everybody smirks at and they they sniff
it first and drink it. No, I mean so
important thing is to have you know some
business rules associated with this
recipe name. And basically the business
rule is that a recipe name cannot be
empty. So if I if I create a new recipe
name object and I pass an empty value in
it should blow up. So it's going to
throw an exception. you know, if I add
if I pass in a valid name, it should
give it back to me in the getter. So,
it's get value. It's kind of a simple
way of saying, you know, give me back
the value out of this object.
A lot of times I like to do on these
simple value objects, if you can cast
into strings, we'll put a two-string
method in that'll just cast it. So, you
don't have to have get value everywhere
in your code. Uh, can get a little
verbose. Hopefully, those are nice witty
names like iphpa and recursive viton.
So, uh, like I mentioned the recipe
test, I think you guys can see that I
printed it out.
So, um, like I said, we're doing
test-driven development. We've added our
test. Let's run it. And everything fails
because the class doesn't exist. The
method's not found, all of that. So,
let's start, uh, by building that class.
And like I mentioned, the thing that
we're going to do most of the time in
these value objects is throw an
exception in the constructor when things
fail. So, when you're building your
value objects, you're going to try to
identify the failure points for these
value objects. So, if the value that's
passed in is a string, we don't have to
test if it's string because we're using
PHP 7.1. Um, if we're using PHP 5, you
couldn't type hint that string there. If
it was value, so you'd have an
additional check like if uh not is
string in PHP, you would then throw an
exception saying this is not a valid
string. Um, but yeah, I mean, if it's
empty, we throw an invalid recipe name
exception. In this case, I've created an
exception class because maybe you need
to deal with that exception specially.
If you don't need to deal with the
exceptions especially in this case throw
an invalid argument exception which is
in the root you know base name space of
PHP that that's perfectly fine I I do
that in some of the examples and then we
hold the value and
that you know that is enough to pass our
first test in that uh recipe test thing
where it's testing to make sure we pass
an empty string
it fails.
Next thing we have to do is we just add
a get value function and that returns
the value back out of the value object
again. Run the next test pass test
passes and then the uh tworing that's
really easy to implement. You just get
the value back. You could also just wrap
the the get value function. Um you know
internally you could just call that
public function internally. Not a big
deal. Um, I mean, again, I don't I
wouldn't stress too bad or too much
about the value objects and how you
implement them. As long as you're
testing them and you're um, you know,
you're wrapping things in test and
you're identifying the fault points. The
internal code, if you want to, you know,
get fancy with your implementation
there, that's fine. Do whatever you
want. Important thing is you test,
lights are green, tests are clean. It's
in the trap. You know, thing is it's
locked down. So now you know as we look
at this recipe name class anywhere I use
this in the system I'm guaranteed that
the recipe name is valid. So it's kind
of a different way of looking at code is
you no longer ever have to check on this
recipe name throughout everywhere in
your code. Recipe name is now valid.
It's now a valid object. It's not
possible for somebody to create an
invalid recipe name.
Look at another example. Get get a
little bit more advanced. Um so a brewed
on like when was the recipe brewed on?
you know, we write a test for that.
We're just going to create a new
datetime, and it should give us back,
you know, a datetime uh immutable uh
value. So, you know, you just hold it.
Datetime already does the validation for
you. You know, it's going to throw an
exception if it's an invalid date. So,
for the most part, you're done. Um but,
you know, if you want to add some
business rules in there, maybe to catch
edge cases and things like that, you
can. You know, like, oh, you know, it's
not possible because we're writing the
software today for there to be a date
that's less than, you know, 2017
November 15th. It's not possible. the
software didn't exist. So throw an
exception if the date's too far in the
past or you know oh maybe this we know
this software is only going to last a
year so we put it in the future you know
things like that you know it you want to
try to stop anything you identify that
are problems with that uh object in the
constructor
another example of pounds you know um we
can test that value uh like I mentioned
make sure it's a float um you know it's
not really possible to have a negative
weight you know unless we discover
antimatter weight is always positive So
you can treat pounds as you know
positive in your system and throw like
at the bottom one here test negative
throws exception you know it's easy to
check that um you know value is less
than zero uh may not be negative now
because PHP can be screwy sometimes you
want to identify like I said pounds
always must be positive you want to
identify a way that that makes sense to
both you and the business of what this
object is going to represent. PHP has
this nasty thing of a negative zero.
What is negative zero? It doesn't make
any sense. Let's eliminate that from our
domain. Um, you know, so we can just
cast it to a regular zero if we have
negative. That way, if they print it out
and it's zero pounds, it doesn't
accidentally show up as negative 0
pounds and confuse the hell out of them.
You know, your two string in this case,
you know, maybe the breweries decide
that, you know, they don't have to worry
about anything less than, you know,
0.0001 ounce, you know, pounds. Do your
formatting and your value objects. do
your rounding. You know, you might want
to hold the entire value. If it's like a
long float, you may want to hold that.
But when you get the value out or we
display it to them, you handle your
formatting here. Now, that's only for
simple things, uh, like, you know,
pounds and stuff like that. If you have
dates and more complex objects and you
need to format it, again, looking at
solid principles, it's a good idea to
separate your formatterers into another
class or into a template, things like
that. Again, those policies apply.
The the thing I mentioned about
immutable in the value objects doesn't
mean that you can't have behavioral
functions on a in a value object. That
is definitely possible. The trick is
though is we're going to return a new
object um if you change something. So if
we had 11 pound or 10 sorry 10 pounds
and we want to reduce that amount by 6.1
pounds, we expect a value of 3.9. Now
what that is actually going to return is
a new pounds object with 3.9 pounds in
it. As I mentioned that that original
one we 10 10 we passed it's not mutable.
So it cannot change. It will return a
new object.
So like I mentioned biggest thing about
value objects is it eliminates checking.
So the pounds object decrease by
function. A little bit hard to see maybe
there at the bottom. Um but yeah like I
said the first thing you're going to do
there is new self
in your object. So you're going to
return a pounds object because like I
said the value objects are not are
immutable. You cannot change them. And
you're just going to say this value
minus decrease by pounds get value. You
don't have to check and see if the
pounds are negative or like in case the
recipe name. You don't have to check if
it's a string or anything like that.
It's all done. I don't even have to
check that when I'm subtracting, you
know, say have six pounds and I try to
subtract 10. I don't have to check that
here because it's not possible. the
constructor will catch that because this
will come in as a negative value and
it'll be neg3 and the exception will be
thrown weight cannot be negative. So a
lot of your checking and if logics and
stuff like that can be handled by the
the the um value objects you know like
mentioned the 70 degrees if you try to
subtract 400 degrees from that it's not
possible the absolute zero is going to
catch that you have a question
>> Yeah, that that's perfectly fine. Like
um yeah, if you had a weight object in
and say it's you just want to deal
because you're dealing in many
measurements like let's say that not
like a brewery where it's all pounds
because it's in the US. Um yeah, you
have a weight object and you're going to
have to store I'm going to get into this
in a second, but it's they call it a
composite value object. So weight is
going to contain two diff distinct value
objects probably maybe in the case
probably just a float just a regular you
know PHP object float for the and then a
string or a unit of measure object that
represents the unit. So then anytime
like this if you're like decrease like
let's say it's kilograms you try to
decrease it by pounds in your you know
you can throw exceptions there and say
like hey you know what the units don't
match or you can do the conversion if
that's business is like yeah we get
stuff randomly all the time we want to
do the conversion automatically be nice
and do it for them don't throw an
exception say oh you must put this in
pounds you know build these rules into
your business build intelligence into
your software you know like I mentioned
before the factories if it's difficult
to create an object from a string or
something like Feel free to build a
factory if that's the way you want to do
it. I mean, it's fine. However you
operate, you know, building your code
now is probably for the most part fine.
It's mostly just naming and
understanding that these value objects
are going to be immutable and always in
always valid.
Um, one of the things I like to do on
stuff is like static constructors on
these value objects. Go ahead real
quick.
>> Yeah.
Now, now PHP especially five, you know,
five or 54 up objects are incredibly
easy to generate. The only time you'll
run into a problem is if you build um
recursive
like issues where okay um you know I
want to make sure that the Fahrenheit
object can't be below absolute zero.
Well, I can't build a absolute zero
Fahrenheit object that extends
Fahrenheit because it's not possible for
it to be absolute zero. It's just not,
you know, so you end up having to make
sure you kind of separate things a
little bit. But objects are cheap. PHP
objects are very very cheap. So don't
don't be afraid of creating all these
objects and stuff.
Good question. Okay. Um, so static
constructors and I like to call these
natural language constructors. They're,
you know, they're coming from the
ubiquitous language. coming from what
you know um somebody talks about you
know maybe in the example here we have
pounds but we want to sometimes we get
stuff in that's metric in kilograms um
you know you have a function that's
static function from kilograms and this
will return a a new pounds object and do
the math for you now your software is
just dealing in pounds not worrying
about kilograms and stuff like that but
we know that that weight is always valid
so
um
yeah I mean you might have to do you may
even want something like from string. So
if somebody writes like 2.0 lbs, um you
may have you may want to write some
parsing logic there that does some reax
or something fancy and does that.
I think I've um you know done the
temperature example here a few times. Um
you get the you know the formulas and
stuff like that. I was going to do an
example but I think I'm going to move
past that. Does everybody kind of get
the idea that we're, you know, you're
creating these objects, they're
immutable, and um, you know, you do all
the validation in the constructor? Just
think everybody got that?
>> No.
>> Yeah. Do whatever you need to do. Um,
like I was saying, like it's two float,
pounds a string, something like that
unit of measure. And I'm just about to
go into like composits of these. So, um,
but you know, like I was saying, the the
units are important getting those right.
You know, if you've heard stories about
the Mars Climate Orbiter that crashed
into Mars because they were working in
miles and kilograms, you know, things
like that. Um there was a the plane
called the Gimly Glider that um you
know, they they weighed the the fuel in
kilograms instead of pounds. It didn't
have enough like, you know, had half you
know, whatever was less than half the
fuel it was supposed to had to, you
know, crash land. Luckily, nobody was
hurt. Um you know, these are the kind of
things you can eliminate by using these
value objects. um and making sure that
you know it's not possible to have
invalid state in your app. All right, so
composite value objects um easy example
would be a brewer's name. So a brewer
full name, you know, might consist of a
first name and a last name. If we look
at the the full name object,
uh there's really nothing to validate
here other than the fact that there is a
first name and a last name because first
name is a value object. It's just going
to make sure it's not empty just like
the recipe name. Last name value object,
it's going to make sure it's not empty.
So the full name, we know now passing
that around anywhere in our system, we
know exactly what a full name is always
valid. It's not possible to have empty
strings. And right here, we can
implement the two string method of how
to represent that in the system. So if
later you decide to add middle names and
suffixes, prefix and stuff like that, um
you don't have to worry about where
you've, you know, said, you know, two
string or get value on a full name or
formatted that. It's, you know, you just
have to update the constructor and where
you've used it and stuff like that. Um,
but yeah, I mean, we've eliminated a lot
of checking here. There's no checking to
make sure first name is not empty and
last name is not empty. We're relying on
these value objects to compose
themselves.
We're going to, you know, the goal here
is to encapsulate as much of our
business logic in the value objects as
possible. Like I mentioned, the
temperature thing. Um, you can get very
complex with these value objects. So,
one of the things that a brewery is very
concerned with is the amount of alcohol
that is in a beer. Um, you know, they
have to put it up on their board. They
have to tell you by law how much alcohol
is in a beer. And, uh, they call it ABV.
If you see that up on the board, that'll
tell you, you know, alcohol by volume,
it's 7% alcohol. Um, you know, that's
kind of a stronger beer. So, don't
drink, you know, 10 of them. But the way
you find out how much alcohol is in a
beer, you can obviously send it out to a
lab and spend a ton of money, have it
analyzed, um, or you can just calculate
it using some measurements. And if you
know about the brewing process there,
like I was telling you, if sugar and
yeast turn alcohol and uh CO2 out the
other side, um there's actually a
chemical equation for this, but I'm not
going to bore you to death with that.
But if we're trying to calculate the
amount of alcohol that comes out, we
know we don't care about CO2 and we
don't care about the yeast. That doesn't
affect the amount of alcohol that comes
out at all. Well, we care about the
sugar and the sugar, the amount of sugar
we start with and the amount of sugar
that we end with. Now, like I said,
yeast consumed sugar. So if we know how
much sugar the yeast consumed, we know
roughly how much alcohol a yeast
produces when it eats that sugar. We can
calculate how much alcohol is in the
solution. So we get alcohol by volume.
By dividing those two things um you know
subtracted from each other. Now you
measure that um amount of sugar in a
thing called specific gravity, but it's
basically the density of the sample
versus the density of water. And you can
do that very expensively. If you're a
professional brewer, you probably buy,
you know, a machine that to do that. Um,
but many craft breweries and every home
brewer does, and I have one of these up
there here if you want to look at it
later. It's a hydrometer and basically
it you put it in the water or you put it
in the the W that came out of the boil
kettle and you measure how far it
floats. And um, you know, so it floats
in the solution. It has a little scale
on it so you can measure, you know, how
much it goes up and down. And if you
know anything about chemistry, the
density of something lowers as it heats
up. Things expand. Like water actually
grows. I think it's like 5% from cold to
boiling like the volume of water grows
um in a solution. So, um when you're
measuring that specific gravity, you
have to know exactly what temperature
you're measuring that specific gravity
at because um like I showed you earlier,
that hydrometer has been scaled and
measured exactly a certain temperature.
So, in my case here, I have a 60°ree
hydrometer. So, that scale is meant for
60° uh Fahrenheit.
And you can look it up on a table and
figure out how much to move, you know,
the the temperature uh with specific
gravity. Um or you can try to you can
calculate it very easily. So I look at
um I put a thermometer in when I do the
measurement. So I know like in this case
it was it's kind of hard to see there,
but 66 uh degrees and I measured the
specific gravity and my you know my
hydrometer's set at 60. Do a little bit
of math and it's actually kind of
complex math but you can calculate you
know what it was supposed to be reading.
So if it read 1.040, we know it's
actually 1.041 because the temperature
adjusted it slightly. And it's not a
very hard calculation to do. So kind of
use this as an example with the
composite value objects to do this. So
the hydrometer test, you know, we're
going to look at the um building a test
for this first because I said like I
mentioned u testing value objects is
pretty easy. I just pulled values from
that table and you know when the
temperature is exactly the temperature
of the hydrometer, everything should
match up. That's an easy test, right? Uh
then we do maybe a test a bit higher,
put some values in there, you know, do a
test a bit lower, do the same thing. And
we're going to end up building a
composite value object that's called a
gravity or original gravity. In this
case, if you're measuring it before the
the yeast has eaten the sugar out of the
the wart, it's called the original
gravity. So, we're going to call we're
going to create a value object that's
called original gravity. And to read the
gravity reading, like I mentioned, you
read the gravity reading off the
hydrometer. So that's a float value
object that we've made sure is within a
reasonable range. And a temperature that
we know is within a reasonable range as
well. So um
you know, we we know that we can verify
that the hydrometer, you know, class
here can value object can tell you, you
know, you can't read a value above or
below the the scale. And you know, we
can do the logic right here to correct
the gravity for the temperature. So it's
like I said, a big long equation. um but
it's all centralized in this value
object. So the business rules and the
business, you know, uh the way they're
they do things, we're putting as much as
we can in the value objects.
So like I said, we want to um find the
uh gravity here, you know, the alcohol
by volume. This is the formula right
here. There's our readings. Original
gravity is OG and final gravity would be
after the yeast eats it. And that's the
formula for creating it. Now we can also
create a composite value object out of
that. We can have a gravity range. So
gravity range value object becomes a
original gravity and a final gravity.
Now we can make sure right in this value
object that the original gravity is
lower than the or sorry is lower than
the final gravity because as the yeast
eats sugar the amount of sugar in there
becomes less. The gravity actually
becomes lower. Um so we can verify that
you know somebody is putting in stuff
properly and that's all this object
needs to be. It just needs to be a
holder of you know those values perhaps
and then you know the class when we
write the alcohol by weight and the
alcohol by volume can work from that
gravity range. So this is again another
value object that has a static
constructor that creates itself from
another value object and the formula is
right in here. So I don't have to
validate anything you know as far as the
gravity ranges go. All that's been
validated for me temperatures the
readings all that stuff's validated. I
just have to worry about building the
formula here and doing the math and to
actually calculate ABV then you know we
do that final function equation there
and we end up with another value object
that is the alcohol by volume. So you
can guarantee everywhere in the software
that the alcohol by volume has been
validated nothing to do no checking. I
want to contrast that you know real
quick with u I mentioned that brewer
wall and that that's a PHP software
application but if you look and this is
just one class of calculations they have
um you know they have ABV OG and final
gravity um you know they're checking is
the original gravity more than the final
gravity is it a numeric value is it a
numeric value you know they're doing
that and if you look through that one
class um you know as they do it they do
it over and over over again they
actually you can't see because it's
getting cut off the top of the projector
here but they check og greater than
final gravity five times in one class.
So right there um you know you're
repeating code. That's one of the first
things we learn is don't repeat
yourself. Don't repeat yourself. Don't
repeat yourself. Don't repeat yourself.
Don't repeat yourself. Um it's a big
thing in code. You know the is numeric
checks. Not having those value objects
that represent a float knowing that a
final gravity is accurate. They did that
39 times in one class.
Passing these value objects around you
know eliminates a huge amount of
checking in your software. Um, you know,
you've put all of that checking into
small little classes that are very,
very, very easy to test. Now, like we
mentioned earlier, not doing things in
DDD. Some things are CRUD. Even things
that are CRUD, I still use value objects
a lot because it makes that stuff a lot
lot easier.
So, like I mentioned, you know, DDD, we
have these objects. Recipe name,
temperature, date brood, those are all
valid. We know they're valid. CRUD, you
know, like I said, strings, floats, you
don't know what they are. Functions,
validations testability usability I
think is, you know, a lot higher. You
don't have to worry. the users don't
have to worry about accidentally
screwing formulas up, things like that.
Um, another check there. And of course,
you know, we all test our code. So, all
the time.
No. Uh, but if it's obvious that you're
not going to have time to test
everything. Um, but value objects, I
would say absolutely test them and
because those are the like I said, the
very first thing that your your
application you put as much logic as you
can into them and focus on them. um
because
when you're building entities and these
are the models I was talking about,
you're going to rely heavily on those
those value objects. So um those
entities like I said they're
identifiable, they have state, they're
mutable. Um but they operate on the
value objects. They use those value
objects in order to store values and um
you know change over time. Um ideally
they're storage agnostic. Now that's not
always possible uh depending on what you
know frameworks you're using, stuff like
that. Laravel. Um, sorry. You may not be
able to do that quite as easily, but if
you're using Doctrine and stuff like
that, um, you know, it makes a little
bit easier to just keep your stuff
storage agnostic and then do mapping
later. But, um, do as much as you can in
the value objects. Put as little as
possible in the entities. Your entity
should just be storing values and
holding state. Um, you know, use those
doctrine mappings, doc annotations. Lar,
if you're Laravel developers, I'm sure
there's a few of you in the crowd.
Nothing wrong with that. Um, you'll do
this a little bit differently. I'm going
to show examples in doctrine. Um, but
Laravel, you probably will use your ORM
objects to hold state. So, you're going
to pass those to your entities and hold
state. That way, your entities can kind
of worry about business logic that's
associated with it. So, you're still
going to you're going to map in and out
of value objects in order to do your
mapping um through the OM
behavior first, storage second. So, like
I made you guys do the event storming,
thought about modeling, we weren't
worried about data and stuff like that.
entities again I mentioned earlier
single entity aggregates contain other
entities just to kind of um you know
keep those two in your head the code
behind them is almost identical it's
just that the aggregates store you know
multiple entities and if you're doing
transactional stuff um your transaction
boundaries are going to be around the
greater aggregates so when you save a
recipe you want to save all the hops and
grains associated with it all at the
same time um if you have a recipe and a
brew session together and you're
operating those in the same controller
because you've done too much in one
place. That happens, you know, has to
happen sometimes. Persist the recipe,
then persist the brew session. You could
do that as all one big transaction, but
put each one in its own transaction as
well. Uh if possible, it'll just make
your life easier because you're already
dealing with enough. Um like I
mentioned, uh you just how you make an
entity. It has an ID. Again, recommend
using EU ids. has functions like change
name, change email.
Um, it's going to be mutations on that.
So, we're going to try to avoid setters.
Now, these are kind of like setters. Uh,
but I'll kind of get into some more
behavior that we're going to have. Um,
if you look at the constructor of a
brewer, you know, what is required for a
brewer? You need a username, a full
name, an email address, and a password
maybe to store it login or something
like that. You we don't have to validate
any of this. This is basically our um,
you know, our entity. We've we've
validated it. There's nothing to do
here. We know the full name is valid. We
know the username is valid. Um, your
entities become quite simple. They're
just storage contain, you know, storage
of stuff.
And like I mentioned, centers are bad in
entities. Um, we want to avoid uh what
they call anemic modeling. Um, if you
look at an ORM, like I've written one in
the past, it's terrible, but um, Cropel
is a good example because, um, more
people know it. So you know you say new
book and then you set title set price
you know new author set set
at no point you know do you know exactly
when the book is in a valid state you
know if you if you don't have you know
is price required like you don't know
like until you go to persist to the
database you know is this book in a
valid state your entities should always
be in a valid state if possible now
there's going to be some times where you
can't do that necessarily and you need
to try to contain that as much as
possible but you know in the case of a
book If it requires a title and a pi
price, put those in the constructor of
that object and have them be valid
object value objects so you know that
it's a valid title, a valid price. Um
there's no reason to like have this be
in limbo anywhere throughout your app.
[100:01] If you're calling two setters on in a
[100:03] row on an object, um you're probably
[100:05] missing a domain concept. So if um you
[100:09] know your the mash thing that we're
[100:10] talking about if it's like set
[100:12] temperature and set quantity set this
[100:14] set that you're probably missing some
[100:16] some concepts there in your domain that
[100:18] you need to look at. Um but basically
[100:20] calling any two methods in a row on the
[100:21] same object. You might be missing
[100:23] something there. It's just kind of it's
[100:25] certainly possible you might do that but
[100:27] something to keep in mind um passing
[100:29] more than one parameter you know passing
[100:31] multiple value objects. If you passed
[100:32] around final gravity and original
[100:34] gravity all the time, uh, you know,
[100:37] maybe you should think about creating
[100:38] that gravity range object that holds the
[100:40] two together so you only have one object
[100:41] to pass around. Just simplifies your
[100:44] code a little bit.
[100:46] Look at another entity. Um, we're going
[100:47] to look at this. You know, you have
[100:49] grain. It has a name. It has a type.
[100:51] Maybe it's, you know, wheat or something
[100:53] like that. Love a bond. That's the the
[100:55] color of grain. like how dark of a color
[100:58] it is in in the brewery brewing uh
[101:00] language degrees lintner um that's a how
[101:04] great is it at converting from grain to
[101:06] sugar you know those are things that
[101:07] brewers care about we're going to put
[101:08] those into value objects and we're going
[101:09] to store those in the grain
[101:12] I mentioned behavior is the most
[101:14] important thing but I know the the
[101:15] developer reptile brains are spinning
[101:17] how do we deal with storage and all this
[101:18] stuff um if you're doing doctrine I
[101:20] recommend doing the doctrine annotations
[101:22] or doing the ammo doesn't matter um but
[101:24] you know you're going to end up storing
[101:26] these back to tables in your database.
[101:27] They're just entities just like you do
[101:29] usual ORM objects. Um the only thing
[101:33] with doctrine is, you know, doctrine
[101:34] doesn't know how to store these value
[101:36] objects necessarily. And you can write
[101:38] um
[101:40] embedibles and doctrine and do all that,
[101:42] but it's kind of a pain in the ass. Um
[101:44] so I recommend, you know, just um cast
[101:45] those back to strings and integers, your
[101:47] value objects. You should have a git
[101:49] value off of that that can give you the
[101:50] primitive that you can store in your
[101:51] database easily. And then your getters
[101:53] that you pull off of that, well, when
[101:55] you call get name, put it back into the
[101:57] value object so that all of your getters
[101:59] on the entities that you need return
[102:00] value objects. All the methods on an
[102:03] entity should either return a value
[102:04] object or another entity if it needs to.
[102:06] So an aggregate entity is going to
[102:08] return another entity.
[102:12] Uh Brewer, I'm not going to go through
[102:13] this because that's another just another
[102:15] example of how to store it. You guys can
[102:16] see this in the slides later.
[102:26] Sorry, it's slow.
[102:31] But yeah, I mean the reason we're using
[102:32] value objects is so when you call get
[102:34] full name or get email, you don't have
[102:36] to worry about checking that anywhere.
[102:38] You know, the email is always valid. Uh
[102:40] so changing values you know you're going
[102:42] to whatever the the you know the uh
[102:45] domain experts call it you know if it's
[102:47] changing email address in the example of
[102:48] a brewer you know call the function that
[102:51] uh in your recipe you know it's add
[102:52] grain add have a function called add
[102:54] grain and pass a grain entity or pass
[102:56] you know grain value object there. Um
[103:00] remember the grounded context again uh
[103:02] recipes inventory and brewing house
[103:04] bounded context. You may have multiple
[103:06] grain entities uh throughout your
[103:08] system. So you have recipe grain, you
[103:10] have inventory grain, you have brewing
[103:11] session grain. They're all uh different.
[103:13] Don't try to use one object across your
[103:15] entire system. Like I said earlier,
[103:17] brewers are entities, but they're part
[103:18] of a supporting B context. You're going
[103:20] to see everywhere. The aggregates that I
[103:22] mentioned um kind of going through this
[103:24] quickly, but you know, they're the ones
[103:25] that manage child entities. I kind of
[103:26] already went over those. Um building
[103:29] entities is uh you know, you guys
[103:32] probably saw these. The first things you
[103:33] saw were the, you know, grain and
[103:35] recipe. The aggregates are the easiest
[103:37] to identify in your system. You know,
[103:38] recipe is pretty easy to find. You know,
[103:40] you're obvious that you know, recipes
[103:41] are going to be needed. Modeling the
[103:42] other entities, the the child entities
[103:44] is a little bit tough. You know, that
[103:45] can be uh difficult. You know, do we we
[103:47] have a grain entity, you know, or do we
[103:50] need to, you know, how we have to model
[103:51] it? That's that's really where the the
[103:52] rubber meets the road when you're trying
[103:54] to figure out the modeling. Um
[103:57] but again, your names come from the
[103:58] event storming session. So a um
[104:01] aggregate you know a recipe uh if you're
[104:03] using doctrine it's going to have this
[104:04] big annotation with the grain you know
[104:06] relation and stuff like that. Laravel
[104:08] you're going to have you know the array
[104:10] and things like you're mapping to. Um
[104:13] but I don't want you to think about
[104:14] storage anymore. We're done talking
[104:15] about storage. Um you know let's just
[104:17] look at a regular aggregate and you like
[104:19] I mentioned you have grain add grain.
[104:21] Now you can just store that in array. Um
[104:24] but you know think about you know what
[104:26] possible business rules could you have
[104:28] with you know adding grain to this
[104:29] entity. Well um there's this thing in
[104:31] brewing um that you have to have a
[104:33] minimum amount of diastatic power and
[104:35] that was that degrees lintner value that
[104:37] I mentioned earlier and if it's less
[104:39] than 40 the recipe will not work. It's
[104:41] an invalid recipe. So we can actually um
[104:44] you know throw an exception uh in our
[104:46] function here that the grain is not
[104:47] valid. Now if you're passing grains in
[104:49] you know to the constructor you could
[104:50] validate it there. uh but you can do the
[104:53] math right here in your entity uh and
[104:55] throw an exception. Now what I would
[104:57] recommend like I said is put as much
[104:59] logic in value objects as possible. So
[105:02] you know instead of doing um this method
[105:04] in the um entity let's create a new
[105:07] value object called grain bill. That's
[105:09] how brewers refer to the amount of grain
[105:10] that's going to go in a recipe. So we
[105:12] create an immutable array of objects.
[105:15] Again value objects are immutable. Uh
[105:17] and all the grain objects would then be
[105:18] immutable inside of it as you're holding
[105:20] them. and you add a grain and it can do
[105:22] the math right there. So um your entity
[105:26] becomes much much simpler. Again all
[105:28] you're doing is holding collections and
[105:29] holding on to it. So add grain it just
[105:31] calls this grains add grain and I
[105:33] remember that's a value object. It
[105:34] returns a copy of it a new copy of
[105:36] itself. So you set this grains equal to
[105:37] it and that becomes your array
[105:39] collection and you know do laravel knows
[105:41] how to persist that. So again, if you
[105:44] have logic in entity, try to move it to
[105:46] a value object. And as I mentioned
[105:49] before, the aggregates are your
[105:50] transactional boundaries for database
[105:52] transactions and stuff like that. Again,
[105:54] you can read a lot more about that, but
[105:56] I hope you're seeing that, you know,
[105:57] this kind of code that you're doing is
[105:59] much more maintainable, much more
[106:00] modifiable.
[106:03] This kind of gets to a user experience
[106:05] point. Now, we've done all this
[106:07] eventtorming. We're building our objects
[106:08] with the naming and stuff like that.
[106:10] it's going to force you to stop thinking
[106:12] about the data. Now, that's I'm, you
[106:15] know, kind of an application designer as
[106:16] well. This is what they call a tax task
[106:19] based user interface. Now, if you've
[106:21] seen the HBO show Silicon Valley, um,
[106:23] Hide Piper had what they call the
[106:25] programmer's interface. Every switch,
[106:26] knob, bezel, bevel, everything on there
[106:29] possible that you could possibly want to
[106:31] change. And this user goes to use this,
[106:34] they're totally confused. And but this
[106:36] lets you, if you're thinking data first,
[106:37] this is the kind of interface that you
[106:38] build. And this is an example I built
[106:41] for a library system. And
[106:44] you know, when a librarian would go to
[106:46] use a library system, they don't think
[106:47] about editing a book and deleting a
[106:49] book. That doesn't make any sense. You
[106:50] know, your your functions on a library
[106:53] system are like renew a book. You know,
[106:55] return a book, replace a book, things
[106:56] like that. You know, these these actions
[106:59] that you guys wrote, these commands,
[107:01] those are the things that people think
[107:02] about. Those are the buttons that should
[107:03] be in your software. That's going to map
[107:05] directly to your functions on your
[107:06] entities. So when you're thinking about
[107:07] it from the events, you're thinking
[107:09] about the entities, you're thinking
[107:10] about the B, you know, the bounded
[107:11] context, the ubiquitous language, it's
[107:14] going to force you to build better
[107:15] software. You're not going to be
[107:16] thinking about the data first. So
[107:18] usability goes up.
[107:21] Um, DDD has like very little to do with
[107:23] security. Again, that's like, you know,
[107:25] kind of concern for every um,
[107:27] application, but you know, DDD is just a
[107:30] layer as far as, you know, DD uh,
[107:32] security is just a layer as far as DDD
[107:34] is concerned. um unless that is your
[107:36] domain but for the most part most of us
[107:37] are just you know putting security in as
[107:39] a layer and DDD you know there's
[107:41] different ways of calling this layered
[107:43] architecture onion architecture the DDD
[107:45] way of saying it is hexagonal
[107:47] architecture and the idea here is that
[107:49] your controllers and security and
[107:51] templates are a very thin layer on the
[107:53] outside and your core domain business
[107:55] logic is what you focus the most on and
[107:58] let the framework do the outside pieces
[108:00] as much as possible like I said don't
[108:02] build your own uh you
[108:06] uh login controllers and things like
[108:07] that. You know, let the frameworks do
[108:09] that for you. Um you know, focusing on
[108:12] that core domain uh is, you know, what
[108:14] you want to spend your time. You know,
[108:15] I've been at the same company um for 10
[108:17] years and doing code maintenance and
[108:20] that kind of stuff is not fun. And I
[108:23] felt like the first few years I was
[108:24] working there, I didn't realize it, but
[108:25] I was digging my own grave. You know, we
[108:27] built it, we built software that we
[108:28] built fast, we built um you know, with
[108:31] using crowd apps and stuff like that.
[108:32] And now we're trying to maintain it much
[108:34] much slower to maintain over time a CRUD
[108:36] app than it is to maintain a DDD app
[108:38] because everything is encapsulated in
[108:40] the DDD apps and it's much easier to
[108:42] understand when you go back and look at
[108:43] it 5 years later. If you work for a
[108:44] consultant agency and you hit it and
[108:46] quit it, whatever. You probably don't
[108:47] have to worry about this stuff.
[108:49] The other reason to do this hexagonal
[108:51] architecture and try to separate things
[108:52] as much as possible is you know the
[108:54] frameworks change much much faster than
[108:56] your domain logic. you know the brewing
[108:58] logic that you're going to write today
[109:00] that's going to be good for I don't know
[109:02] 1600 you know started brewing in 1600s
[109:04] it's probably good for another 500 to a
[109:06] thousand years symphony or Laravel you
[109:08] know symphony is going to change every
[109:10] six months Laravel changes whenever it
[109:12] changes I don't know but you know you to
[109:15] keep up with that you're going to have
[109:17] maintenance to do on the framework and
[109:18] the controllers and things like that you
[109:19] want to keep that separate from your
[109:20] domain logic you don't want to tie your
[109:22] domain logic to your framework code and
[109:24] things like that you want to keep it
[109:25] separate keep it namespaced and stay
[109:26] away from your domain main logic. I'll
[109:28] point you to a talk by Mattas Reyes. He
[109:30] gave it laron in 2014. Um, decoupling
[109:34] the model from the framework and I'm
[109:36] going to post these slides up later on
[109:37] the joined in. Uh, Ruby Midwest, uh,
[109:40] Uncle Bob Martin, uh, has a great talk,
[109:43] architecture, the lost years. Another
[109:46] one you should watch definitely about
[109:47] keeping your domain logic separate. Gist
[109:49] of that is if you open your folder of
[109:51] your code and this is the example uh
[109:54] GitHub project I have for this workshop
[109:57] and
[109:58] you know you probably can't tell what it
[110:00] is necessarily but when you open the
[110:02] domain folder up you should sure as hell
[110:04] be able to tell what the software is
[110:06] doing um versus organizing everything
[110:08] into entities and controllers and
[110:10] repositories and things like that. Like
[110:12] if your code is organized like that you
[110:13] you look at those folders and you have
[110:15] no idea of cohesion across the you know
[110:17] entire app.
[110:20] So again thin controllers uh persistence
[110:24] things like that are all helping to the
[110:25] outside you have an event bus and a
[110:27] command bus maybe that you know kind of
[110:29] taking application services you have
[110:31] those kind of things it's impossible but
[110:32] like I mentioned earlier the ports and
[110:34] adapters try not to um you know have
[110:36] your domain logic depend on the
[110:38] framework have try not to have the
[110:39] framework depend on the domain logic if
[110:41] possible that will definitely help with
[110:43] your maintainability.
[110:45] All right, so a couple more things to
[110:46] mention uh just to get you, you know,
[110:48] going in the DDD domain events. Um those
[110:52] are just basically mutable value
[110:53] objects. Uh part of the core domain when
[110:56] I had you write the orange things, I had
[110:57] them write you write them in the past
[110:59] tense. An event always happened in the
[111:01] past tense. When you put it in your
[111:02] software, it's already happened. It's
[111:03] not possible to have an event that's
[111:05] scheduled for the future. That's not an
[111:06] event. Um you can create them in
[111:09] entities. fine as like you know it's
[111:11] happening there but um they don't
[111:14] contain the entities so you're going to
[111:15] map values out of the entities into
[111:17] value objects and store them there you
[111:19] know the example was uh brew session was
[111:21] planned was one of the events that maybe
[111:23] another context is caring about it's
[111:25] going to pass it um you know you take
[111:27] whatever you wrote in your orange code
[111:29] smush it together like one big German
[111:31] word and brew session was planned and
[111:33] that becomes your event class so like I
[111:35] said they've handled the naming for you
[111:37] um and Those are just big composite
[111:40] value objects that you're going to pass
[111:42] around everywhere in the system. You can
[111:43] depend on this event. Everything is good
[111:45] on it. No checking. You know, kind of
[111:47] repeating itself, but you get the idea.
[111:51] You know, if you're asking yourself
[111:52] where where do these events come from
[111:54] right now. Um most of the time you're
[111:56] going to come from event from your
[111:57] entities or from services or maybe just
[111:59] somewhere in your application layer. Um
[112:02] one of the things I like to do with
[112:03] entities is uh have this trait that we
[112:06] use. It's called event recorder. And all
[112:08] it does is hold an array of events. So
[112:10] that way an object can um as you do
[112:12] things in the object you just hold that
[112:13] array of events and then uh the
[112:16] controllers are using that entity can
[112:18] just ask the entity hey what events
[112:19] happened and publish those up. Um so an
[112:22] example of that is like the yeast
[112:24] starter we talked about building a yeast
[112:26] starter uh before it goes into the from
[112:29] um event recorder. the constructor of
[112:31] that will create a event starter was
[112:35] made big composite value object record
[112:38] it um wherever you're using that you're
[112:41] creating that new yeast starter then ask
[112:43] it for the events that were created and
[112:44] publish them back up to your application
[112:47] you know when it's the yeast starter is
[112:49] finished you're going to measure the
[112:50] specific gravity so you can figure out
[112:51] how much yeast you created you're going
[112:53] to record that it was finished and
[112:55] you're going to publish that up because
[112:56] you know maybe somebody cares somebody
[112:58] doesn't um but you might as well publish
[113:00] the events
[113:02] So if you're looking at a controller or
[113:04] service or wherever the wherever you're
[113:06] doing this this logic on your entities
[113:09] um and you have a function that's maybe
[113:10] creating it you know like I said you're
[113:12] going to create the new entity that
[113:13] you're doing um you got to persist your
[113:16] entity if you're using doctrine and you
[113:17] have to flush it everything should be
[113:20] persisted and flush it flushed before
[113:22] you fire the event because other pieces
[113:24] of the software you don't know where
[113:25] they're going to be are going to rely on
[113:26] that data to be there it has to be there
[113:28] you don't know it someday you might
[113:30] decide Oh, we can't publish our events
[113:32] right away and act on them because it
[113:33] takes too long. We're going to push them
[113:34] to a queue. Well, if you've persisted
[113:36] your entities before you publish, you
[113:38] don't have to worry about that because
[113:39] it can happen later. It's not a big
[113:40] deal. Um, so yeah, just use an event
[113:43] publisher that comes with your framework
[113:44] and publish the events.
[113:47] Uh, and then you'll build listeners like
[113:48] I said and that is other bounded
[113:49] context. You have a listener that just
[113:51] listens for this event to be come across
[113:54] the stream and you know maybe in this
[113:56] case it's a inventory automatic
[113:58] purchasing listener. So, it's going to
[114:00] say, "Oh, you know, Bruce Sesser is
[114:01] planned. We better make sure we have all
[114:02] the grain handy and order it if it
[114:04] doesn't." And again, your value objects
[114:06] make this kind of stuff um you know,
[114:08] kind of a lot simpler. This could be a
[114:10] very complex piece of software, but you
[114:12] know, we can we can distill it all the
[114:13] way down to a value object that's a
[114:15] minimum grain order and make sure you
[114:17] know, oh, if it's less than zero, it's
[114:18] not possible to be negative. You know,
[114:20] we have a lot of things that are all
[114:21] happening because these value objects
[114:22] are doing the work for us.
[114:25] using domain events passing on messages
[114:27] and stuff like that make uh things a lot
[114:29] more modifiable.
[114:31] All right. So um you know there's other
[114:34] things in domain driven design like
[114:35] specific specifications um building
[114:37] business rules in a specification um
[114:40] like I mentioned earlier um you know you
[114:43] may have grain that you have a certain
[114:44] amount of degrees lintner that's
[114:46] required. So if you look at your grain
[114:48] bill for a piece of uh recipe you're
[114:51] going to store this in the database you
[114:52] have all these float values and stuff
[114:53] like that. um and you want to build a
[114:57] repository that's going to return you
[114:58] grain that's possible to self-convert
[115:00] stuff like that, you're going to use
[115:02] these specifications. The specifications
[115:04] are basically business rules to still do
[115:05] a class and that's going to return a yes
[115:07] or no answer. And you're going to use
[115:09] those and um we can do an example real
[115:12] quick. If you flip through your your
[115:14] your uh packet there, um you see I have
[115:18] like a uh is it minimum diatic power
[115:22] specification.
[115:25] Now um that's probably going to operate
[115:28] on a grain and I don't I don't want to
[115:30] spend any time filling this out. I kind
[115:31] of didn't plan that very well. But um
[115:35] you know that specification is going to
[115:36] return a yes or no answer. of the
[115:38] repository. You don't have to build
[115:40] these queries into your repositories
[115:41] necessarily. If you have a very small
[115:44] data set, you don't need to build tons
[115:46] and tons of criterias and quer queries
[115:48] and all kinds of stuff in your database.
[115:50] You can just return all the grain and
[115:52] then filter through a specification and
[115:54] filter that down uh using a filter
[115:57] class. And um you know specifications
[116:00] become more important as um business
[116:04] rules become more more complex. So we'll
[116:06] have specifications that rely on other
[116:08] specifications. So a constructor of a
[116:10] specification may have five
[116:11] specifications with it and you know we
[116:14] just use that giant one in one place. We
[116:16] say yeah is this uh satisfied by this
[116:18] specification. Now if the business rules
[116:20] change we don't have to go and update
[116:21] the controller code and all this stuff.
[116:23] We just update the specification. You
[116:24] can test that a lot easier than trying
[116:26] to end test everything. Um
[116:28] specifications are a very uh important
[116:30] thing. So
[116:32] everything is awesome right? You guys
[116:33] are feeling it. You're feeling the vibe.
[116:35] Anybody have any questions on the
[116:36] entities and aggregates and stuff?
[116:39] No.
[116:47] >> Both actually. Yeah. So, um a good
[116:50] example would be like uh Brewer was
[116:53] fired say, right? Um there might be
[116:57] application concerns like we need to
[116:59] clear his sessions, you know, we need to
[117:00] log him out immediately. we need to um
[117:03] you know delete his account from the
[117:04] directory. Uh we need to delete his uh
[117:07] open directory account. We need to do
[117:08] things that are like infrastructure
[117:10] concerns. Then there's probably domain
[117:12] concerns as well like oh he's in the
[117:15] middle of a brew session. We need to let
[117:16] somebody know to take over because you
[117:18] know he was responsible for this but
[117:20] he's gone now. So you're going to have
[117:22] both listeners on both sides. Yeah. And
[117:25] you probably you want to make your
[117:26] listeners um you don't want to have one
[117:28] listener that listens for brew brew is
[117:30] fired. You may have five listeners. So
[117:32] you're kind of spreading out the logic.
[117:34] In every bounded context, you'd have a
[117:36] separate listener that's listening.
[117:40] >> Yeah.
[117:59] Yeah.
[118:08] >> Yeah. I mean, that's going to be a
[118:09] business type rule. So, you're gonna So,
[118:11] you know, if you have that problem,
[118:13] you're going to have to notify somebody
[118:14] or something like, you know, or um you
[118:17] could throw an exception. I mean that
[118:18] could be the case that you know you're
[118:20] um you know when you have a controller
[118:23] at least if you're doing stuff in your
[118:24] Laravel you can wrap that on a
[118:25] transaction and you know throw an
[118:28] exception in your listener and the
[118:29] transaction won't um even though you've
[118:31] flushed and persisted the transaction
[118:34] wrapper around the whole thing will make
[118:35] the whole thing blow up all at once.
[118:37] >> Yeah. And it's kind of um there's some I
[118:40] didn't cover this at all but there's
[118:41] something when you get kind of further
[118:42] into the domain driven design stuff they
[118:43] call it sagas or process handlers. And
[118:46] it's basically a way of mapping out that
[118:48] kind of business flow where it's like
[118:49] okay yeah we got to make sure this is in
[118:50] place that you know the example is like
[118:52] a McDonald's or Burger King when they're
[118:55] making your sandwich they gota make sure
[118:56] you have the bun the hamburger patty the
[118:57] cheese lettuce they don't have to make
[118:59] all those at the same time or Starbucks
[119:01] for example you know the process can be
[119:03] done in parallel but it all needs to end
[119:04] up at the same place so you're kind of
[119:06] dealing you can do those kind of you can
[119:07] model that you know in a class make sure
[119:09] that this needs to happen and this needs
[119:10] to happen if it doesn't throw an
[119:11] exception catch it
[119:15] Okay. Um, we got about 10 minutes. I
[119:18] want to cover two other subjects that as
[119:20] soon as you get into DDD, you're going
[119:21] to see and I don't want to conf be I
[119:23] don't want you to be totally confused or
[119:25] assume that you need to run out and do
[119:26] this stuff because um it's designed for
[119:29] very very large apps. So again, DDD it's
[119:32] you know the people from enterprise
[119:34] software people are liking this a lot
[119:36] more than your you know small software
[119:39] development shop. um you you don't need
[119:42] to do these things and one is uh command
[119:44] query responsibility segregation CQRS
[119:47] you don't need to do this unless you
[119:48] have a very big app and you have a lot
[119:49] of complex stuff um but a lot of people
[119:52] you know they see fancy um ways of
[119:55] implementing or fancy frameworks they
[119:56] want to jump in and use them uh they
[119:58] call this CQRS and it's a fancy way of
[120:01] saying we're going to keep the right
[120:02] side separate from the read side now um
[120:05] what you end up is you have write models
[120:07] and you have read models your right side
[120:09] becomes is more of your domain model and
[120:11] your read side becomes projections and
[120:14] um
[120:16] you know you kind of can separate you
[120:18] know what it is to see like a list of
[120:19] grain uh separately from everything you
[120:22] could do to grain. So you don't have to
[120:23] when you're building your views and
[120:24] things like that you don't have to worry
[120:25] about all the functionality that can
[120:27] happen. You can just read from the
[120:28] database and display it. And if you have
[120:30] a large team or a big product and or you
[120:32] need to cache data things like that the
[120:35] secrets may help with stuff where you're
[120:36] building projections of data and you're
[120:38] trying to cach it. Um the way that kind
[120:40] of works and this is kind of like
[120:42] together command query response
[120:44] declaration. You have a controller and
[120:46] you have a command bus. Command bus is
[120:48] kind of separate from the controller.
[120:50] And as a request comes in the controller
[120:52] handles the request creates a command
[120:54] which is a value object sends to the
[120:56] command bus. Uh sorry there's the
[120:59] command. You guys did the blue squares.
[121:01] Those are your commands. If you're doing
[121:02] CQS those become your command objects.
[121:05] Um if you don't do CQS those should
[121:07] become your method names. um you know
[121:09] add grain, add hops, things like that.
[121:12] Um those again are value objects,
[121:14] composite value objects. So the
[121:15] controller passes that command down to a
[121:16] command bus. Command bus figures out
[121:19] which handler which is again just like a
[121:20] listener. Um fires the command to that
[121:24] figures out the response, passes it back
[121:26] up. Um the idea here is that you can
[121:29] have really really really thin
[121:30] controllers because all you have to do
[121:32] is take the user input, figure out a
[121:34] command, the command is valid. I mean
[121:36] it's like that um the example I just had
[121:38] all those are value objects. It's
[121:39] possible for it to be invalid. Command
[121:41] bus you know fires back the response. Um
[121:44] in the slides here I'll have a link to
[121:45] go learn more about that but um the idea
[121:48] is you know that these very complex
[121:50] controllers you can eliminate down quite
[121:53] a bit. Um I put a diagram in in the
[121:56] packet as well as kind of like a you
[121:58] know flowchart as far as how that falls
[122:00] through your software. Again I don't
[122:02] want to talk about it because it's it's
[122:04] very complex. Um
[122:08] again why would you want to do this? Um
[122:10] well having those really thin
[122:12] controllers makes it easier to keep your
[122:14] domain logic uh separate from your
[122:15] framework. You want to switch from
[122:16] frameworks you want to make that you
[122:17] know changes that it's it's very easy to
[122:19] change um if you have many many ways of
[122:22] doing the same thing. So if you have an
[122:25] iPhone app and a web app and a console
[122:28] application, various, you know, many
[122:30] many versions of an API and you want to
[122:32] do the same thing in multiple places,
[122:34] commands and command buses make that a
[122:36] lot easier because you build your
[122:38] command, you fire it off the command
[122:39] bus. All of your domain logic is in the
[122:41] handling side. There's no logic in the
[122:43] controllers and stuff like that. So it
[122:44] becomes easier to do that.
[122:47] All right, one more thing and that's
[122:49] event sourcing and um that is a uh
[122:53] basically a way of storing models and
[122:55] data that's different um than most
[122:57] people are used to. So it's not database
[122:59] tables they're spread across. Um does
[123:01] anybody play chess here? Yeah. All
[123:04] right. So you guys are probably familiar
[123:06] with this notation, but there's a
[123:07] notation for chess that you can play
[123:09] back any game in chess. Uh and you can
[123:12] play it to any point and see what the
[123:13] board looks like at that point by
[123:15] following the notation. If you look at a
[123:17] bank journal, uh, it has credits and
[123:19] debits. So, at any point, you can figure
[123:21] out the balance by following the credits
[123:22] and debits. As software developers,
[123:25] you're probably used to seeing like git
[123:26] diff requests. Git git doesn't store
[123:29] like every copy of every file that
[123:31] you've ever had. It stores the
[123:32] differences between the um over time.
[123:36] That's basically what I'm talking about
[123:38] with event sourcing is that you're no
[123:39] longer going to persist your event
[123:41] properties. Um, your object, your events
[123:42] are persisted to an appendon event
[123:44] storage. Um, so your object like your
[123:48] grain object, you're not going to store
[123:49] a name, uh, quantity, you know, those
[123:52] kind of things. You're just going to
[123:54] store the events like the name was
[123:55] changed, the quantity was increased, the
[123:57] quantity was decreased, and you're going
[123:59] to by looping through those events and
[124:01] replaying them, you're going to be able
[124:02] to figure out what the current state is
[124:04] or what the state was at any time. Um,
[124:08] reasons to do events sourcing, uh, it
[124:10] avoids a lot of data mapping because
[124:11] you're just storing events. You don't
[124:12] have to you have a recipe. You don't
[124:15] need a grain table. You don't need a
[124:16] hops table. You don't need a east table.
[124:19] You don't need a you know all these
[124:20] other tables that you would need to
[124:21] store linked you know associated models
[124:23] with all you have to do is store the
[124:25] events. U that becomes one table
[124:27] associated with your um recipe and you
[124:30] potentially reduce model counts because
[124:31] you don't need to store all these as
[124:32] models. You can store them as value
[124:34] objects as events. And um there's a
[124:37] couple libraries that are PHP libraries
[124:39] that are good for doing this. Um, but I
[124:42] kind of wanted to do an example real
[124:44] quick that um is in the GitHub
[124:47] repository. You can go look at this. Um,
[124:50] but uh it's in the like kind of the
[124:52] second to last page I think or third to
[124:54] last page of your packet there. And if
[124:56] you look through that, you'll see that
[124:57] um basically you just loop through those
[125:00] history events and uh replay them and
[125:03] you call a method that knows how to set
[125:05] the internal variables uh of that
[125:07] object. And all you have to worry about
[125:10] storing if you're going to persist that
[125:11] object then is the event string. So you
[125:13] need, you know, a recipe table that
[125:14] might store the ID. That's it. And then
[125:17] you have a recipe events table that
[125:18] you're storing everything in. You know,
[125:20] if you're using NoSQL database, you you
[125:22] just have one big blob that is your
[125:24] recipe and it's going to have all the
[125:25] events associated with it. So storage
[125:28] really does not become a concern then of
[125:30] at all. It becomes very very easy. Um
[125:32] what becomes difficult is that if you
[125:35] have um many many recipes many many
[125:37] events and you're trying to read that
[125:40] data like you want to display a list of
[125:42] recipes and all the sol you know what's
[125:43] happened you don't necessarily want to
[125:45] have to load the recipes loop through
[125:47] all the events play them back um you're
[125:49] talking about you know complexity you
[125:51] know if you remember big O notation
[125:53] complexity getting worse and worse and
[125:55] worse as you have the objects you know
[125:56] if you have 10 recipes to display and
[125:58] each one of those recipes has 10 events
[126:01] in the fast. You have to load 100
[126:02] objects to load 10 recipes. Obviously,
[126:05] that can get way worse. Um, so you can
[126:08] cache it. You can do things like that,
[126:09] but that's where the CQS and event
[126:11] sourcing kind of come together is that
[126:13] if you're going to do event sourcing,
[126:15] you probably want to build read
[126:16] projections that make your application
[126:18] fast. So, you're going to want to store
[126:19] a cacheed copy of all your recipe names
[126:22] and maybe a few details about them. Um,
[126:24] so to do event sourcing, you know, it
[126:27] only do it in things that are very
[126:28] complex or you're carried about care
[126:30] about the entity over time. Uh, because
[126:32] you get logging for free. Your event
[126:34] stream is your log. Um, you know, but
[126:37] again, if you start reading material on
[126:39] DD, they're going to push you towards
[126:41] secure events uh sourcing. You don't
[126:45] need to do it um for most applications.
[126:47] It's only for big, you know, enterprise
[126:50] applications.
[126:51] I know I'm a little bit over time here.
[126:53] Um, learning more. Again, if you want to
[126:55] learn more about brewing beer, homebrews
[126:56] association.org,
[126:58] home brew, the Reddit home brewing
[127:00] thread is really good, go visit your
[127:02] local homebrew shop. I have mine in my
[127:04] phone as a favorite. Um, they're great.
[127:06] You know, always a great resource if you
[127:08] have one nearby. More importantly, if
[127:10] you want to learn more about DDD, you
[127:12] know, all these books are great. I
[127:13] mentioned earlier in the beginning, um,
[127:15] you can find them on Amazon, whatnot.
[127:18] uh a couple of PHP developers that I
[127:20] know did a podcast where they ran
[127:22] through um they have a couple different
[127:24] books that they've gone through, but
[127:25] they went through the red DDD book every
[127:27] chapter and had a discussion about it.
[127:29] It's maybe about 12 hours, 14 hours
[127:31] total. U but if you start looking
[127:33] through the red book, watch this um dev
[127:37] book club goes along with it. It's like
[127:39] having a book club. I mean, if you've
[127:40] ever done that before, you really get an
[127:42] understanding of DDD from watching that
[127:43] and understanding it. couple of podcasts
[127:46] that you can go watch uh about DDD
[127:48] they've t different episodes um there's
[127:51] a conference called domain driven design
[127:52] Europe that has a all their videos uh
[127:54] their sessions are online
[127:57] uh if you want a much more in-depth
[127:58] workshop this is kind of a high level
[128:00] you know we'll talk about it for two and
[128:01] a half hours do a few things um I I
[128:04] would have wished I could have done more
[128:05] um examples but we we took longer than
[128:07] to do a few things I talked way way too
[128:09] much as everyone knows I do um but if
[128:12] you want to go for a couple of days um
[128:14] bon Vernon, the guy who wrote Redbook,
[128:16] runs workshops and these are all
[128:18] international right now. U but he does
[128:20] plenty in the United States as well and
[128:22] it's a great uh way to learn more about
[128:24] it. Uh there's a couple of um GitHub
[128:28] GitHub repositories that are great. The
[128:31] friends of DDD state of the union is a
[128:33] great one. Has a bunch of example
[128:34] projects like full end to end um you
[128:37] know projects that to get you going on
[128:39] DDD. Um, awesome DDD. Again, another
[128:42] list of things that you can learn more
[128:44] about. Um, you know, that kind of thing.
[128:46] If you have questions, there's a
[128:48] fantastic Google group, uh, ddd phpb.org
[128:52] that just takes you to the Google group.
[128:54] Um,
[128:56] fantastic answers questions on there.
[128:58] You have a if you have a question,
[128:59] search in there first. First, you'll
[129:00] probably find your answer. Um, but, you
[129:03] know, again, you can always reach out to
[129:04] me on Twitter um or on my website. You
[129:06] can get my email. Leave you with a
[129:08] couple of final thoughts. Um, I can't
[129:11] teach you everything about DDD in two
[129:12] and a half hours, just like I can't cure
[129:14] your slice in golf in 60 seconds. It's
[129:16] actually not possible. Um, you're going
[129:18] to get better by working together with
[129:20] stakeholders, working with other
[129:21] developers, modeling. Um, if you don't
[129:24] have other developers you work with,
[129:25] teach your dog about DDD, you learn more
[129:27] by teaching. Uh, I have, it's probably
[129:30] my fourth time, fifth time speaking
[129:31] about DDD. I learn more every time. Uh,
[129:34] you know, um, if you don't have a dog,
[129:36] you can talk to your elephant. um they
[129:38] don't you know they remember everything.
[129:39] So, um, have one of these on your desk
[129:41] and, you know, when you're thinking
[129:42] about code, verbalize it. It just makes,
[129:45] uh, for much better code, better
[129:47] understanding.
[129:49] Reasons why you should learn more about
[129:50] DDD. Um, autonomous cars primarily first
[129:54] reason. No. Um, CRUD applications are
[129:57] dead. I mean, how much longer can it be
[130:00] that to generate a full application that
[130:02] just reads and updates and deletes data?
[130:03] That's that AI is going to have that
[130:05] crap figured out in a couple years.
[130:07] Don't worry about it. Um, so having
[130:09] better skills, having more advanced
[130:11] skills, understanding and being able to
[130:13] solve problems that are complex that a
[130:15] machine may not be able to figure out,
[130:16] that's going to be your value for the
[130:17] next few years.
[130:20] Value objects. Value objects. Value
[130:21] objects. Avoid common pitfalls in your
[130:23] program. I mean, how many times have you
[130:25] had a bug that was tied to a value that
[130:27] you didn't validate properly? Value
[130:29] objects, big deal. Separate your code
[130:31] into
[130:33] bounded contexts. Keep things separate
[130:35] as much possible. feel much easier. If
[130:38] you're dealing with legacy, keep legacy
[130:41] away from your bounded contexts. Um, the
[130:45] easiest way I found is you're publishing
[130:46] events from legacy that are new value
[130:48] objects. So, you publish a domain event
[130:50] from legacy to talk to the new bounded
[130:52] contexts. Don't try to wrap legacy code
[130:55] maybe, especially in my case, um, you
[130:57] know, legacy code that's not dependency
[130:59] injected properly. It's not written
[131:01] properly, you know, it's using old OMS
[131:03] and things like that. it's not possible
[131:05] to move this over to a you know
[131:07] dependency injected proper way of doing
[131:09] things. Um so you pass messages uh tends
[131:13] to be the easiest way to get stuff
[131:15] between uh things you know whether it's
[131:17] done through an API or call or whatever.
[131:21] How many of you have seen the agile
[131:23] manifesto?
[131:25] Yeah. Okay. So everybody hears agile
[131:28] with capital A registered trademark from
[131:30] Jira. But the real way that you're gonna
[131:32] have a better uh way of building
[131:34] software is being agile. And if you look
[131:37] at the actual manifesto and this is kind
[131:39] of getting cut off because of the
[131:40] projector, but you know valuing the um
[131:43] individuals interactions over process
[131:44] and tools. We've talked a lot about
[131:46] ubiquitous language today. We tried to
[131:48] you know go through how you can build
[131:49] the ubiquitous language with your people
[131:51] that you work with. Value that over the
[131:54] frameworks and the implementation that
[131:55] you build. working software over
[131:57] comprehensive documentation, the
[131:58] ubiquitous language and mapping those
[132:00] directly to entities, mapping those
[132:02] methods to those names. You don't need
[132:04] to document the the brewer can read your
[132:06] code. You don't need to write a bunch of
[132:07] documentation that goes along with it.
[132:09] And again, customer calibration and
[132:11] responding to change, those are making
[132:12] your software more modifiable, more m
[132:14] more maintainable.
[132:17] So, I covered a lot. I talked a lot and
[132:19] I know you guys probably have more
[132:20] questions and I'm happy to answer them
[132:21] afterwards, but we're kind of out of
[132:23] time. Um,
[132:25] I wish I could have done more examples.
[132:27] Um, kind of ran out of time, so I
[132:29] skipped a few of them. I had them out,
[132:31] you know, we could have done. Um, but
[132:33] I'm happy to run through any of those
[132:34] after, you know, you want guys want to
[132:36] do some coding or anything like that,
[132:37] I'm happy to stick around and go over
[132:39] more. Does anybody have any questions?
[132:43] No. Yeah.
[132:58] Yeah.
[133:06] >> Yeah.
[133:12] >> Yeah. Start with a value. start building
[133:14] value objects that you know are going to
[133:17] you might be able to use those in legacy
[133:19] code because those are going to come in
[133:20] easy. There's no dependencies for them.
[133:21] So you're going to be able to start
[133:22] using those right away. Um yeah, if you
[133:25] have a process fire an event and you
[133:27] know new code that's reacting to that,
[133:29] build a listener and then build your
[133:30] model off of that. Um you know we're
[133:33] doing we have that exact problem. you
[133:35] know, we're we're sharing code between
[133:37] we're sharing database tables between
[133:39] legacy code and new domain code. And
[133:42] that's our primary pain point is that
[133:45] legacy code didn't validate something.
[133:47] So like in the case of a brewery, you
[133:49] know, example, um you know, maybe we
[133:51] didn't validate the brewer's full name
[133:54] in our domain. We had setters, you know,
[133:55] so it just said set set and for some
[133:58] reason somebody's name wasn't we didn't
[133:59] know who it was. It wasn't set. Well,
[134:01] the new domain model expects all that to
[134:03] be there. So we end up having exceptions
[134:04] because we're looking at data that's
[134:06] invalid. Now it's proper that it's an in
[134:09] an exception because we can't proceed.
[134:11] We don't know who it was. So we stop
[134:14] there and we halt you know the the code
[134:15] halts. But that's our primary problem is
[134:17] that you know trying to share model and
[134:20] background stuff with the legacy code
[134:21] and the new code becomes difficult. If
[134:24] you like if you're going to share data
[134:26] and you build a new model, one of the
[134:28] things we'll do is like part of our m
[134:30] migration process is we'll actually read
[134:33] every entity out of the database and
[134:35] make sure that it can be rehydrated and
[134:37] operated on um making sure that we're
[134:39] basically validating all our old data
[134:41] going forward that way. So, you know,
[134:43] example would be find all the brewers in
[134:46] the brewery, loop through each one, call
[134:48] get full name, get um, you know, because
[134:50] that's going to force it to go through
[134:52] the constructor process, the value
[134:54] objects, and it's going to validate
[134:55] everything. It's the only way to
[134:56] guarantee that, you know, you've you
[134:58] mapping old legacy code to new domain.
[135:06] >> Yeah.
[135:08] >> Yeah. um make you can make a value
[135:11] object called middle name optional and
[135:14] you know they could be set or not set it
[135:16] could be empty string you know you can
[135:17] deal with that way you can have um
[135:20] multiple constructors so I mentioned
[135:21] that set a constructor thing you could
[135:23] have a full name that's first name and
[135:25] last name constructor you could have
[135:26] another constructor that's first name
[135:28] middle name last name constructor it all
[135:30] creates full name objects so now you're
[135:32] dealing in full names you're not worried
[135:33] about first middle last being there um
[135:36] but your your optional parameter
[135:38] constructor is handled at
[135:40] It's common with like email addresses
[135:42] and stuff like that where you may not
[135:43] have data. Um, avoid nulls if you can in
[135:46] your code. I mean, you'll have less
[135:48] problems that way.
[135:54] Okay. Thanks, guys. Uh, I appreciate
[135:57] your feedback. Um, and I have a lot more
[135:59] candy if anybody wants any of those nut
[136:01] rolls. You guys suffered enough, so you
[136:03] deserve one. But thank you.
