Domain Modeling Made Functional - Scott Wlaschin - KanDDDinsky 2019

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Great talk.

πŸ‘οΈŽ︎ 5 πŸ‘€οΈŽ︎ u/ThinkLargest πŸ“…οΈŽ︎ Aug 02 2021 πŸ—«︎ replies

The idea that there's little overlap between FP and DDD seems strange.

It's what the Typed FP community has been doing for ages (at least as long as I've known about Typed FP), without having a name for it. It's just using the type system. Some folks started calling it TDD (Type-Driven Development) at some point, possibly for compliance with managerial requirements. Others made up other names, like TFD (Type First Development), but none really gained universal acceptance.

πŸ‘οΈŽ︎ 1 πŸ‘€οΈŽ︎ u/aiij πŸ“…οΈŽ︎ Aug 02 2021 πŸ—«︎ replies
Captions
[Music] thanks everyone for coming this is the main modeling made functional and hopefully you haven't seen this before otherwise you'll know the answer to all these questions but before we get started I just actually have a little challenge for you which is here's some code this is f-sharp code which is what I'll be using the code really doesn't matter but this is some sort of record type and there's these fields and there's an email address and there's this flag called is email verified which must be true if you have proved with the own email by clicking a link in an so if you saw this code in a code of you would you be happy with it or would you want to change it and if you would want to change it why would you want to change it's using primitive types good any other yeah the state is so it's hard to understand using a boolean to represent something much more complex and yeah so I mean I bit to be honest I probably would have been fine with this a few years ago but I've sort of evolved my thinking about this kind of design and I'm going to try and explain in this talk I'm going to try to explain why you know how I've evolved and why you might want to think about changing stuff so domain modeling made functional my name's Scott volution I have a twitter handle and I have F sharp F on profit comm I'm gonna be using F sharp for all these examples but this is not really about a sharp per se it's about domain modeling so there's a whole functional programming people there's a whole group of for domain driven design people and I'm right in the intersection and currently this is a very small intersection because the functional programming people tend to be quite mathematical and they don't care about domain design and the domain of design people tend not to be into functional programming so you know hopefully this talk will persuade you these things actually can go together really really well people think the functional programming you know is good for mathematics and parallel processing and you have to have a PhD in computer science to understand it but I'm here to tell you that functional programming is really good for boring line of business applications lobbers I call these blubbers and you know this is things like accounting inventory management ecommerce boring stuff basically the stuff that most businesses do and most probably most of you and certainly what I used to do is this is the your day job this is what you get you make your living doing and I think functional programming is actually really good for this stuff it's not it's not just good for fancy stuff it's good for boring stuff so I'm gonna try and persuade you that functional programming and domain design can actually get on together they can actually be friends so there you go right so let's talk about design I'm just going to tell you my take on why design is important so just like any process software developed where is a process there's an input and you do something and there's an output and we love to talk about the process we love to talk about coding and testing and what's the best compiler and what's the best editor and vice versus vim versus Emacs and you know TDD versus BDD vus like we love to talk about all the stuff but if you if you remember the thing you know garbage in garbage out that means if you have bad inputs you will have bad output so if you can reduce the garbage coming in then hopefully you can have reduce the garbage coming out if you have bad garbage coming in the best compiler the best editor the best toolkit the best development processes will not help you you will still end up delivering something bad so the idea of trying to reduce the garbage coming in I call that design okay that's just a general word for just trying to understand what's going on before you start building it so how do we do design right I think that's why we're all here there's confidence to try and figure this out so there's two parts the first thing I'm going to take from agile contribution is getting rapid feedback and you know doing something and learning from it as fast as possible don't just take six months to deliver something try and deliver something in two weeks or one week or a few days and then from DDD I like to think of domain driven design is actually trying to create a shared mental model that everyone is on the same page everyone is thinking the same way everyone is using the same words and that shared mental model is also in the code it's not just in people's heads so here's a little picture of the shared mental model and we have experts domain experts subject matter experts they're sometimes called we have product people we have developers we have customers users whatever you wanna call them stakeholders everybody everyone involved should be sharing the same mental model that's my version of domain driven design they're all using the same language the same concepts and in particular like I said the code is part of this and the main model and the code and the documentation that all tried to be the same thing okay and the common language is in the code as well not just in people's heads so when I talk to non developers or even developers and they say can you really have the code represent the domain this is what people think code looks like especially non developers you know and it's like yeah this does not was you know this does not represent anything useful right this is so the kind of thing I'm talking about this is the kind of code I want to show you the kind of code that I think you should be writing so you have to try and guess what domain this is okay so if you can win points by trying to you know figure it out right so yes here we have our game and hopefully you've guessed that it's something to have a card game and there's something called a suit and there's something called a rank and there's something called a card and there's something called a player now what's interesting is I imagine that most of you don't know F sharp this is f sharp code and even though you don't know F sharp you could probably understand this code because this is about the concepts so we have our shared language down the side right and the way I explain this to non developers because I want non developers to read this code not just programmers you know the vertical bar is a choice so it's a club or a diamond or a spade or a heart the little star means a pair so a card is a it's a choice of a one pick suit and then pick a rank you have a list type and then we have this thing with an arrow so this is a to deal is an action it's not just a noun it's a verse something you do and we're going to represent in F sharp or in functional model we're going to represent this function so this says deck arrow deck star card alright so what does that mean so here is our function so functions have inputs and outputs and so our deal function the input is a deck of cards and after we've dealt a card there is now a new card on the table and we have a different deck because in in functional programming everything's immutable so the deck you don't you take the original deck you create a copy of the original deck with that one card missing so the input is an original deck and the output is a new deck and a card on the table so we represent that with deck arrow that's the input and then the output is a pair it's a deck and a card so that's how we represent an action in our model let's look at picking up a card so there's a card on the table and I have some hand I have my own cards and I'm going to pick it up so the input is my hand and the card on the table and after I've done the pick up the card on the table is missing and I have a new hand with a new card a so it's a different hand it's changed so again I represent this saying the input is a pair of things and the output is my new hand so this is how we model actions verbs workflows use cases stories whatever you wanna call them this is how we have model it in functional so here's a question do you think this is a reasonable amount of code to write for this domain I would say yes basically we fit the in the hole the domain is on one page it's pretty good we don't know it's not in 20 different files you know it's actually right there in one page do you think a non programmer I could understand this and I think yes in fact when I've been doing domain modeling exercises with people when I have everyone in a room I have programmers in them but I also have the domain experts and the subject matter experts and the product owners in the room and I'm typing all this stuff out and someone said this is great but where's the code because this is but it's like this is code right do you think that non-programmers if I'm typing this up on the screen do you think that non-programmers could provide useful feedback for example there's a deliberate mistake anyone see what the mistake is ace is missing right now I don't think you have to be a programmer to notice that right this is the kind of thing in real in real life I've had people tell me that I forgot something I you know I didn't I missed out one of the choices because you know but they can give me that kind of feedback right away so this thing about rapid feedback is really important now in agile the idea is to get feedback in weeks or even better in days by having a rapid prototype by having a minimal via product by having all these different words wouldn't it be great if you could get for you I can minutes rather than days that would be awesome so this process is great for that because what you're doing is I go I type all this stuff on the screen and I'm talking to people and getting information and I type up something like you know a deck is a list of cards and to deal you start with a deck and you know the function I just told you and the domain experts will probably say this deck thing you know that's not quite right when we deal in a real card game we don't use a deck we use a shuffled deck something called a shuffled deck and I don't know anything about card games and so I say okay so I can put it down like this make sure I got the right understanding and they say that's good and then I say you know what is a shuffled deck what is this special thing called a shuffle the deck they say it's just a list of cards so I write this list of cards now what's interesting here is that I don't say it's the same thing as a deck because they're both lists of cards the shuffle deck is a different concept from a normal deck from a none shuffle deck it's a different concept in the domain it's not a normal deck with a boolean flag saying whether it's been shuffled or not it's not a it's not they're two subclasses of a base class right a shuffle deck is just a different concept from a normal deck they might have the same representation they might both be represented by list cards but they're different concepts okay then I'm going I said okay so we've got this shuffled deck thing where do I get one of these shuffled decks okay how do I make a shuffled deck and the expert says well you do a shuffle now when you're talking to the experts they normally look like they act you know like you're stupid because you're asking all these stupid questions and you don't know anything and this is good because you should be asking these questions and learning about the domain you want to become a domain expert yourself so I said okay you need to do a shuffle so you know you this concept called shuffling and you start with a normal deck and you end up with a shuffle deck so what we've learned just by typing the stuff down I've got feedback within minutes now I don't have to wait a week until we've actually delivered something I've got feedback from the domain experts straightaway and I've learned something I personally have learned something new about the domain so in this in this idea you know we're all now using the same language as a developer next time I talk to the domain expert I will use a word like shuffle deck is a new concept I've learned so you know so this is very very fast process and you're basically writing code but not really you know you're learning very fast from the experts in an agile way but you're not actually writing code but what's cool is this code because it's code and it's not a document and it's not a UML diagram this code can be used as a template for writing your real cut so this is typically the first file in your project your f-sharp projects Islay this is the kind of these are the concepts now the rest the code is more complicated but the concepts of a string so here's our final piece of code no notice it's domain-driven it's not database driven so there's nothing about foreign keys there's nothing about tables right it's pure everything every single word here is something to do with the domain so this is what's called persistence ignorance in the domain of design book nothing to do with databases now obviously at some point you're going to have to put it in a database that's fine but that's not part of the domain logic that's something else you know but it's not part this bit here he's also not object-oriented there are no base classes there's no manager classes there's no factory classes everything is the main driven so in the real world you have this vocabulary you like suit and rank and card and so on so in the code you have the same vocabulary and you know the domain code should always be in sync with the real world so if you learn something about the real world like slow we've learned this new thing called a shuffle deck and a new thing called shuffle we put that in the code we represent that in the code itself we don't add a boolean a flag to the deck when we learn that we actually literally model it as a sip so that's the right way to do it the one way to do it is the Oh a round alright so don't use programmer jargon in your domain if you say we need some sort of class that manages all the players or we need a base class between the deck and a shuffle deck or we need an abstract card proxy factory beam right so this kind of jargon should not be in your domain now obviously in real code those complicated bits you know but that should be in the implementation side that shouldn't be in the domain side so don't if you start doing this you're doing it wrong ok so one of the agile philosophies is you know if you can make the design be in the code that's great because the code is the source of truth documentation goes out of date UML diagrams go at a date but the code is the design you know the code is the source of truth if you can have the design in the code that's the best possible solution so I think this is the a very good answer here and of course this is executable code like I see this would typically be the first file in your project and then the rest of the code within depend on this so if you change this the rest of the code will break until it fixes so this is the code and then the design are always going to be in sync and it's not just about the result of trying to create this this is not like an answer the process is a really important part of this the idea is trying to get everyone on the same page when you do it when I do these kind of workshops with people people argue about what is the right word to use are we using the same words and yeah that process of arguing and debating and discussing and and talking that is a really important part of it getting everyone on the same page is the actually the important part this is just the output of that but you know you don't just do this and then share it with everyone so here's the answer this is kind of a living document that's based on you're trying to just capture what people are thinking you're not trying to force it on people so the process is very very important and we'll see this a lot you know event storming it's all about the process it's not about the results you know context mapping all this stuff it's all about getting everyone on the same page and everyone to share their ideas okay so this is a key domained of a design principle which is communicate the design in the code so now if we go back to this let's look at the problems write this the problem with this is it does not communicate design in the code that's the main problem with it so for example which values are optional some values might be optional and some required so in this case the middle initial might be optional but it's not communicated here you might have some validation logic somewhere that checks it but that's not communicated what are the constraints I mean can the first name really be a million characters long I don't know how when you can put in a string but a lot can it be a million characters long can it contain non-printable characters can it contain line feeds and it just be all blank there's some constraints right it's not at all obvious it just says it's a string there's no guns no constraints anywhere so that's bad so let's say you're going to put it in a database you want to have the constraint that it's less than 50 characters and it's not like now you could put that constraint in the validation logic somewhere but that's not in the domain model that's somewhere buried in your code and it's very easy for people to bypass validation logic by mistake if it's actually in the code it's really hard to Piper which feels are linked where the consistency boundaries you know some fields can have to be changed as a group and some fields can be changed independently aggregate roots in the main driven design terminology that is not at all clear right so if you example these three fields have to be changed as a group as an atomic unit and these ones have to be changed as an atomic unit it's not at all clear from the design and finally there's some domain logic here we have this flag and when should it be true and when should be false so the answer is go and look it up in the documentation no that's the wrong answer you do not look it up in the documentation because you're going to get it wrong or someone's gonna forget and but I make a mistake right so this is does not communicate at all what you're supposed to do with this flag so the will the business will hear is if the email address is reset to a new value you have to set it to false until it's been verified ok you have to set it to false because otherwise it's a security that is not communicated in this design okay so here's our problems you know the main thing is it does not communicate a lot of important design decisions ok so all these four things and there's fully more we can fix these with functional domain modeling and by the end of the talk you'll see how I fix them so now before I show you how to fix them I need to tell you about functional programming and algebraic types so algebraic is a mathematical jargon word and I'm going to use the word composable instead composable types alright so what's a composable type so in functional programming the first thing is that types are not classes they're more like sets so if you have a function so let's just go back to the very beginning what is a function so a function is something that takes inputs into outputs so I'm going to use a little railway track analogy I'm gonna put a little tunnel on it something a tunnel of transformation something goes into this tunnel like an apple and it comes out as a banana ok so this is a function that turns apples into bananas ok so that's everything you need to know about functions you're an expert in functional programming okay so a lot of types so here we know this function what is a type well a type is just a name for a set of things so all the set of valid inputs all the set of valid outputs if you give that a name that's all the type is so it's not like a class at all so for example if all the possible inputs are integers we call that integer all the possible strings we call that string okay but we could have all the possible people in the world and we call that person we could have all the possible fruit in the world and they call that fruit now because it's a set you can have anything in a set right anything can be in a set so you can have a set of functions that's the set of things so this is a type called fruit - fruit functions functions that turn fruit into fruit so this is where it gets a little complicated because in functional programming you can have functions that have functions as input and they return other functions as output so you can have a function that turns this function which creates another function which has another function as a parameter so that's where it gets complicated but the basic principles are pretty straightforward right so that's everything you need to know about types so let's talk about composition so composition is like Lego and hopefully everyone knows how to use Lego so if you think about Lego all the pieces are designed to be connected they all have little bumps on them right that's just like the algebraic type system that's why I call a composable type system all the types are designed to fit together and that is only possible because these types are just sets and so just like sets you can do set Union and set intersection and cross products and all the stuff and that's only possible because there's no behavior unlike oo in functional programming data and behavior are completely separate things so the data is much easier to manipulate because it's just data and there's no behaviors so a composable type system so how can we compose types in a composable type system well we build new types some smaller types just like we build bigger things of Lego from smaller things Lego and we use and and we use or okay so what does and mean so let's say I want a fruit salad like one of the lovely fruit salads out there and I say well to make a fruit salad you need an apple and a banana and a cherry now notice I'm using the word and right so this is something that you get in all programming languages a pear or a tuple or record type or a struck so anything in F sharp we write like this this is a fruit salad has an apple field and a banana field and a cherry field and the apple field has to be an apple kind of apple variety and the banana has to be a banana variety in the cylinder so this is again if you look at it looks kind of like JSON alright so everyone's used to this kind of thing okay so now the other thing which is different from most other languages is using or so if I want to snack I might say I want an apple or a banana or a cherry and I'm using the word or does the kind of thing you can't get in seashell for Java and in f-sharp we use a vertical bar to represent the choices so it's an apple or a banana or a cherry now if it's an apple you have to know what kind of apple it is what variety of Apple it is okay so in I call these choice types because they they're choices the technical words of these things is some types or discriminated unions or there's lots of jargon words but in from a domain modeling point of view I call them choices because it's a choice so let's look at a real world example of how you build something from a choices let's say that you have a payment system and you create a cash check or card this is kind of old example because nobody takes checks anymore but you know for cash this there's no extra information it's just cash for checks you need a check number for credit cards you need a car type and a card number and an expiry date and all the other stuff so if you had to implement this model how would you do that okay now if you an oo person you would probably say well let's create a infinite an interface or an abstract based class like a payment method and then we create a subclass or an implementation for each possible choice so cash is a kind of payment methods check is a kind of payment method credit card is a kind of payment method and each of the subclasses has the information they need so that's kind of object-oriented version of doing it right I think most people would probably do it this way so let's look at how you do it in another completely different way so I'm gonna do the other way by building up bigger things some small things so I'm going to start with the smallest thing which is a check number and a card number and I'm gonna not going to use primitive types just like we said and we're not going to use them I'm going to because people don't talk about innocent strings they talk about check numbers and card numbers and a payment amounts and stuff right so I want to immediately define those things using words like that and then I will have a choice maybe a car type is a choice between the visa and mastercards and the credit card information I need is a car type and a card number so we have an or visa or mastercard and crack card type and card number so I'm using the or and the aunt right two different ways of building up types and then the payment methods going back to the thing I say it's cash or cheque or card and if it's a cheque there's some extra information which is the cheque number and if it's a card there's some extra information which is the card info and because I used or there's a choice type but I can keep going I'm gonna say okay I want a thing called a payment amount and I've got something called a currency which is euros or dollars so that's another choice and say I want a payment so a payment is an amount and a currency and a payment method so what I've done and that's it so there's a record type I built up a you know using and so what I've done is I've built bigger types and smaller types using the composition approach and so this is why composition I think is really good it's really a nice way of doing so you know if you're used to types in a language like C or C sharp you normally think of it as an annotation for type checking just to make sure that you can't pass a string into an inch by mistake I think you know that kind of thing it's kind of annoyance but if you think of it as a domain modeling tool you know dealing as a deck in a deck of cards it's a way to capture information about what you're trying to do but what's cool is if you do this you get both at once because the compiler will type check your domain model so if you get it wrong the code won't compile so having it trying to do domain modeling using a static type system like this is actually like carrying a compile-time unit tests you do not have to write a unit test for certain things because it literally won't compile if you get it wrong so that's pretty nice so static typing is great for the domain modeling statically type all the things okay all right now let's see what we can do with this type system so let's start off with the optional values okay so here we have our contact again and as I said the middle initial is optional how do we represent that how do we represent optional value so I'm going to start with a simpler thing let's say I'm just taking the length of a string so I have a function and the input is the set of all possible strings and the output is the set of all possible intz so I say this is a string to int function there's just one little problem is that in most languages null is a valid value for a string I can say something's a string and I can give it the value null and that is bad that's really really bad okay because null is not a string pretends to be a string if you if you assign it says yeah the compiler say yo null the string no worries when you're trying to use it I said no no no no not really a string I'm gonna throw no reference exception so I like to say that now is like cerumen of static typing if you know Lord of the Rings you know it's treason he's a traitor he pretends to be your friend and he's going to turn around and stab you in the back so null is a really bad idea so okay we're gonna say null is not allowed as a value don't ever allow now as a value so how can we represent missing thinks then well if you think about it we say is either a string or it's missing notice I use the word or there's a clue right so we're going to model it there's a choice it's either a choice between all the strings or nothing okay so an in F sharp you have to tag these with a little flag to make them very clear so we're gonna say that's some string and that's nothing and we're going to write it like this so optional string is a choice it's either some string or nothing and if it's some string there's some extra later which is the actual string that it is so you say this is just amazing optional strings fantastic and then let's create an optional int and then let's create an optional boolean and after a while you see I've got some duplicate code here maybe you can make them simpler so yeah what we do is we create a generic type option and that little tick is F sharps way of saying it's a generic type so if in C in c-sharp well Java you wouldn't so this is a type that you can write yourself now in most function languages it's built-in but if it wasn't built-in you could define it yourself because you're not defining some special new thing that the compiler needs to understand you're just using the composable type system as a toolkit for building new types and this is one of the new types you can build so algebraic types are very nice you can do a lot of stuff that you can't really do in traditional language ok so if we go back to this we just say instead of saying it's a normal string Mercedes an option of a string with the angle brackets and one of the nice things in F sharp is you can actually put the option afterwards so string option which is a little easier to read for non-technical people so the same thing that's nice and readable right what about simple values and constrained values so as someone pointed out we really should avoid primitive recession we shouldn't use int sand floats and bulls and strings in our domain because that's not what people use in real world ok I was once at a I want to do when I was younger and I was modeling something and I said well this is is this an integer or is it a float and they said is float something to do with water you know they did not know what I was talking about was just fair enough why should they they're not they're not a programmer so don't use the word float don't use the with integer use payment amounts or you know product number or whatever it is swim and on top of that not only should we not use forms of types but almost always integers and strings have some sort of constraints it's very unusual for string to allow a string to have a million characters in it or to allow an integer can you you know can you have integers which are 4 billion it's really maybe it's really unusual normally there's some sort of constraints on your things so if we solve for an email address you know it can't be empty has to have some sort of pattern matching like an app sign if you have customer ID which is an int it's probably not any int it's fully a positive integer right so there's some sort of constraint so email addresses are not strings custom IDs are not ins I mean another way to think about if you think about an int a customer ID the reason it's not an int is because you can't take the square root of it can you add to customer IDs and get another customer ID you know can you multiply it by five I mean doesn't make any sense right so they might be represented by int but they're not actually intz in the domain so what we do is we use rapper types to keep their distinct and here's two rapper types is the F sharp way of doing a rapper type we just put a a male of string so it's rapping a string just an ID of int is rapping at int right so rapping a string and rapping in this is very simple one line to do that so here's two raps types an email dress wraps a string and a phone number wraps a string but because we got wrappers around them they are now distinct types and that means you can't ever get an email address mixed up with a phone number and let's say we have a customer ID and an order ID and they both RepRap into senior database but they're not the same type they're different types they can't be mixed up you should never pass an order ID to somebody that's expecting a customer ID it just doesn't make any sense so model them as distinct types always so you get clear domain modeling and it also eliminates one kind of bug where you accidentally mix them up and you pass them in the mall or something okay so now let's look at how we create one of these things let's say I want to create an email address and I've got a string now if the string contains the @ sign then I'm going to take the string and I'm going to wrap it up in the email address that's good what happens if it doesn't contain the out sign what should I do should I return null no should I throw an exception no so what can I do what can what can I return you know I can't return an email address because it's not a valid email does nothing right exactly so one of the things if you if you throw if you return null or you would throw an exception or something if you look at the signature it says you give me a string and I'll give you an email address and that is the lie that is wrong so it's a deceptive piece of information so yes what I'm going to do is return something or nothing just like we did with the optional thing and that's much better so if you look at the signature it now says you give me a string and I might give you back an email address maybe if you give me a good string so this is now this is not a lie this is much clearer this is better documentation - this is now you know a self-documenting code better than the other one and we can do the same thing let's say we want to have strings that are 50 characters long so we create a wrapper type for them and then we create a special constructor for them and if it's less than 50 that's good and if it's more you know if it's not then that's bad and if we look at the signature it says you give me a normal string and I might give you back a string 50 if it's good so this kind of validation is normally just done at the edges of your program you know when you get data coming in from a JSON thing or from and whatever you have to validate it but once it's validated once you've got your string 50 or your email address it's immutable so it can never change which means you never have to validate it ever again so you never have to do any kind of defensive programming in your core domain code you never have to check for null you never have to check that something's about it because once it's been validated at the edge you can pass it around in full confidence that it can never go wrong so that's one of the nice things that okay here's something which is a problem can you really have 999,000 items in your shopping cart or your shopping cart no this is probably a bug because the person who wrote this ecommerce site probably used an integer to represent the quantity right and you hear stories of you know things overflowing to minus 1 or minus 32 K or something crate you know this should never happen you should never be able to order that many things so that's because they didn't do domain modeling so the right answer is to create a type a wrapper type now to be honest when I've done ecommerce sites I have never bothered to do this before because it's a lot of work creating these wrapper types so you know creating a new type just for this domain or just for this little piece of code is not something that people do generally because it's like I say it's a lot of work but if it's one line of code it's a lot easier so if your language makes it easy to do you're gonna like to do it much more okay let's look at the constructor so if it's greater than zero and less than 99 that's good and if it isn't it's bad when we look at the signature says you give me integer and I might or might not give you back an order line quantity now what's interesting about this is that 0 is not a valid value it has to be greater than 0 so when you have the one of those minus buttons and you know Dan you know two and you take away one that's one you take away another one it goes down to zero you can't make one of these things so what that means is that when you're coding your UI or your back-end or whatever you are forced to deal with the case when it goes down to zero you can't accidentally forget about it so this is the idea where you know it forces you to think about that situation because you don't actually have a valid quantity at that point so you what are you going to do I don't know you probably going to remove it from the shopping cart but whatever the answer is you're going to have to think about it you can't just forget about it you know and cause a bug so if we go back to our contact here's what we had originally so the first thing we did is make an option and then the second thing we did is we created all these constraint i p-- s-- of all these different choices right so it's looking better already the next thing is we need you know if you're a domain driven design person you'll recognize this is an entity so we need to add and enter the ID and notice that I'm not it's not an int it's something called a customer ID special to customers or contact it really should be a contact ID I guess and then here's our two things which really should be separate well I can just rather than having them in one big structure I can just pull them out into two separate structures and one of the nice things about having the data separate from the havior is that this kind of pulling things apart and putting them back together again is really easy to do because there's no behavior so you know refactoring is very very easy right so that's that now what about this last thing here where is it it's email verify flag we're going to do with that so let's talk about getting rid of flags altogether what we're going to do is replace flags with choices so here is our situation we have this flag and we have some business rules and it says if the email has changed that verified flag has to be set to false because who knows you know you've said add anything and also if you do want to set it to true you have to use a special verification service that compares you know that checks the email hash and all that other stuff you can't just set it a tree because that's a security issue you have to go through this special verification service so that's a business rule can we enforce the business rule in the design without having some logic somewhere and the answer is yes we can so as it stands anyone can set it to true it's a security problem and it's just bad it doesn't communicate the property issue so here's what we're going to do we're going to create another wrapper type we're going to wrap an email address and we're going to call it a verified email address so we have a wrapper around a wrapper and this is one of my favorite principles which is no problem that can't be solved by wrapping it in another type we're going to see that so by by doing this what we've said is that a verified email address is not the same as a normal email address right the whole thing is it's a distinct type just like a shuffled deck is not the same thing as a normal deck we've created a special concept in the domain and then we're going to have a verification service now the verification verification service is a function which has two inputs and one output and the input is an email address and a verification hash of some kind so you give me the email address and I'm going to give you back a verified email maybe because the hash might not match up or whatever I mean there's various reasons it might not work it's very clear that it might not work sometimes so you have to deal with that okay so I might give you back a verified email okay so that's the a ssin function and then here's the cool bit we take this contact information and we change it into a choice we say it's either a choice between an unverified email and a verified email so this flag has disappeared unverified or verified now in the unverified case the information that goes along with it is just a normal email address so anybody can set it to be unverified all I need is any old email address to set it to the verified case I have to have the verified email that's the thing I need to have to put it into the verified case where do I get one of these verified emails from I have to get it from the verification service right so the and it's me now what I typically do is make the constructor private or something so that nobody else can pick one of these things except the verifications but by doing that now just like the business rule says I have to get one from the verification service the only person who can create one of these things so that's that business all I have to go through the verification service to get Authority so in that for that case to be true okay so by replacing the boolean with a choice its first of all clearer and secondly I've actually enforced those business rules I think it's amazing all right so to create that case you have to go have a verified email and to create the verified email you have to go through the service so the business rules are automatically enforced so this is a really nice way of doing it so I'm modeling the domain more accurately and I'm also getting my business rules right so if we go back to our challenge we started off with one thing we now have a whole bunch of things we have an email address and a verified email and contact information and a personal name and a contact and a verification we have a whole bunch of things that we didn't have at the beginning so what's good about this is the lot of the stuff that we had at beginning are now solved right which value is optional where you have this optional concept what are the constraints well we have something called a string 50 we have something called an email address it's not just strings anymore which feels are linked by breaking them into separate groups is much more obvious and is the domain logic clear yes it is unlike that boolean flag it's crystal clear that there's these things called unverified and verify now what's also cool about this is the ubiquitous language the common language is evolving along with the design we started off with one thing and we have all these new things but this is not I wouldn't say this is more complex because these are actual real words that real people use they will talk about something called a personal name they talk about something called verified email and uh normally I mean these are words in the domain so we're actually just representing that the main better we're not making it more complicated we're actually if anything we're making it more clear and of course this is compatible code this is not your Mel diagram and now the other thing that in in Eric's book he talks about refactoring towards deeper insight which means when you learn something about the domain often that opens up a whole new way of thinking about certain things that you didn't really that you weren't really aware of before so for example we just created this thing called a verified email to solve that particular problem but once we have this concept called a verified email you might find all sorts of other uses for it in the domain right so this new curve often like say you find a new concept and then you say well okay this rule we have a business rule that we didn't think about that says you have to send parceled recess only to verify the emails now before we would have something where we check the flag and only if it's set to true do we actually do the thing again we could easily make a mistake and forget the business rule but if we with this new thing called a verified email we just say sending a password reset it needs a verified email so our domain modelling is actually more it's now self documenting to send a password reset you need a verified email okay I don't have to write a unit test for it I don't have to check any billion flags you know self documenting and it's a compile time unit test all right one more thing sir let's say that times change and we add something called an address a postal address to our context so we have an email and an address and we have a new business rule that you have to have a contact must have an email address or a postal address there must be one way of contacting you okay at least one way of contact now this design as it stands does it meet that requirement no because as it stands both over at both fields are acquired all right things are always required unless you tell they're optional ok well I'll make them both optional and that doesn't work either right because they could both missing so our problem is how do we model the facts that how do we model this business law well we could say well let them both be missing and I'll have some special validation logics on whether it checks if they're both missing and throws an exception as I know we're not going to do that we're not trying model it in the domain can't can we do that the answer is yes of course we can so a great phrase is make illegal states unrepresentable so rather than letting people do something and then having validation logic or special code checks that's a bad thing just don't let it happen in the first place okay so how can we do that here's our business rule so if you think about it that means you can have an email address or a postal address or both right that's what the business will now I'm using the word or there's a clue now how am I gonna model this there's three possibilities okay well I'm gonna model it like this email only or address only or both right so by modeling it this way and then I you know I stick it in my contact by modeling it this way there's only three possibilities so it's self-documenting I don't have to have any special code that checks the fourth case because there isn't a fourth gate I literally cannot compile the fourth case where they're both missing I don't have to write a unit test for it he literally cannot happen it is not possible for me to write bad code and it's good if I'm a new developer and I'm coming on to your team and I'm trying to understand what are the business rules I don't have to look at documentation I don't have to ask somebody what are the business rules for emails or whatever make a look at the code code is really really explicit it's kind of ugly I want to say it's beautiful code it's ugly but is explicit you're not going to make a mistake right so there's the idea of in try to encode the requirements in the type as much as possible now you can't always do that the rules of Poker for example you know like with the card game you can encode a lot of the stuff you cant encode everything but you can more than you think you can get at least get all the big concepts down and the concept of dealing and picking up cards and you know all that stuff so static types are almost as awesome as this cat on a unicorn right so communication is two-way it's okay to push back a contact must have an email postal address you might say well that's actually really a bad thing because that might not be you know it's hard to implement so let's change it to be a Content must have at least one way of being contacted so that's better so instead of having both things we'll say you've got one way of being contacted so here's all the different ways we can contact you we can contact you by email or address or whatever and then these we go back and we say you've got a primary way of being contacted and a secondary way the prior way is acquired and the secondary way is optional so this is a better design okay this is much more common design because you can extend you say we need add Facebook you need to add texting in their Twitter or whatever you can just extend this with all the different choice all right so to sum up we've used code to represent the shared mental model we've shown how designs can evolve this is really important you don't just want to get it right first time it's not a big design up front designs will evolve you need to be able to have some way of embracing the change without being scared to make changes and I think you can see in this approach it's quite easy to make changes and still be confident that you you know you have a good design and this refactoring towards deeper insight is William so static typing I think is very very good here if you're trying to do this in Python or Ruby or something I really wouldn't be as confident about making all these so composable type systems are awesome you could see that we've got choices rather than inheritance we used options instead of null we used wrappers over and over again and wrapping things and more wrappings and then finally this thing in your head always try to make illegal states unrepresentable if you so if you like this talk I have this talks on video and I'm going to pull you put this one up there at some point I have more videos I have a book if anyone has the book and they want me to deface it I'll be quite happy to do that and contact me on Twitter if you got any questions and if you like this one stay for the next talk in this room Romans talk which is like a deeper dive into the same kind of thing thank you very much [Applause] [Music] you [Music]
Info
Channel: KanDDDinsky
Views: 29,212
Rating: undefined out of 5
Keywords: KanDDDinsky, DDD, KDDDconf
Id: 2JB1_e5wZmU
Channel Id: undefined
Length: 50min 49sec (3049 seconds)
Published: Fri Dec 06 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.