Domain Driven Design with the F# type System
This is a modal window.
Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.
Formale Metadaten
Titel |
| |
Alternativer Titel |
| |
Serientitel | ||
Anzahl der Teile | 170 | |
Autor | ||
Lizenz | CC-Namensnennung - keine kommerzielle Nutzung - Weitergabe unter gleichen Bedingungen 3.0 Unported: Sie dürfen das Werk bzw. den Inhalt zu jedem legalen und nicht-kommerziellen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen und das Werk bzw. diesen Inhalt auch in veränderter Form nur unter den Bedingungen dieser Lizenz weitergeben | |
Identifikatoren | 10.5446/50868 (DOI) | |
Herausgeber | ||
Erscheinungsjahr | ||
Sprache |
Inhaltliche Metadaten
Fachgebiet | ||
Genre | ||
Abstract |
|
NDC Oslo 2014140 / 170
2
3
5
6
8
11
12
16
21
22
23
27
31
35
37
42
43
45
47
48
49
50
52
54
56
57
58
61
65
66
67
71
74
77
80
81
83
84
85
87
88
89
90
91
94
95
96
97
98
100
102
107
108
112
114
115
116
118
120
121
122
123
126
127
128
130
133
135
137
138
139
140
141
142
143
144
145
147
148
149
150
153
155
156
157
158
159
160
161
162
163
166
169
170
00:00
Problemorientierte ProgrammierspracheSystemprogrammierungAdressraumE-MailZeichenketteNebenbedingungInformationsmodellierungMarketinginformationssystemProgrammierungSondierungE-MailCASE <Informatik>ForcingDatenfeldSystemprogrammierungBridge <Kommunikationstechnik>AdressraumEndliche ModelltheorieFahne <Mathematik>RechenschieberNummerungProgrammierspracheVerschlingungSchreiben <Datenverarbeitung>VideokonferenzSpieltheorieOvalTelekommunikationFlächeninhaltNebenbedingungZeichenketteMultiplikationsoperatorGeradeBitKartesische KoordinatenMathematische LogikÜbertragWort <Informatik>MathematikerinMereologieKette <Mathematik>SoftwareentwicklerProgrammierungFramework <Informatik>Twitter <Softwareplattform>Überlagerung <Mathematik>GenerizitätFunktionale ProgrammierungSoftwarewartungTouchscreenPunktProzess <Informatik>SichtenkonzeptEin-AusgabeFunktion <Mathematik>Umsetzung <Informatik>EntropiecodierungCodeProblemorientierte ProgrammierspracheSoftwaretestGüte der AnpassungKategorie <Mathematik>HilfesystemKonfiguration <Informatik>ValiditätArithmetisches MittelReelle ZahlObjekt <Kategorie>VererbungshierarchieInterface <Schaltung>StereometrieDatenparallelitätTermCOMFunktorFehlermeldungBoolesche AlgebraModifikation <Mathematik>PaarvergleichDifferenteMonade <Mathematik>Rechter WinkelAlgebraisches ModellKontinuierliche IntegrationAnalytische FortsetzungComputeranimation
09:04
StereometrieKovarianzfunktionProgrammierungFunktionale ProgrammierungTaskParallelverarbeitungAlgorithmusComputerGeradeArithmetischer AusdruckProblemorientierte ProgrammierspracheZeichenketteCodeGleichheitszeichenWeb-SeiteInhalt <Mathematik>Hash-AlgorithmusFehlermeldungEin-AusgabeAppletProgrammierspracheObjekt <Kategorie>BitCASE <Informatik>EntropiecodierungDifferentePaarvergleichKonstruktor <Informatik>Kategorie <Mathematik>DifferenzkernRichtungCompilerEindringerkennungKlasse <Mathematik>Quick-SortSchreiben <Datenverarbeitung>MultiplikationsoperatorProgrammierungAlgorithmusReelle ZahlGeradeSoftwareentwicklerTelekommunikationMathematische LogikDecodierungVersionsverwaltungDefaultMereologieInhalt <Mathematik>Wort <Informatik>Kartesische KoordinatenMathematikProzess <Informatik>Güte der AnpassungWeb SiteSystemprogrammierungFunktionale ProgrammierungCodeFront-End <Software>Dreiecksfreier GraphProgrammfehlerIterationGewicht <Ausgleichsrechnung>TextbausteinHash-AlgorithmusInformatikData-Warehouse-KonzeptKontinuierliche IntegrationKomponententestAusnahmebehandlungDatensatzRechenwerkWeb logRechter WinkelFokalpunktIntegralPunktZeiger <Informatik>SichtenkonzeptKontrollstrukturExistenzaussagePhysikalischer EffektE-MailFaltung <Mathematik>Problemorientierte ProgrammierspracheComputeranimation
18:07
Ein-AusgabeFehlermeldungInvarianteMagnettrommelspeicherCodeZeichenkettePaarvergleichObjekt <Kategorie>DefaultProgrammiergerätProblemorientierte ProgrammierspracheDatenmodellTelekommunikationKontextbezogenes SystemSystemprogrammierungE-MailCOMProdukt <Mathematik>ObjektverfolgungProgrammierspracheKeller <Informatik>Fächer <Mathematik>PlastikkarteBimodulSpieltheorieRhombus <Mathematik>RankingAuswahlaxiomEin-AusgabePunktPlastikkarteWort <Informatik>StrömungsrichtungWeb-SeiteRechter WinkelSummengleichungProblemorientierte ProgrammierspracheMailing-ListeCASE <Informatik>ProgrammierspracheRhombus <Mathematik>CodeZeitrichtungFunktion <Mathematik>TabelleKlasse <Mathematik>Elektronische PublikationDatenbankMathematikKonstruktor <Informatik>Objekt <Kategorie>EinsQuick-SortAdressraumE-MailKategorie <Mathematik>Endliche ModelltheorieKontextbezogenes SystemKartesische KoordinatenTelekommunikationProdukt <Mathematik>ExpertensystemBitMereologieProzess <Informatik>SoftwareentwicklerFramework <Informatik>Arithmetisches MittelSpieltheorieDifferenteC sharpGebundener ZustandF sharpDefaultBenutzeroberflächeSuite <Programmpaket>UnrundheitKohäsionComputerspielProgrammierungSichtenkonzeptOrdinalzahlVerschlingungHMS <Fertigung>Divergente ReiheMultiplikationsoperatorDezimalzahlResultanteNotepad-ComputerInformationsspeicherungAuswahlaxiomSystemverwaltungFunktionale ProgrammierungProgrammiergerätInvarianteSoundverarbeitungQuaderInterface <Schaltung>Vorzeichen <Mathematik>Flussdiagramm
27:11
PlastikkarteRhombus <Mathematik>BimodulSpieltheorieRankingCodeSystemprogrammierungOperations ResearchFunktion <Mathematik>AuswahlaxiomTupelSelbstrepräsentationNummerungDämpfungFunktion <Mathematik>MereologieKlasse <Mathematik>BitMultiplikationsoperatorNummerungPlastikkarteFunktionale ProgrammierungKonstruktor <Informatik>Ein-AusgabeInformationProblemorientierte ProgrammierspracheCodePhysikalische TheorieKartesische KoordinatenBoolesche AlgebraSystemprogrammierungTotal <Mathematik>SchaltnetzZwölfZweiBenutzerbeteiligungVisualisierungMultiplikationSampler <Musikinstrument>Innerer PunktRechter WinkelQuick-SortMailing-ListeGanze ZahlAlgebraisches ModellProgrammierspracheArithmetisches MittelReelle ZahlWort <Informatik>Produkt <Mathematik>AuswahlaxiomSummierbarkeitEinsSymboltabelleDateiverwaltungBlackboxVorzeichen <Mathematik>Zentrische StreckungAggregatzustandQuantenzustandMessage-PassingE-MailEntropiecodierungSystemaufrufDatenverwaltungFlächeninhaltZellularer AutomatForcingPunktDifferenteQuellcodeObjekt <Kategorie>
36:15
NummerungPlastikkarteAuswahlaxiomVererbungshierarchieVersionsverwaltungInterface <Schaltung>Problemorientierte ProgrammierspracheInformationsmodellierungSystemprogrammierungHydrostatikRechenwerkSoftwaretestZeichenketteDickeKonfiguration <Informatik>E-MailAdressraumKartesische KoordinatenInterface <Schaltung>KomponententestAuswahlaxiomKlasse <Mathematik>PlastikkarteQuick-SortMereologieKontextbezogenes SystemKonfiguration <Informatik>Demoszene <Programmierung>NummerungEin-AusgabeFunktionale ProgrammierungMatchingHydrostatikSpeicherabzugHochdruckProgrammiergerätZeichenketteDickeÄquivalenzklasseProblemorientierte ProgrammierspracheKategorie <Mathematik>Konstruktor <Informatik>CodeElektronische PublikationGeradeInverser LimesInnerer PunktEndliche ModelltheorieSystemprogrammierungMultiplikationsoperatorE-MailAdressraumCASE <Informatik>SummierbarkeitFunktion <Mathematik>Zeiger <Informatik>Mailing-ListeParametersystemPhasenumwandlungBoolesche AlgebraSystemzusammenbruchZweiAbzählenEindringerkennungBefehl <Informatik>ProgrammbibliothekEinfache GenauigkeitInformationsspeicherungMathematische LogikEntropiecodierungRechenwerkProfil <Aerodynamik>DifferenteIntelligentes NetzKontrollstrukturRechter WinkelOvalCoxeter-GruppeSystemaufrufStichprobenumfangQuelle <Physik>SelbstrepräsentationKraftPhysikalischer EffektPunktVererbungshierarchieAggregatzustandProgrammierspracheNabel <Mathematik>AbstraktionsebeneComputeranimation
45:18
ZeichenketteAuswahlaxiomE-MailAdressraumNummerungKonfiguration <Informatik>DickeProblemorientierte ProgrammierspracheNebenbedingungSchlussregelFahne <Mathematik>Mathematische LogikProgrammverifikationAdressraumBusiness ObjectE-MailLoginOrdnung <Mathematik>NamensraumDifferenteInnerer PunktGrenzschichtablösungQuick-SortGanze ZahlSelbstrepräsentationProgrammfehlerCodeElektronische PublikationGeradeKlasse <Mathematik>FehlermeldungCASE <Informatik>EntropiecodierungKategorie <Mathematik>ProgrammverifikationZweiAbfrageKonstruktor <Informatik>Primitive <Informatik>Zeiger <Informatik>Dienst <Informatik>EindringerkennungTeilbarkeitElektronische UnterschriftMultiplikationsoperatorProblemorientierte ProgrammierspracheValiditätSystemprogrammierungPunktAusnahmebehandlungFunktionale ProgrammierungZeichenketteMatchingKonfiguration <Informatik>BitSoftwaretestCompilerNebenbedingungNummerungAuswahlaxiomBimodulMessage-PassingNormalvektorSchlussregelHash-AlgorithmusDemoszene <Programmierung>Web SiteRechter WinkelComputerspielNegative ZahlAggregatzustandComputerunterstützte ÜbersetzungForcingDatenbankWeg <Topologie>Quelle <Physik>ProgrammierungVorzeichen <Mathematik>Güte der AnpassungComputeranimation
54:22
Mathematische LogikE-MailAdressraumKonfiguration <Informatik>ZeichenketteBinder <Informatik>NebenbedingungProgrammierspracheCodeAggregatzustandDoS-AttackeSummierbarkeitProdukt <Mathematik>AuswahlaxiomVererbungshierarchieDienst <Informatik>Notepad-ComputerFunktionale ProgrammierungUnternehmensarchitekturMobiles InternetProblemorientierte ProgrammierspracheSystemprogrammierungTwitter <Softwareplattform>NormalvektorDatenbankAusnahmebehandlungAdressraumProgrammverifikationE-MailDienst <Informatik>Ein-AusgabeKonfiguration <Informatik>DatenfeldSystemprogrammierungCodeProblemorientierte ProgrammierspracheAuswahlaxiomBoolesche AlgebraMultiplikationsoperatorDifferenteMereologieProgrammierspracheDiagrammOrdnung <Mathematik>NebenbedingungElektronische PublikationDickeMathematische LogikSchlussregelCASE <Informatik>AggregatzustandTwitter <Softwareplattform>EreignishorizontFehlermeldungProjektive EbeneVerschlingungHilfesystemGrenzschichtablösungProgrammfehlerEDV-BeratungInformationQuick-SortEinsEinfache GenauigkeitFunktion <Mathematik>NummerungZeichenketteInhalt <Mathematik>Rechter WinkelSystemplattformBridge <Kommunikationstechnik>DatenverwaltungGruppenoperationDesign by ContractFunktionale ProgrammierungMomentenproblemQuellcodeMetropolitan area networkFahne <Mathematik>DatenstrukturFamilie <Mathematik>ZweiSystemaufrufVorzeichen <Mathematik>Computeranimation
01:03:25
Twitter <Softwareplattform>SystemprogrammierungProblemorientierte ProgrammierspracheComputeranimation
Transkript: Englisch(automatisch erzeugt)
00:03
Okay, can everyone hear me okay? All the way at the back, can you hear me? Yep, okay, good. Right, so this talk is domain-driven design or domain modeling with the F-sharp type system.
00:22
It's not really, I'm not going to go through a lot of domain-driven design, it's more about how can you construct a domain model with the F-sharp type system. So I'm going to be explaining about algebraic types and how you can use that for designing. So a little bit of domain-driven design but mostly about the F-sharp type system and algebraic types.
00:43
So hopefully that's what you're interested in. So I'm going to start with a challenge here. How many things are wrong with this design that you see on the screen? So you probably have seen this kind of code millions of times. It's a very simple contact to a person.
01:01
There's a first name, middle initial, last name, an email address and a flag to say whether the email address has been verified by sending the contact an email to verify. So you might think, well, I'm not going to say it's about syntax. I'm actually talking about from a design point of view,
01:22
how many things are wrong with this code? And I think you'll find there's quite a number of things wrong. Hopefully I'll surprise you with some of the things that I'll say. So before we start on that, though, let me just talk about the software development process.
01:40
And just like any process, there's an input and there's the process and then there's the output. And one of the things that we do in conferences like this is we normally talk about the process, which is the coding and the testing and the tooling and continuous integration and continuous deployment and all these things.
02:01
I think one of the problems with that is there's this well-known truism, which is if you have garbage in, you get garbage out. So we tend to focus on the process, but really what we should do is focus on having less garbage coming into the process in the first place. So what I'd like to do in this talk is really focus on reducing the amount of garbage that comes in
02:22
and then hopefully that reduces the amount of garbage that comes out. So reducing the amount of garbage that goes in is really the design process. So if you have a good design process, you can actually guarantee good output. If you have a bad design process, all the best tools in the world will not necessarily guarantee a good output, or at least there's a lot of extra effort
02:41
to guarantee that extra good output. So that's what this talk is about. How can we have good design and how can the language that you use help you with that design? And I think that F-sharp is actually an excellent language for design purposes, not for coding, but for design. So here is our contact again,
03:03
and I propose to you that this is actually a very garbage-riddled design. If you use this design, you might have lots of errors in your code that would be completely preventable if you used a different design. So the first question of this design,
03:23
one of the reasons why this is not a good design, is it's not very clear which values are optional. So the middle initial probably is gonna be an optional property or an optional field. The first name might be required, the last name might be required, the email address might be required. It's not clear from this design. So when it comes time to coding,
03:40
you won't actually know. So there's gonna be a communication gap straightaway between coding and the design. Now you might code it so it's optional, but that's gonna be buried in your code, and when someone else comes along, they're gonna have to look at your code to find out whether it's optional or not. So in this case, the middle initial is optional. It'd be nice if we could somehow put that optionality into the design itself.
04:03
What about the constraints? So if we take the first name, it says it's a string. So does that mean you can put two billion characters into the first name? Can you have line feeds in the first name? You know, there's actually, there are constraints on the first name,
04:20
which a string does not represent. Now again, you probably have some validation logic in your code, but that's in the code. Can we actually represent that in the design? So in this case, the string is not allowed to be more than, say, 50 characters. What about which fields are linked together? So what I mean by that is in terms of concurrency.
04:41
I can, if two people, if one person updates the first name and another person updates the last name at the same time, that's going to be a concurrency error. But if one person updates the first name and someone else updates the email, that might not be a concurrency error. Is there a way of communicating that through this design? In this case, it's pretty obvious that the name part is separate from the email part.
05:03
And finally, what's the domain logic? So we have this is email verified flag. That means I sent you an email and you clicked on it so I know that you actually are the owner of that email. But where is that logic in this design? I mean, that's just a Boolean. Anybody could set it to true or false.
05:20
I mean, I've called it something, but the logic is not encoded in the design. So in this case, the logic is that if the email is changed, I have to reset it to false. If you change your email, I have to re-verify you. So here's all the questions again. Which value is optional? What are the constraints? Which fields are linked? And what's the domain logic?
05:41
And I think that F-sharp can actually help with all of these questions. So domain modeling in the F-sharp type system. My name's Scott Voloshin. My Twitter handle is scottvoloshin, very cleverly. I have a website called fsharpforfunandprofit.com. And I have a consulting company called F.P. Bridge.
06:03
And if you want these slides, if you go to fsharpforfunandprofit.com slash ddd, the slides will be there and some other links will be there too. And I'll post the video there too when it's ready. Right, so domain-driven design. Hopefully, how many people have read domain-driven design, most of you?
06:20
Yeah. So the main quote really from domain design is you focus on the domain and the domain logic rather than on the technology. So you're trying to capture something in a way that anybody can understand it, any business person can understand it. It's not about writing it in code. It's not about using clever technology. It's about communication really.
06:43
So domain modeling and DDD is quite a big topic. Functional programming is another quite big topic. This talk is really going to be on the overlap of those two things. Okay, it's not a particularly common area, but I think it's actually a quite profound thing to talk about. So first of all, I'm going to spend a little bit of time
07:01
demystifying functional programming because I think a lot of people are kind of scared. So I just want to take away some of that fear. I want to talk about functional programming is actually a useful thing for real world applications. It's not just some academic theoretical thing. I'm going to compare F-sharp and C-sharp. How many people use C-sharp here? Everybody, right.
07:22
And I'm going to then talk about the F-sharp type system and then I'm finally going to talk about how you use the F-sharp type system to design real things, really practical things. So let's start with demystifying functional programming. Why is it so hard? I think a lot of people are put off by it because it's very scary.
07:42
You have all these words like functor and catamorphism, currying and monad. Right, these are very scary sounding words and I think the reason is, yeah, there's Homer, he's quite scared. The reason is because the mathematicians got their first and they named these words. Now if we renamed the words differently,
08:02
they wouldn't be so scary. So for example, I think these words are just unfamiliar, not scary. So if we named things like functor, if we called it mappable instead of functor and if we called it collapsible instead of catamorphism and we said aggregatable or chainable. So those are still words you might not understand
08:22
what they mean, but they don't sound so scary. So here's Homer, he doesn't understand them but he's not frightened by them anymore. It just means it's something you have to learn just like you have to learn entity framework or you have to learn link or you have to learn in WCF, if anyone knows how to do that. So I tell you what is really scary
08:40
is object oriented programming. You just don't think it's scary because you already know it. But if you're a new person coming to programming and you're faced with object oriented programming, this is what you have to deal with. You have to deal with all these scary words like polymorphism and inheritance and interface and generics and covariance and contravariance and solid. And solid itself is five different things,
09:02
SRP, OCP. There's a lot of things you have to remember. Each one of those abbreviations and you've got IOC and DI and ABC and MVC and AOP and thousands of little things you have to know. So that's what really scary. So functional programming is actually a lot less complicated than object oriented programming.
09:21
It's just that you're more familiar with object oriented programming. The other thing about all these words is you're not going to need it for this talk. So I'm not even going to talk about any of them. All right, so don't worry that I'm going to be mentioning them. So let's talk about functional programming for real world applications.
09:42
So you've probably heard that functional programming is good for mathematical stuff and it's good for complicated algorithms and it's really good for parallel processing but you need a PhD in computer science to understand it. So the first thing is actually true. It is good for all of those things. It's so not true that you need a PhD.
10:02
I'll tell you what I think functional programming is really good for. It's really good for boring line of business applications. Blobbers, I call them. So I think, how many people make their living writing blobbers? Yeah, pretty much everyone writes boring line of business applications.
10:20
So that's e-commerce websites and accounting packages and ETL data warehouses and I don't know, all sorts of stuff, backend infrastructure, whatever. So that's basically what we do every day. But I think functional programming's actually really good for that. So let me tell you why. If you're going to be doing blobber developments,
10:41
first of all you need to express requirements very clearly because you're working with customers and sometimes they don't quite know what they're talking about and you need to have really good communication. Secondly, you need to have a rapid development cycle because you want to get the code out there before the customers change their mind which they probably will next month or next week. So if you can have a rapid development cycle
11:00
then you can actually get the code to deliver them and then they're happy. And finally, you need high quality because there's nothing worse than trying to fix a bug for something you did six months ago. You want to be working on new stuff, you don't want to be fixing old stuff. And then you look stupid if you have bugs as well. So what's interesting is that all these requirements actually is where the agile movement came out of.
11:21
XP came out of Chrysler. The whole thing of expressing requirements clearly, that's behavior-driven design and on-site customer and all this kind of stuff, rapid iterations. F-sharp is good for that
11:41
because it's concise and it's really easy to communicate. So it's actually better than C-sharp for that. The rapid development cycle, again that's where you have your continuous integration and your continuous deployment. F-sharp is good for that. It has a REPL where you can actually code interactively and it has many, many conveniences to avoid boilerplate in your code. So you can actually churn out a lot of code
12:01
much faster in F-sharp. And finally, the high quality deliverables, of course you have your unit tests and so on. And in F-sharp you can do unit tests as well just because it's a .NET language, you can use N-unit. But the type system can actually be used to ensure correctness at the design time. And that sounds, you can actually encode business logic into the types.
12:22
That sounds like a very strange thing to be able to do, but you can actually do it. There's one other thing which is really important. If you're doing boring business applications, you need to have a bit of fun. The applications might be boring, but hopefully at least your coding is fun. Now the nice thing about F-sharp is F-sharp is actually a fun language to code in.
12:43
I don't think anyone thinks that C-sharp or Java are fun languages. They're useful, but they're not exactly fun. I think Python is more fun. Ruby is a bit more fun. F-sharp is fun. And in fact, fun is even a keyword in F-sharp. So they know what they're talking about.
13:01
So F-sharp is really good for blobber development. So let me just show you some F-sharp and C-sharp code and you tell me which one you think is better for domain-driven design. So in domain-driven design we'll start with a simple immutable object. In domain-driven design, that's called a value object.
13:22
So how would you implement a value object in C-sharp? Well, first of all, what is a value object? So a value object is basically an immutable object which is based on comparing all the properties. So if you have a personal name, two personal names, if the first names are equal and the last name is equal, they're basically the same thing. They might be different objects, different pointers,
13:42
but from a logical point of view, they're the same object, which means they have to be immutable because they can't be changing underneath you. So here is a C-sharp version. We have a constructor with a first name and last name. We have two properties. I'm using private setters for immutability.
14:00
Not really immutable. I could be using read-only if I was going to be super strict, but that's probably good enough. Something I've left off, though, is that personal name is a reference type like all classes in C-sharp, which means if I have two personal names and I compare them, they're going to compare differently. So what I have to do is I have to override equality.
14:23
So I have to override getHashCode and I have to override equals and I have to override equals again and I have to implement i, equitable, whatever it is. So there's a lot of code you have to write to do equality, and this is actually quite bug-ridden code. I don't know if any people have ever put objects in a dictionary or a set with a mutable ID
14:40
and the hash codes change and all of a sudden things break. It's very easy to get this kind of stuff wrong. So let's compare this with F-sharp. So in F-sharp, this is your personal name. You've got a first name and a last name. Exactly the same thing, two properties. The whole thing fits on one other code. But of course I need to do equality as well.
15:02
So let me show you the code for equality. So yeah, there is actually no code for equality because in F-sharp, classes or types are equal by default. You don't have to override equality. So it's the other way around.
15:21
If you want them to not be equal, you have to go to work. And the best code is no code at all. If you don't write equality methods, you can't mess them up. So it's less work for everybody. Now in domain-driven design, you also have this concept of an entity object. An entity object is normally based on some sort of ID
15:42
and here's an example of a person and the person ID. If they have the same ID, they're the same thing, but the contents of the thing can change. So this person has changed their name from Alice Adams to Bilbo Baggins, say. And generally the content is mutable. It doesn't have to be mutable.
16:00
So let's look at the C-sharp code. Very same kind of thing, except I've removed the private setter to make it a mutable name. And I have to override equality again. And now I have to change, subtly change the way I compare things. Buried in the code is the important part. Let's look at the F-sharp code. Now in F-sharp,
16:21
if you want to change the way that equality works, you have to say customer quality. You have to override equality. And then you have to write get hash code and equals just like the C-sharp code. The nice thing about F-sharp though is the person cannot be null, so you don't have to do any null checking ever. Unless you're dealing with code that's coming from C-sharp
16:41
where you don't necessarily trust it. So. But in this case we're comparing by ID. But in many situations, you might not want to compare by ID. Sometimes you might compare by person, by the name. Sometimes you might compare by ID. And it may be that you want to use different comparisons and different situations.
17:02
So rather than always having a default comparison, you know, you can pass in i comparables or whatever it is. What I like to do in F-sharp is actually say no equality. And what that allows you to do is say that you cannot compare people directly. You can't say this person is equal to this person. You can say their IDs are equal.
17:21
You can say their names are equal. But you can't actually, if you actually try and compare them directly, you actually get a compiler error, okay? Which is I think a very cool thing. I think most entities should have that by default. So, the other thing in F-sharp is that the person class is immutable unlike the C-sharp class. And let me tell you some of the advantages
17:42
of immutability. So, if it's immutable, the only way you can change it is make a new one, right? You can't just change the property. You have to create a whole new person every single time. And there are some syntax things in F-sharp that make that not so hard. But if you do have to create a new person every single time,
18:01
the advantage of that is you can validate the data on construction. So, in the constructor, you can say is the name blank or whatever. And if it's a valid thing, you can return a new person. And if it's not a valid thing, you can not return a new person. And that's the only way you can create a person is through that constructor. And that means that any change has to go through that checkpoint,
18:22
which means that there's only one place you have to enforce invariance. So, I don't know if you've ever had code which on the setter of a property, you've had to check that it's not blank or check that it's not null. And then if it is null, what do you do? You're going to throw an exception or you're going to have an object which is in an invalid state.
18:41
You don't really have very many good solutions to that problem. If you don't allow an invalid object to be created in the first place, you eliminate a whole class of problems right there. So, let's have a look at the C Sharp code that we've got so far. There we go, the personal name and the person.
19:00
So, let me ask you a couple of questions. Do you think this is a reasonable amount of code to write for these two simple objects? I think the answer is no. I think that's way too much code. And secondly, do you think a non-programmer could understand this? If I was showing it to an in-house customer and I say, have I got the definition of a name right?
19:22
Is this what you mean by a personal name? I think they would say, well, I can't understand all this weird stuff. The other thing is you can't really tell which ones are values and which ones are entities. You can use market interfaces, but the important stuff is sort of buried right in the middle of the code.
19:41
It's not at all obvious how they compare. The ones are value type and it compares on properties and the other one is an entity and it compares on its ID. Okay, let's look at the F Sharp code. So, there's the personal name and there's the person. So, let me ask you the same two questions. Do you think this is a reasonable amount of code to write for a simple object?
20:03
And I think yes. I think it's pretty much a small amount of code that you can get away with writing. Secondly, do you think that a non-programmer could understand this? If I went to somebody and I said, you know, have I got the right definition for a personal name? Like a business analyst or something, and would they say, yeah, I can understand.
20:21
I don't understand F Sharp, but I can look at this code and say, yeah, actually you're missing a middle initial or you're missing an email address or something. It's pretty obvious just by looking at it. You don't have to be an expert in the language to understand this code. So, comparing C Sharp and F Sharp. So, this is just now, I'm not saying that C Sharp is a bad language,
20:41
but I'm saying from the design point of view, if you're just designing types, I think that F Sharp wins out over C Sharp. Value objects are really easy. Entity objects are really easy. Value objects by default. Immutable objects by default. You can tell them apart and it's easy to understand by a non-programmer. So, if you do nothing else,
21:01
you might consider just using F Sharp for doing your types or your classes. And it's just an assembly. You can link it in with your main C Sharp code. So, if you want an easy way to create immutable classes, have an F Sharp assembly and just do that. Don't have to write any other F Sharp code whatsoever. All right? Just do that bit and then use those classes from C Sharp.
21:22
So, this last thing, whether it's understandable by a non-programmer, I think that's a really important point. Again, it's all about communication. So, let's look at F Sharp for domain-driven design. And like I said, domain-driven design is about communicating a domain model within the team.
21:41
So, communication is a hard problem. If we take this word, U-N-I-O-N-I-Z-E, what does that word mean? Well, if you're an activist, that might mean unionized. If you're a chemist, it might mean unironized.
22:02
All right? So, the same word can mean two different things to do to two different people. So, in domain-driven design, there's this concept of a bounded context, a framework within which the words mean something. So, if you're talking about social activism, unionized means one thing.
22:20
If you're talking about chemistry, unironized means something different. So, different domains, different bounded contexts, same word. So, it's very important to define the context that you're using that word in. So, let me give you another example. SPAM, okay? So, if you run a supermarket, SPAM means one thing, and if you're an email admin,
22:42
SPAM means another thing, right? So, these are kind of cheap shots. These are pretty easy. Everyone knows these ones. What about products, though? What does a product mean? So, this is something you might run into. Like, if you're in the sales team, a product is something you can sell, but if you manage a warehouse,
23:00
a product might be a physical thing that you can put in a box, and sometimes the things you can sell are not necessarily the same things that you can put in a box, and you start getting this miscommunication between the teams. So, one of the goals of domain-driven design is to define these contexts and make sure the words are used appropriately in each context. Here's another one, customer.
23:20
You probably run into this one, too. You know, from a marketing point of view, a customer is anyone who has an email that you can send them SPAM to. If you're in finance, the customer is someone who's given you money. You know, it's a different definition, and it affects your design because a customer who's given you money, you might have an account ID and you might have a current balance and stuff, and from a marketing point of view,
23:40
a customer is just an email address and maybe not even a name, you know. So, this brings us to the topic of ubiquitous language. So, if you're working in a domain like chemistry, say, you know, chemists have these words like ion and atom and molecule and polymer and so on. Now, as developers, if you're working on an application in the domain of chemistry,
24:02
it's very important that we use the same words that chemists use. We shouldn't talk about molecules as an aggregate of atoms, and we shouldn't talk about polymers as a linked list of atom aggregates or something. Those are techie words, right? We should use the same words that the customers use.
24:21
So, again, part of the process of domain-driven design is to come up with a set of words that the developers can use and the domain experts can use and that we all agree on them, and then when we talk about a polymer, that we have a class called polymer. We don't have a class called, you know, list of atom aggregates or something.
24:43
So, again, the ubiquitous language can look similar in different domains. So, in the sales domain, you might have something called a customer and something called a product. In the warehouse domain, again, you might have something called a product, but it's a different kind of thing. So, but defining the ubiquitous language in a domain is a very important part
25:00
of domain-driven design. So, here is an example of some F-sharp code. For a domain, so this is a domain of a card game. How can we tell it's about a card game? Because there's a word at the top that says this is a card game, right?
25:21
If this was in a different domain, if I was in the domain of a clothes shop, then suits would mean something completely different, and if I was in the domain of, you know, the navy or something, then deck would mean something completely different. But in the domain of a card game, in the bounded context of a card game, these are the things that matter. And so this is the ubiquitous language right here.
25:43
So let me just go through this F-sharp code. The vertical bar means a choice. So a club or a diamond or a spade or a heart. The star means a pair in this case. So a card is a pick one from a suit, pick one from a rank, that's your card.
26:00
The list is actually a keyword in F-sharp, that's very nice. And this one with an arrow is a function. So this means to deal something, you start with a deck. The one on the left side of the arrow is the input. On the right-hand side of the arrow is the output. So you start, the input is a deck. The output is a different deck with a missing card plus a card on the table.
26:20
So that's the output. So that's the entire domain on one page. So let me ask you the same questions. Is this a reasonable amount of code to write for this domain? I've got nine classes on one page. So I think that's pretty good. In C-sharp, if I was doing this, there'd be nine files in a folder, probably.
26:44
Do you think a non-programmer could understand this? If I had left off something like the king, do you think I could show this to a non-programmer and say, have I left all the cards right? And they would say, yeah, no, you left off king. It's like, oh, thanks. It wouldn't be buried in some enum somewhere.
27:04
Another important point about this code is there's nothing about databases here. There's nothing about user interface. This is persistent ignorant. Persistence ignorant. There's nothing about how it gets stored. I don't care whether you store this in a SQL database,
27:21
whether you store it in a file system. If you want to be web scale, you can store it in Mongo. It doesn't really matter. It's not part of the domain how it's stored. And the other thing about this code is this is code, and it's a design as well. It's design and code at the same time. And that is one of the agile philosophies, really,
27:42
is that the most accurate thing to represent your design is the code. Comments go out of date. Documentation goes out of date. The only thing that is the truth about your application is the code itself. So it's nice if your code can actually represent your design in a nice way. And this is actually not pseudocode.
28:01
This is compilable code. I could take this code, stick it in Visual Studio, and run it and get some stuff out. So this is not pseudocode. So I think that's really quite a powerful thing. If you do this kind of approach, you don't need to have UML diagrams, OK? No UML diagram, no documentation, just the code itself.
28:22
The code acts as documentation. If you change that code, if you add a new type to a new kind of card, for example, your code will fail to compile because it's compilable code. Right. So that's the sort of introduction to domain-driven design. And let's talk about the F-sharp type system.
28:42
So the F-sharp type system is very different from what you're used to in C-sharp. So in C-sharp, you have classes. In F-sharp, you have types. OK, types are not the same thing as classes. And the F-sharp type system is called an algebraic type system, which is the same type system that's used in Haskell and the same thing that's used in OCaml, a couple of other languages.
29:02
Very common in functional languages. So algebraic is another one of those mathematical words. So I'm going to use the word composable instead. So it's a composable type system. So unlike objects or classes, you can actually compose the types together to make new types.
29:20
So what do I mean by composable? It's like LEGO, right? If you take two LEGO bricks and you stick them together, you've got another thing that you can then stick other things to it. You can build up complex things by gluing smaller things together. So that's what composable means. So if you do have two smaller things, how do you glue them together to make a bigger thing?
29:41
Well, in algebraic types or composable type system, there are basically two ways you can glue them together. You can either multiply them together. So a new type is an old type times another old type. Or you can add them together. So a new type is a type plus another type.
30:02
Now, at this point, you might be thinking, well, that's just crazy. How can you add two types together? How can you multiply two types? What does that even mean, right? That just sounds silly. But I think if you just hold on another five minutes, I think all will become clear. So let's start off with the times. So here's a very simple function.
30:21
So a function is basically a black box. It's got input and it's got an output. All right? That's all the function is. So I'm going to take the function add1, for example. So on the input, I have a list of numbers, integers. On the output, I have a list of integers. And the way you write that in F sharp is int arrow int.
30:44
So int is the input. Function output is an int. All right? So that's pretty straightforward. Now, what happens if we want to add a pair of numbers together? So if we add one to it, the input is a pair and the output is then added together. So the output is easy.
31:01
We can say the output's an int. But what's the input? What kind of type is that input? Now, we could define a class called pair, but that's got nothing to do. It's not built from smaller pieces. I want to actually create this type by adding two other types or combining two other types.
31:20
So let's look at how we can do that. So let's think about what a pair is. So a pair basically means you pick one from the first pile and you pick another one from the second pile, right? So one column and the second column. So let's say there were four numbers. Obviously, there were like 64K or four gig numbers.
31:41
But let's say there are four numbers only. So you can pick four from this pile and you can pick four from the second pile. How many possible pairs are there? There's four times four, 16 possible combinations, yeah? So that's multiplying them, right? Four times four, 16. Here's another example. Let's say you have a pair of Booleans.
32:02
So true false and true true and false false, right? So again, the first column is you pick one from two possibilities and the second column is you pick one from two possibilities and the total number of combinations is two times two. So the way you get the pair is by multiplying the first column by the second column.
32:22
So that's the total number of possibilities. So in fact, that's exactly how you do it in F-sharp. You say a pair of ints is written as int star int, int times int. And a pair of Booleans is written as Boole star Boole, so literally multiplication, okay? So you can see where that's coming from.
32:40
It looks a little bit weird at first, but it's actually quite sensible if you think about it. All right, well, that seems kind of academic. Maybe say, okay, well, that's kind of theoretical. Let's look at a real example of how you might see it in a real business application. So let's say you have a bunch of birthdays. So Alice was born on January 12th and Bob was born on February 2nd and so on.
33:00
How can I represent these birthdays using the type system? Well, we have a set of people. The first column is a set of people, all the people in the world. The second column is a set of all the dates in the world. And every possible combination of a person and a date is multiplication.
33:26
So that's how I get the birthday type is every possible combination of the people type and the date type. So in code, in F sharp code, you would write it like this. Type birthday equals person times date. And that's how you'd create a new birthday type.
33:42
Right, so that's one kind. So that's what they call a product type or a multiplication type. What about the other one where you're adding things together? So let's look at how you might represent a choice. So here's a function that says whether you have a fever or not. You pass in some sort of temperature
34:00
and if your temperature's more than 37.5 or 38, where it says, okay, you've got a fever. And if it's in Fahrenheit, there's 100, say, and you get a fever. So how can I document what the input is? Well, the output's a Boolean, so that's easy enough. So, but the input is a choice. I can either pass in a Fahrenheit temperature
34:22
or I can pass in a Celsius temperature. How do I represent that? So if you think about it, this is the same kind of thing. I've got a list of possible temperatures in Fahrenheit and I've got a list of possible temperatures in Celsius or centigrade. But this is slightly different because I pick one from the first pile
34:41
or I pick one from the second pile, okay? So let's say there are four in the first pile and four in the second pile. How many possible combinations are there? There's actually only eight, right? I pick one of these four or I pick one of these four. So there's eight possible combinations. So that's addition, right? It's what they call a sum type.
35:01
And how do you represent it in F sharp is you have to, they're both, say, integers. So you have to have some way of differentiating the integers so you tag them with a little symbol. So I'm going to tag the Fahrenheit ones with the letter F and I'm going to tag the Celsius ones with the letter C. And then you write that in F sharp as the temperature type is a choice between
35:23
an integer which is tagged with F and a, say, a float that's tagged with C. So that's how you'd write this choice type in F sharp. It could be one or the other, but not both at the same time. All right. So again, that's maybe a bit strained
35:40
but let's have a look at a real example. So here's a payment method. So as an e-commerce business or maybe as a shop, I can take cash, I can take checks, I can take credit cards, say. And you'd represent that as three different choices. I can take cash, I can take checks, and I can take credit cards. Three different choices right there in front of you.
36:01
One of the nice things about these choice types is you can have extra data attached to different pieces. You can think of them as different constructors. You might have a cash constructor and a check constructor. And the cash, you don't need any extra information for a check, you might need the check number. And for a credit card, you'd need the card type and the card number, so a pair.
36:24
And when you actually use these things, you want to kind of look inside and see which particular choice was actually used. So when let's say you want to print the payment method on a receipt or something, what you do is use a match statement, which is the F sharp equivalent of a switch basically or a case statement.
36:41
And if it's cash, you say print cash. If it's a check, you say paid by check. And one of the nice things about the matching is it extracts the information inside at the same time. So you match and you suck out the data. And finally, if I pay by card, I can get the card type and the card number at the same time. So that's how choice types work in F sharp.
37:03
And it's very cool that you can do this match and assignment in one step. So these are not just enums like in C sharp because enums are just integers behind the scenes. These are more like subclasses, and each subclass has its own data. So you might say, well, why don't you model it with inheritance?
37:20
Why don't you use subclasses? Why have this special kind of choice type? So how would you implement this in C sharp or an OO language? So what you'd probably do is have some sort of interface or abstract-based class that represents all of them, an I payment method. And then you'd have a cash class or subclass with a constructor.
37:40
You'd have a check class that had its own data, and you'd have a credit card class that had its own data. So the problem with this approach is that the interface has no common behavior, really, between the two. I mean, you might say, well, yeah, the printing method is a common behavior. But that's really in that context.
38:00
I don't think it should be part of the core domain class that it can print itself on a receipt. That's really mixing. That's not separating your concerns well enough. Printing on a receipt is a different thing. There's actually no common methods that belong to the domain. The other nice thing about the F sharp approach is the exercise is really obvious.
38:20
If I say, well, what do I need to store a credit card? It's like, well, I need a card type and I need a card number. Is that obvious from the C sharp codes? It's kind of buried in the constructor. It's buried in the properties. It's not really obvious where it is. It's basically scattered around in many locations. And in C sharp, that would be four different files probably, right? You could have one class per file, four different files.
38:44
In F sharp, it's four lines of code in one place. The other thing about the F sharp code is it's closed, which means that you can't add a fourth option. OK? That is your limit. If you want to add a fourth option, you can. But all your code will break.
39:00
And then you have to fix up your code, which I think is a good thing. If you add a new kind of payment method, you want your code to break until you've handled that particular new payment method in all your code. In OO, in the OO version, anything that implements the I payment method is valid, right? So you might get an unpleasant surprise. I might implement an evil payment method.
39:22
As long as it implements the interface, it's totally valid. Now, in some cases, you might want open. You might want to have, oh, yeah, anybody can implement this interface and it's fine. I trust them. In business logic, most of the things are actually quite restricted. You have a restricted set of options, like payment methods. And adding a new one is actually
39:40
something you want to know about. It's not something that you should just slip in behind the scenes without telling anybody. So what are types for in functional programming? It's an annotation to a value for type checking. So here we go. It's just like in C sharp. You say this is an int. You know, it takes an int and it outputs an int.
40:00
But it's also a domain modeling tool. So in this case, I can tell you that to deal something, you have a deck of cards. And you input this and you output that. So you can actually model the domain using the types. And it's both at once, which means that your domain modeling tool is also compiled into your code. So your code can never not match the domain.
40:26
So I think of a static type system like this as almost like having compile time unit tests. If I have a unit test that says, oh, you know, a check payment must have a card. A payment card must always have a card number
40:40
or something. I don't need to have a unit test for that. It will not compile if the code doesn't match the domain. So it's a compile time unit test. So type all the things. That's one of the mottos of the static type people, especially the functional programmers. All right, so what can we do with this type system?
41:00
Let's actually put it to use, do something useful with it. I'm going to start with optional values. This is a really common case. So here we have our personal name. And the middle initial is optional. And the first and last name are not optional. And I can't tell. So can I use the type system to indicate that?
41:22
So let's go back to our strings. So let's say we have a function that's the length of a string. Really, really simple function. And the input is a string. And the output is an int. All right, that's a very simple function. The only problem is that we have null in this list of strings. And null is evil because it's pretending to be a string.
41:44
Right? You cannot use null as a string. OK, if I say set phases to null, that just doesn't make any sense. It's illogical. So null is not really a string. It's in the pile of strings. If I say to null, are you a string? I say, yeah, yeah, I'm a string.
42:02
And then you say, well, give me your length. And you say, ha, going to crash your application now. It's like, well, you told me you were a string. It's like, well, yeah, I'm pretending to be a string, but I'm not really. So I think of null as like a salmon. It's going to betray you behind your back. It pretends to be one thing, but it's something evil.
42:21
All right? So in F sharp, null is not allowed. Null is not a valid string. Well, actually, in F sharp, unfortunately, it is a valid string because of the way that it has to be compatible. But most of the classes you create, your own classes in F sharp are not allowed to be null. So you never have to deal with nulls in general.
42:42
So no nulls. So if you don't have nulls, what can you do instead? All right? So we have our list of strings here, and null is not in them. But we want to say, well, maybe the string's missing. So how can we indicate that? Well, we've already seen how we can do that, because we have this choice between something
43:02
and nothing. So the way we represent that in F sharp is exactly that thing. You have a choice between a string, a valid string, which is not null, and nothing at all. And it's a sum type. It's a choice type. And we tag the strings with some string,
43:21
and we tag the missing with nothing. So it's either some string or it's nothing. And we write that like that. So it's either some string or it's nothing. So that's a type that you would see a lot in F sharp. And you say, well, that's really cool. I've got optional string. And then, oh, I want optional int as well.
43:41
Oh, and optional Boolean's really important. So you start having all these optional types floating around. And at some point, you say, there must be an easier way than that. So let me define a generic type that takes a type parameter. So in this case, it's an option of any type. And it's either sum of that type, or it's nothing, none. And that type is actually built into F sharp.
44:02
So this option type is built in. And it's used a lot in the F sharp libraries. So with this option type, we can rewrite the middle initial to say it's an option of a string. And in F sharp, you can actually have a nicer syntax where you call it a string option
44:21
rather than an option of a string. So I think that's actually much nicer. If I was showing that to someone, I'd say, look, it's an optional string. I think it's pretty easy to understand. All right, the next kind of type I'm going to talk about is what I call single choice types. And this is an example of a single choice type.
44:40
It's something, and it's choice A of something. But there is no choice B. There's only one choice. Why would you only have one choice? So let's look at some examples. Here's an email, which has only got one choice of an email. Customer ID's only got one choice called customer ID. Why would you only have one choice? What possible use is that?
45:01
So think about this for a second. Is an email address just a string in your domain? Can you add hello to it without breaking code? Can you reverse it without breaking stuff? An email address isn't a string. Once you validate it, you really shouldn't be messing with it. You shouldn't be adding things and subtracting things. You shouldn't be reversing it.
45:22
It's a thing. It's an email address. It happens to be represented as a string, but it isn't really a string in your domain. Is a customer ID just an int? Can you add five to your customer ID and still get a valid customer ID? Probably not. I mean, maybe you can, but your customer ID's, they might be represented by ints in your database,
45:42
but they're not really ints in a domain. So by using single choice types, you can keep these types separate from their underlying representation. So for example, if you have an email address, which is represented by a string, and a phone number, which is represented by a string, by wrapping them in a single choice, you've now made them different types, distinct types,
46:02
and you can no longer compare an email address with a string, and you can't compare it with a phone number, and you can't compare a phone number with a string. They're just completely different types altogether, and you'll get compile errors, compile errors if you try and compare them. Similarly, if you have a customer ID and an order ID, and this is a very common problem, you might have a,
46:20
they're both represented by ints, and in your code, you want to keep them separate, in your query strings to your DAO or whatever. They're not the same thing. If you try and compare them, you should actually get a compile time error. So this is, are you familiar with the phrase primitive obsession? Yeah? So the primitive obsession is when you use ints instead of domain objects like this.
46:41
You shouldn't be using ints to represent customer IDs. The other nice thing about using a special type for the email is you can wrap it in a constructor that does validation. So an email address is not just a normal string. It has to have an at sign in it, say. So very crude validation
47:00
is that it has to match some sort of regex, okay? So in this case, it has to have an at sign in it. So in the constructor, you can say, well, if it does match that regex, then you return a new email address, a wrapped email address. But here's the problem. What happens if it doesn't match the regex? What are you going to do? You're going to return null?
47:21
No, because null is not a valid email address. You're going to throw an exception? Well, that seems a bit harsh just because I passed in an invalid email. It's like, you know, can't you just give me something more useful that I can process as a... Well, I have to catch exceptions all the time. Well, I think we've already seen the answer to this. You use the option type.
47:41
So if it's a valid email, you say, yes, it's an email, some email. And if it's not a valid email, you return nothing. It's like not a valid email. But this is a proper type. You're not throwing exceptions. You're not returning null. And you can tell what it does unlike the null case or the exception case. You can actually tell what it does by looking at the signature for the function.
48:01
So you might think that the signature is give me a string and I'll give you back an email address. And that would be true if I was using nulls and it would be true if I was using exceptions. But I'm not using either of those. I'm using options instead. So the signature says you give me a string. I might give you back an email address. I might not. You'll have to handle both cases
48:21
because depending on what string you give me, I might give you back nothing at all because it's not valid. And you are forced to handle both cases. When you take this code, at some point in your code, you're going to have to say, well, is it something or is it nothing? Okay, you don't have to, but you can pass it around. You don't have to handle it straight away. It's not like an exception or a null. You can pass it around
48:40
until somebody has to deal with it. So let's look at another example. Here's a string 50. In this case, I want to make sure that the string is constrained to be less than 50 characters long. So again, I have a little in the constructor. I have a little test. Is it less than 50? If it is less than 50, I can return a valid string 50.
49:02
And if it's not, I return nothing. So the constructor, the signature for the constructor looks like this. Pass in a string, and I might give out a string 50, or I might not, depending on whether you passed in a string that was too long or not. Here's another example. Let's say this is an e-commerce system,
49:21
and I've actually seen this in a real e-commerce system. What is wrong with that picture is that I have a million things in my shopping cart. That should not be possible. I shouldn't really be able to add a million things. Well, maybe I can, but most shopping sites will not let me do that. But they might let me do that because somebody has only represented it
49:41
by an int behind the scenes. So what I really should do is create a new type called an order line quantity, and it wraps an integer. It's not a normal integer. It can't be negative. It can't be two billion. There's going to be some sort of constraints on it. And so the nice thing about F sharp is I can create a new type just for this domain
50:00
because it's one line of code. It's really easy. In C sharp, I should probably do the same thing too, but I probably can't be bothered because it's a lot of work. So most people don't actually do this, and this is the kind of thing where you get bugs in your code because you represent it as an int and then you have to have special code to handle negatives, special code to handle more than 1,000 or something. Don't have that code.
50:20
Just don't allow it to be created in the first place if it's not the right number. So in this case, it has to be greater than zero. So even zero is not a valid number for a shopping cart quantity. So if I do something with it, I'm going to have to handle the case where it's, you know, I guarantee by having this type it's between one and 99. I literally cannot have a zero quantity item in my basket, okay?
50:41
Yes. So the question is, where would that constructor be implemented? The answer is it would be implemented normally near the type itself. So in F-sharp you have a module which is basically a name space or a kind of grouping of codes.
51:03
And what you can do is you can actually make sure that the constructor is the only way you can hide the built-in constructor and you can make sure that people are forced to go through that constructor. And that would be the only way. It's like having a private constructor and then having a public factoring method. And you'd put them together
51:20
in the same file. Does that make sense? So again, here we return something or we return nothing. So every time I add something to my order line quantity, I have to take into account that I might get something back which isn't an order line quantity. So if I subtract one, I might get nothing, right?
51:41
And in my code I have to say, well, what happens if I get nothing back? Well, okay, maybe I have to move that line from the basket, right? I'm forced to deal with that code. It reduces bugs just by doing this. So let's have a look at our code quickly, the challenge. So our challenge was how do we do optionals?
52:01
And I'm going to rewrite this now to use constrain strings, string 50 rather than just a plain old string. So that's a lot easier to see what's going on. And then the other thing, one of the questions is how do I make it very clear which sets of properties have to be grouped together for currency purposes? And it's really easy in F sharp
52:21
just to create two new little sub small types and then the big type just contains the small types. And because it's really easy to do, I mean, this is eight lines of code and you can all be in one file. You don't have to create three separate classes and three separate files.
52:40
So in F sharp, you tend to get a lot more small types than in C sharp just because it's so easy to do. There's one thing we have not talked about which is this email verified. So this is this Boolean that's only allowed to be set to true if the email has been verified. So let's have a look at that for a second. So we have some business rules around this.
53:02
The first business rule is if the email is changed, you have to set it back to false because it's not verified. And the only way you can set it to true is if it's been verified by some sort of verification service. You know, they click on an email and there's a hash in the email and you check that the hash matches and if it's okay, then it's all good. So those are business rules. Now, again, normally in C sharp,
53:22
those business rules would be sort of embedded in the code. My question is can we embed those business rules in the type system? So it's compile time error to get it wrong. So the compiler will not let you set that to true by mistake. Is that even possible? So let's see. So this is not very good.
53:41
This is terrible because anybody could set it to true and say, oh, yeah, this is a verified email address, even though it isn't. So how would you do this in F sharp? Well, the first thing you do is you would wrap the email address in a new type called a verified email address. And this is one of the things you tend to get in functional programming, which is anytime you have a problem, you solve it by wrapping it in another type.
54:02
There's no problem that can't be solved by wrapping it in another type, basically. So you tend to have these types within types within types. But because it's a one-liner, it's really easy to do and it's equally easy to unpack it as well. So now I have a difference now between a normal email address and a verified email address. They're now different types and they can't get mixed up.
54:21
That's good. And how do I get one of these verified emails? I have an email service, a verification service, and it takes in an email address as input and it takes in some sort of hash, which you pass in, and it checks the hash and if it matches the email, it says, yeah, this is a verified email. Or it might say, actually, it's not. So that's why it's an option.
54:41
I can tell straight away from this service that it might not work, all right? So email input, an optional verified email as the output. And then what I do is I define my email information as a choice. I say either it's an unverified email or it's a verified email.
55:02
If it's an unverified email, it's just a normal email address. If it's a verified email, it has to be one of these special verified email addresses. And the only people who can create this is this verification service. I can't create one of these things. Only the verification service can. So when I'm coming along and I want to change the email address, I can change it to unverified,
55:20
but I can't change it to verified, all right? So this is a way of guaranteeing in the type system that I can't have an accidentally verified email address. So I've got rid of the Boolean and replaced it with two choices. And that's a very common thing you'll see in functional code is every time you have these Boolean flags,
55:42
like has it been shipped, has the order been paid, all that kind of stuff, those are replaced by a set of states, a set of choices, and each choice might have different types associated with it. So let's go back to our original challenge. Here is our email address. We've broken it up into,
56:01
we had one type originally, and now we've got five, six types plus the verification service. The questions were, again, which values are optional? What are the constraints? Which fields are linked? And what is the domain logic? So which values are optional? This is now clear from the code, right? I could show this to somebody and say, which one's optional? And you say, yeah, the middle initial is optional.
56:22
The other ones are not optional because they're not allowed to be null, remember? So they're required. The first name is, by definition, required. If I don't say it's optional, it has to be required. The constraints now, I've replaced it with these special types called string50 or string1. And the email address, again, is a constrained type. So if I have a first name,
56:41
I know it's gonna be less than 50 characters. I can't, when it comes time to put it into the database, I know that I'm never gonna have some database bounds exception that I have to handle because I know it's gonna be within the right length. Which fields are linked? Well, that's easy. I just created two little smaller types. That was trivial. And finally, is the domain logic clear?
57:01
And it is, I think, because it's now very clear that I have unverified and verified ones. And it's also very clear that the verified email is a different kind of thing than a normal email. What's interesting about this is not only that I've now encoded all those rules in the design, but the ubiquitous language has evolved, too.
57:22
So in my design, I now have something called an email contact info. I have something called a verified email address. So these are things that I didn't have in the original design. But in fact, these actually do represent concepts in the domain. If I was talking to a business, then you'd say, yeah, we have verified emails and we have non-verified emails. So I've actually documented that now,
57:42
which the original design didn't have. And this, of course, is compilable code. This is not pseudocode. This is not a UML diagram. This is real code that compiles, and it would be basically the first file in your F-sharp project.
58:00
So if you don't mind hanging around for another couple of minutes, I just want to do one more thing, which is this concept called making illegal states unrepresentable. So let's say that some time passes and we now have an address as well as an email, right? The requirements are changing now. And we have a new business rule
58:21
that a contact must have an email or a postal address. So I need to be able to contact you somehow. That's a fair enough requirement. So does this type actually meet that requirement? And the answer is no, it doesn't, because the way it stands right now is both the email and the address are required. They're both required fields, right? They can't be null, so they're both required.
58:41
So this doesn't allow for the fact where I have one but not the other. All right, well, I'll make them both optional. So yeah, does that solve it? No, that doesn't solve it either because now they could both be missing. So the business requirement is you have to have one of them. You can't have them both missing. So how can I encode that business requirement?
59:01
Again, in C-sharp, we'd probably have some code that's buried away. Well, if they're both null, then throw an exception or return an error or something. It'd be nice if we could actually encode that in the type in such a way that you could not, you didn't even have to deal with that case. You could not literally represent that. So that is this concept called make illegal states unrepresentable.
59:22
If you can't do something, don't even allow it to be in the type system. In this design, I could make them both optional or both null. It's like don't allow that to happen. So how can I prevent that from happening in the type system? Well, if you think about it, that business rule says that you either have an email address or you have a post address or you have both, right?
59:40
That's what the business rule says. There's only three choices. Not four choices, not two choices. Well, how would you represent that? Well, as I say, you always wrap everything in a type. You create a new type with three choices, whether it's email only or address only or an email and postal address, right? So I now have a type that represents that business rule.
01:00:00
and I literally cannot have both of them missing because it's not one of the choices in the type. So by defining a type like this, I can actually hard code my business rule into my design in such a way that I literally cannot mess it up, okay? You cannot have a bug from having them both missing because there is no way to represent them both missing.
01:00:25
And then I just replace my email address and address as just one new type now. So what I've done, I started with them as separate things and I've merged them into a single thing which now has a set of choices.
01:00:43
So static types are really awesome, almost as awesome as that. One final thing, let's say that that's a rather restrictive thing, an email address or a postal address. What they probably mean is, the business probably didn't really mean that. What they probably meant is there's some way,
01:01:00
you have to have at least one way of being contacted because you're a contact. I should be able to contact you in at least one way. So rather than doing that, what I might do is say, well, here's different ways of contacting you. I can contact you with an email or I can contact you by sending you a letter. And then in the contact type or structure, I have a primary contact information
01:01:22
and a secondary contact information. And the primary contact information is required and the secondary contact is optional. So again, the type system has created, again, it might be something that when you talk about this, the business owners say, yeah, of course, we have a primary contact and a secondary contact. It's a new part of the domain language now.
01:01:41
You've got a part of the ubiquitous language, have a concept of a primary contact. And again, the primary contact is not allowed to be null, so it's by definition required. It's very clear from this code what the business rule is. So I talked about the challenge, I talked about ubiquitous language, self-documenting designs, algebraic types,
01:02:02
designing with types, using options, single case unions, and making illegal states unrepresentable. So hopefully this all makes sense now. I haven't talked about states and transitions, that's a really important thing. Services, CQOS, how you do the functional approach to doing use cases and domain events and error handling.
01:02:22
So if you're worried about using F-sharp, I actually think you should just try it out. Like I said, even if you just use the type system and don't write any code, it's supported by Microsoft. If you want to go mobile, it's built into Xamarin Studio. That gets you F-sharp on millions of platforms, so it's a very safe choice.
01:02:40
If you need to persuade your manager, I have some examples at F.P. Bridge, and there is the link to my site, F-sharp on Twitter if you've got any questions. Contact me on Twitter, and if you need any help with F-sharp, contact me at my consulting business. Thanks very much.
01:03:04
If you have any questions, please come and see me afterwards, and please remember to put the, the review things, yes. And there's some more F-sharp things tomorrow morning, by the way. Two F-sharp talks tomorrow morning if you're interested in F-sharp. Please go to those.