String is not your friend.
String has no structure nor meaning in any business domain you may happen to be modeling.
Once it becomes a habit to use unstructured types in domain modeling, we
call it a primitive obsession. An application becomes obsessed
with strings, ints, decimals... Bugs... they come next.
And an inability to develop complex features. Today, I will show you how limited an application
can become if it is obsessed with primitive types. And then, then I will show you the way out.
I will teach you how you can substitute primitive types with proper domain classes.
Let's start with a simple example. Would you ever pass a decimal number
in place where money is expected? I guess not, because money, a proper money
type has other responsibilities and other abilities that the decimal numbers don't have.
For example, a money type is managing rounding. It is managing how many
decimals there are in money. Also, a proper money domain model is
forcing the existence of a currency. You will never be able to add or
subtract different currencies. Decimal number, a plane decimal type,
also doesn't have such a feature. You see, this Money model is defining
arithmetic operations on money. And what is specific is that some of those
operators end up calling these methods, which are checking whether the currencies match in two
money instances that we are adding or subtracting. If you ever used the bare decimal, you
would lose this ability and that would be a very dangerous situation.
An application could have a nasty bug to mix up currencies.
Oh, that would be a disaster. Let's get on to a more serious issue.
I will run this application to show you where is the missing feature.
I want to implement a new feature and you will see that I cannot do that
because there is a primitive obsession here. This is the application which is selling books.
And look at this list of authors of the book. It is unstructured.
I want each author here to be a link to the books of that author.
But I cannot implement it right now. You will see why.
There's the same problem here, if you go to the details of a book,
there's the list of recommended books. Well, the author is still not a
separate link, the entire row is just one link to the details of another book.
I want these also to be separate links. I want this application to look rich.
But I cannot do that. Let me show you the code.
This is the interface which we use in this application to format
the look of a book anywhere in the UI. And look, it's returning a string.
It is obsessed with a string. String has no structure.
If we get a list of authors, separators, then the title, it all gets into a single string.
We have lost the information what is what in there.
We have lost information what is the author there, what is the ID of the author,
what is the ID of the book, where is the title... We have lost all the the relevant
information from this business domain. All thanks to converting a
domain model into a string. Here is the Razor page which
is rendering book details. As you can see, all it can do
is just display that string. Even this ID of the book that is used to construct
the URL is coming from a different place, not from this string, obviously.
We have a request from the customer which we cannot even implement at this
moment because we have lost the information. From this point on, I will teach you how to
improve an existing application and to move it away from primitive types, to substitute
primitive types with proper domain types. But there is one additional constraint.
The application must work through the entire process.
I'm not allowed to add a feature in such a way that it breaks anything else in the
application so that we must correct it later. Because correcting the broken parts
of the application could fail. I could miss something that I have broken
and in that way effectively introduce a bug. We must not add bugs to an application
while developing a new feature, of course. So, the technique I will show you now is
based on augmenting an existing primitive type and turning it into a more complex domain
type before implementing the entire feature. There will be a proper class that will do this.
I will call it Citation. But now we have a problem.
This is where the book is formatted for the UI. The result is declared as string and
now I want to use a Citation class. And this is how that object is produced.
This is the concrete implementation of that formatter.
It is just using strings. It is producing a proper string
by concatenating smaller strings. It's all strings everywhere here, you see?
And that is the problem in this application. I will let both concrete implementations of
that interface return the Citation model. But how?
Of course, the page model will also serve the model instances.
And we have just declared the simplest possible solution to the immediate problem we are
having, the problem of using bare strings. The problem is now moving
one step lower underground. The UI will use ToString
to display, which is wrong. We don't have ToString implementation
and, even if we had it, ToString should not be used to render the UI.
And both concrete implementations are just trying to return the string.
So, implementation fails. But declaration is there.
The first step in augmenting the string into a proper domain class will be
to wrap it into the class and do nothing else. Just make the code work with the class
which is behaving the same as the string. I hope you are following me on this.
It might become clear when I do it. There will be a string, just
a string, inside this class. What I'm really doing is making
a better string as the first step towards creating a better domain model.
I will declare an implicit operator which will allow me to assign a
string to an object of this class. So, wherever we used strings, we will be
able to just assign that to an object, to a reference to this class, and that string
will be wrapped into the object of this class. And the other way around, the
Citation used as the string. Implement ToString.
And now we have stitched this new model, which is still very
primitive, it's still not doing anything. We have stitched it in all the
places where we used strings. We are now ready to start removing
those strings from the rest of the code and teaching all elements of the
code to use this Citation model instead. Once that step is over, and that process is
over, we will be able to even remove these two members from this class.
Do you see the way now? I'm really making small, safe steps.
The application is compiling fine after this point.
And it is also working the same as before. I haven't broken anything that already
works and we are ready for the next step in developing a proper domain model.
Before that, don't forget to press the Like button on this video, thank you very much.
And also, if you want to see the source code before and after this change, the
source code is available to patrons, to sponsors of this channel, so become the sponsor!
Help me keep this channel free for everyone else. Thank you very much, I'm really
grateful to all the sponsors who are already sponsoring the channel.
That will give you access to the source code of this video and all the
other videos on this channel, thanks. We are ready to deepen the domain model and to
cut all the relations to common plane strings. There are two supporting formatters.
One is formatting the author list, and the other is formatting a single authors.
These formatters are used under the hood when formatting the book.
They will both return Citation models this time. We get back to the Citation class.
What is it? This is the place where we
start developing a domain model. And, as I said, cutting all
connections to the plane string. We need proper domain modeling at this stage.
What is the Citation? The request is to segment the citations
in the UI, so that every element in a citation becomes clickable.
So, there will be segments. Why not hold zero or more
segments inside every Citation? This design is already pulling me in the
direction, forcing me in the direction of designing this Citation class so that we can
build a Citation object at runtime incrementally, out of smaller parts, the parts of the citation,
the segments: an author, a comma, another author, another comma, a title of the book.
Every element in what used to be a string will now become a proper object which is
telling something about what is inside of it. So, I'm exposing Empty static
property getter for convenience. You can always start not from an empty
string and then concatenating elements, but from an empty Citation and then
adding - let me add that - adding more citation segments at the back of it.
This design is already getting out of hand. I will dedicate an entire video
to developing this class further. So, stay tuned.
There will be a video which will explain how we move forward.
There will be a lot of code behind this point. I will only make the next step
here and we will stop there. But that step will also be interesting.
We need to bridge the gap between the strings and this complex Citation which consists of segments.
Initializing a Citation from a string means to wrap that string into a CitationSegment
and add it to an empty Citation. You see, there is complicated logic here.
But nobody will know that. Nobody else, all those classes that are still
serving strings, will know nothing about this. We will enhance them later, when the time
comes to walk through all the rest of the application and clear it from strings.
The other way around, turning a Citation into a string to be displayed in the UI.
Well, just walk through all the segments in this immutable list, turn every one
of them into its textual representation and concatenate them into a single string.
We have made a giant leap away from strings. We already have a domain class
which is better than the string. There's a lot of work here remaining.
As I said, there will be the entire video dedicated to implementing this
domain model, which will be complicated. Just to tell you where this is heading,
there will be multiple kinds of segments. One will be the author with the author ID.
The other will be a book, or book title with a book ID, so that we can
construct an anchor tag in the UI, for example, for an author and for the book.
There will be a lot of work in all the code that is producing instances of the
Citation because every class in the system will need to tell all the details.
Every formatter will say: this is the author, this is the book's title, this is
the delimiter that stands in between. And all those things were impossible with strings.
Impossible! Let me just run the application right to now in
its current state and you will witness that it is working exactly the same as before.
Authors are still nicely separated. Recommended books are still formatted using
a different citation formatter this time. I have not broken anything in
this application, anything. Everything is working the same as before.
But we have a new domain model that we will be able to develop further so that we will
eventually get a much better application. Stay tuned for the next video where I will
effectively implement this entire feature. There will be a lot of code to complete that.
And that endeavor will have its own pitfalls. You will have to learn a special
coding technique that goes hand in hand with what we did in this video.
That is the technique of doing incremental coding. Stay with me, come back for
the continuation and enjoy watching other videos on my channel, thank you.