SOLID Design Principles in #Angular (Advanced, 2021)
Aug 15, 2023
SOLID Design Principles in #Angular (Advanced, 2021)
🔥 Learn how to build really complex Angular forms \u0026 form controls with my new advanced course https://bit.ly/advanced-angular-forms 🔥 Use coupon: YOUTUBE_DISCOUNT to get -a 10%-off discount! I am quite sure that every one of you has heard about S.O.L.I.D design principles which help to design our code maintainable and flexible. In this video, I wanted to show you how these principles could be applied to your Angular applications. I hope you will find something useful and enjoy watching it! ⭐ Successful Interview “Angular Interview Hacking” coursehttps://courses.decodedfrontend.io/co … ⭐ Become a Pro in Angular Material Theminghttps://bit.ly/angular-material-themi … ⭐ Blazing fast GraphQL Backend just in 1 Day with Hasura Enginehttps://bit.ly/complete-hasura-course 00:00:00 - Intro; 00:01:05 - What is Design Principle; 00:02:23 - Single Responsibility Principle; 00:13:21 - Open/Closed Principle; 00:21:40 - Liskov Substitution Principle; 00:27:15 - Interface Segregation Principle; 00:32:52 - Dependency Inversion Principle; 00:41:02 - Outro; 🔗 Link to the source code on GitHub:https://github.com/DMezhenskyi/solid- … #webdevelopment #angulartip #frontend
Content
4.4 -> Hi Everyone! Dmytro Mezhenskyi is here and today we're
going to talk about solid design principles
11.84 -> in context of angular this video was inspired
by this comment and i found this topic really
20.88 -> interesting because i remember myself many years
ago i remember i knew all those five principles
29.04 -> but when it came to real life called the real
implementation i could not recognize them in the
38.88 -> source code i could not apply them and i decided
that maybe not only i had such an issue so i
48.4 -> decided to make this video for you and explain
it a little bit and show you real life cases so
56.24 -> this video will be split into five sections
time codes will be in the video description
63.2 -> check this out and we are getting started but
before we start diving into those five design
70.64 -> principles let's talk a little bit what is design
principle itself and design principle this is
80.32 -> recommendations how to design your code such a
way in order to keep it maintainable flexible
88.56 -> and so on and so forth and telling that it's
recommendation it means that it is very abstract
97.04 -> it doesn't have any concrete implementations
you can implement some design principle many
103.76 -> different ways and already on top of this design
principle we have design patterns which solve some
113.68 -> already concrete use case and implement one two or
more design principles and we are not restricted
122.88 -> by only those five design principles we have many
more like dry don't repeat yourself principle or
132.48 -> keys keep it simple and there are many of them but
those five are most popular ones and we will have
141.6 -> a look at them right now and the first one i'm
going to start with is the single responsibility
148.96 -> principle which sounds like there should never
be more than one reason for a class to change
156.88 -> which means that some component or class or
model should do only one thing but do it well so
166.96 -> let's have a look at the application i prepared
for you this is just a simple widget weather
174.96 -> widget which just exports some json data and now
let's have a look at the implementation of this
184.88 -> by the way i have uh in this project five branches
uh every branch for uh reserved for some certain
194 -> design principles so you can clone this project
to your local machine and switch between branches
201.92 -> and the implementation of every design principle
okay but yeah let's get back to our implementation
209.36 -> you can see that my app component which is only
one component for now we have the material toolbar
219.28 -> we have the main tag which should contain the
whole content for my web page and also i have the
228.72 -> widget template and besides this i also have
on export json function which does export of
239.6 -> whether widget data in json format and i have some
styles yeah but i collapsed it for now okay this
250.08 -> piece of code violates pretty much everything not
only single responsibility principle however we
258.4 -> focus on the single responsibility principle
here and yeah let's figure out how we split
269.04 -> responsibility between components actually it
is sounds easier than it is because very often
278.64 -> it is hard to decide where you know where ends
responsibility for class a and starts their
286.48 -> responsibility for uh component or class b however
i have one rule which i use in order to determine
298.24 -> this is to determine if my class does too much or
not this is the "AND-word" rule and it means that
308 -> you describe the responsibility of some certain
component and once you encounter the word "AND" it
320.96 -> might be the signal that your component
does already too much. let's apply this
329.68 -> rule to this example and how I would describe the
responsibility of the app root component I would
339.92 -> say that the responsibility of this component
is to render the skeleton for my page so it's a
349.12 -> top-level components like toolbar and content and
we have already toolbar we have this main content
360.8 -> and we render also the widget layout and
we do export JSON file JSON data for this
375.04 -> component as well within one single
component so it only gives us a hint that
384.72 -> this component does too much and what would be the
solution for this case I would suggest that widget
396.16 -> should be definitely the separate component
and the logic here might be might belong to
405.6 -> the component itself so the solution would
be is to generate additional component
418.88 -> I will call it like a widget then I say that
it has scss it should have inline styles inline
429.6 -> template and we skip tests and we also do it
flat so well I don't want to have the separate
441.2 -> folder for this component and I click run and we
have to get the new widget component here we go
451.28 -> good and now I can refactor it like this so
I can copy this everything including this
463.28 -> section and we can paste it to the template
of widget component here we go also I move on
475.28 -> this on export JSON handler here we go and
also I have to copy styles so I will copy
493.6 -> this part header widget icon basically everything
except the content so it will move to styles here
510.08 -> and go back and instead of widget
513.76 -> oops I forgot it here and instead of widget
we just say that it should be app widget
525.2 -> here we go so if we go back to
check it here everything looks good
533.36 -> and hopefully our application should
work as well what's wrong here
545.52 -> ah okay now i have to place it host here because
we apply uh these styles to our host element
558.08 -> good uh now it should be compiled successfully
and you can see nothing has been changed but
564.8 -> our app component became more lean and it
is responsible exactly for rendering the
575.44 -> content and rendering the toolbar but still if
we have a look at the widget component we can say
584.24 -> that all right widget component it generates
the layout for the view of our component and
594.64 -> it exports the data as a json and most probably
the export of the json is not this logic i
606.56 -> mean is not responsibility of the component
itself yeah it can handle this on export json
614.4 -> click event however the logic is not
the responsibility of this component so
621.6 -> i would suggest you to create the separate service
and this service we could call json exporter
637.04 -> all right so let's run it and yeah we
see that we created json exporter service
647.6 -> so we can move this logic from here and create
some methods like export yeah here we go
665.28 -> so yeah and here i would inject our json
exporter like this and now i can call
678.72 -> the export method against this json exporters
exporter sorry and i can save it i should save it
687.2 -> here as well and we can go back and click export
as a json and it doesn't do its job what is wrong
704.08 -> ah sorry actually it does it for whatever reason
709.76 -> jumps right i have to fix it all right now it's
better just if you're curious it's a command
718.24 -> option minus on or plus on mac os and you zoom
zoom in or zoom out this view all right never mind
728.56 -> but still you can see that our widget is working
but we have splitted responsibility between three
740.16 -> entities app component widget component and
also service but one recommendation so keep it
749.76 -> find your balance in splitting all these
responsibilities because you may split it to such
756.8 -> a small pieces that yeah you will be flexible like
a hell but it might be too hard to support this
766.08 -> so find your balance usually you find it within
your team during the code reviews and yeah it's
774.24 -> my variety from team to team uh what the what
responsibility of what because there is no
782.4 -> some super strict rule which can determine
that here responsibility of this component
789.28 -> ends and starts the responsibility of another one
and this is everything about single responsibility
796.96 -> let's move forward and the next one is open closed
principle so open close principle very important
804.96 -> one and it sounds like software entities should
be open for extension but closed for modification
815.84 -> and what does it mean on the in the real life it
means that you should design your classes models
822.48 -> and libraries even libraries such a way that you
should extend it functionality without touching
832.88 -> this component it is a very good example libraries
you cannot modify components which are coming from
842.72 -> some certain library yeah because they closed
they are published into the npm you have no
848.16 -> access to the source code to change but still you
have to be able somehow extend this functionality
858.72 -> and here i prepared another example it is very
similar to what we see for single responsibility
866.08 -> but now we have two widgets but different content
inside so the first one is weather but when
875.6 -> the second one is uh oops it's by mistake also
whether but it is the velocity which shows the
884.56 -> last velocity of your last sprint
and this is how i implemented this it
891.6 -> has small differences comparing to the first
implementation but this is like pretty much
899.36 -> similar we have the ev separately apple uh
apple app widget and it has the property
907.6 -> weather or velocity and if we have a look at the
component implementation itself we will see that
917.6 -> it has such a thing like ng if if it is if if
it widget is weather then we render the view
926.4 -> for weather otherwise we render the velocity
view you can of course use ng switch here it
934.08 -> doesn't really matter for this particular case
but you understand the idea so the problem with
940.8 -> this that it is closed definitely especially if
we distribute this while library it is closed you
947.84 -> cannot modify it but it is not open you cannot
add the new content to it as example i want to
956.08 -> have third widget which renders i don't know
some just a paragraph or some another view
965.28 -> and i cannot do this because i restricted by
only these two types either weather or velocity
974.8 -> and this is the violation of open close principle
how we can solve this issue well we can create
986.32 -> separate view or separate component for every
of this widget and then we would use the just
996.72 -> a content projection like ng content so i
would suggest you to generate two components
1009.44 -> first one i will name weather content and then
it will be scss it will be flat in line by the
1020.64 -> way you can configure this everything inside
the angularjson file but i'm too lazy to do it
1030.56 -> i think it is enough so we can run it here we go
and then we create the same bot for velocity oops
1042.64 -> here we go i can close it i can close i
believe this part and now let's move this class
1052.8 -> or this html from weather to weather content right
here here we go and then we will do similar to for
1070.56 -> our velocity velocity and this part and copy it
here then we have to adjust this part we have to
1083.68 -> remove this everything and instead we will use
mg content right so here instead of ng content
1096.16 -> will be rendered anything we put between uh app
widget tag you'll see this in action and there are
1104.8 -> another way how to implement it you can use
this with the component outlet and ng container
1111.84 -> but it doesn't really matter for this case
uh you are free to implement as as you want
1120.8 -> yeah and i save it and i have to i have to
probably add some styles yeah because the
1131.12 -> definitely value and widget icon should be part of
styles for these guys so i will create a new file
1142.24 -> i will say widget content scss so here will be
common uh styles for for this everything and and
1157.68 -> weather widget weather widget it is um ah here we
go this is this container let's rename it to the
1167.6 -> widget content here we go and for weather
we also rename it and now we have to copy
1179.28 -> this part as well and we and we copy it here
and as we renamed it widget content it should be
1193.52 -> good and yeah i forgot that style overalls should
be here and let's import the widget content scss
1206.88 -> here we go and the same will do for this part as
well so i save it and inside app component already
1216.8 -> i don't need these inputs anymore and i can
define between those um widget tags i can
1228.56 -> define which content i want to have here and
in my case this is the web app weather content
1240.56 -> and in the second case i want to have app velocity
content and now see the uh benefit of this
1252.08 -> everything if i want to introduce the new widget
and with some specific content there inside
1261.2 -> i can easily do this i can just place the
paragraph there and say that content is
1271.68 -> coming i don't know whatever and you can see
that without modifying the app widget itself i
1281.76 -> could extend it with another content and now if
you have a look at our widgets you can see that
1290.64 -> i reused completely the widget itself but
content is uh customizable and i can easily
1299.92 -> customize this and change the view so this is
the open close uh design principle in action
1308.64 -> all right and the next one in the turn is uh
lisk of barbara lisco's substitution principle
1316.32 -> and it sounds like functions that use
pointers or references to base classes
1324.16 -> must be able to use objects of derived classes
without knowing it yeah it sounds a little bit
1332.48 -> complicated however everything boils down to next
thing imagine you have two classes parent class
1341.92 -> child class which extends the apparent
one and lisco substitution principle
1348.72 -> means that objects of this super class or
parent class should be replaceable with its
1359.36 -> subclasses or child class without breaking the
application let's have a look at the example
1366.32 -> so here is pretty much similar where we stopped
except i fixed the issue with titles for our
1376.32 -> widgets yeah and and this is pretty much it
there you can see the content for for our widgets
1386.96 -> widget content css and yeah things like
that however let's imagine that we want to
1394.96 -> have slightly different wrappers yeah we we want
to have this one and maybe we we are planning to
1405.44 -> have some another modifications of this widget
container and we know that all those containers
1417.68 -> will have some functionality like export to json
and all of them should support the input title and
1429.44 -> we can we don't want to duplicate this everything
here we want to extract this to the i don't know
1438.08 -> some base class so let's create this have a class
and i will name it like widget base i will call it
1453.36 -> and let's generate one
1461.28 -> so inside this widget-based class i
will move the whole logic from here
1468.56 -> so i will copy it and paste it right
there then i have to import the input
1479.6 -> okay class is using yeah this is it means that we
have to decorate this with directive as example
1491.2 -> because since iv you have to do it if you
going to use inputs as example and extend your
1501.12 -> another component with this new one what
we exactly uh going to do right now so i do
1508.64 -> not export but extend this is what happens when
i uh typing and talking at the same time so we
1519.28 -> successfully extended this and how we can
violate the list of substitution principle
1527.36 -> we can violate this by overriding
this method on export json and
1534.24 -> as example instead of exporting the export json
we throw some error like i do not support it all
1546.56 -> right and this is the only violation because
it changes the behavior of this on export json
1558.88 -> comparing to its parent there's completely
different logic and if you replace the
1566.64 -> implementation of this widget base with
widget content you will break your application
1574.88 -> which is actually the violation of a list
of substitution principle so the solution
1582.8 -> to not violate this principle is to just
remove it and remove it completely if you
1589.44 -> don't do a do some any additional logic for
widget component or if you do you should not
1599.44 -> break the contract of this it should
not return some completely different
1605.44 -> type which is not compatible so if here
return type is void let's say then also
1615.04 -> type of this handler should be also
void and you can of course call super
1622.16 -> and then you have to you can i don't know
do something like console lock additional
1628.24 -> whatever so this is how you can uh extend
it without violating this design principle
1634.8 -> all right we're getting closer to the end and
the next one is interface segregation principle
1642.56 -> and it sounds like many client-specific interfaces
are better than one general purpose interface and
1653.6 -> what does it mean as example uh we have velocity
and weather content and we would like to define
1664.24 -> some interface for them some contract yeah and
we usually do it via interfaces so let's generate
1674.72 -> the interface i will call it widget content and
run it here so here we go and let's open this
1686.16 -> widget content and as an interface let's say i
want to have the id which should be some string
1694.96 -> maybe just for testing purpose whatever and
let's assume that this content should support
1705.44 -> let's save live reloading so means that
we're pulling some data from the server if
1712.8 -> some new data arrives we render the view and we
can say that we need the property loading yeah to
1723.28 -> show some maybe spinner during the
request to the back end which is boolean
1730.88 -> and we need some maybe function let's say reload
and it should return void all right so we save
1741.92 -> it and we implement this interface for every uh
content so it implements widget content here we go
1754.96 -> it already complains that we have to implement all
necessary fields so let's implement this interface
1762.08 -> here's the string we can define some i know
empty string as a default and here we can
1770.08 -> say that it falls and reload throws some error
but let's say that con so log do polling right
1783.92 -> so this is what will happen for this widget
content and we have to do the same for another one
1793.92 -> so let's save it right here and for weather we're
going to do absolutely the same so i import so i
1805.2 -> import widget content and we also need to
implement this thing here empty of false and
1819.36 -> do polling whatever so and i see that i didn't
save my app model okay and now we are done
1833.76 -> but here is the problem imagine that
1838 -> someone i know product owner comes to us and says
that uh you know what uh that life reloading for
1846.88 -> weather makes sense because weather changes uh
really fast and we need to reflect the latest
1853.44 -> data but velocity we update this data only i don't
know every end of the sprint usually two weeks
1863.28 -> and we don't need to do polling for this
data it should not be supported yeah it
1869.36 -> doesn't make sense for this kind of data so it
should not support the live reloading but the
1879.52 -> thing is that we have to okay we don't support
we can uh leave it empty but in this case uh we
1890.8 -> have still have to keep this loading property
and reload just in order to follow the widget
1899.6 -> content interface so this is where interface
segregation can help us so instead of having this
1911.2 -> let's say big interface we have to split it on
two interfaces let's say we can create interface
1924.08 -> reloadable right and we can move these two guys
inside the reloadable interface and then we would
1935.76 -> say that okay weather should support it so
we will implement also reloadable interface
1945.28 -> here we go but here we do not support live
reload so we can safely uh remove this code
1953.84 -> which is not being used and which is obsolete for
our velocity content like this and this is the
1962.88 -> interface segregation principle in action so you
just split one big interface to many other smaller
1971.12 -> interfaces all right finally the
last but not least design principle
1977.52 -> is dependency inversion principle and it says that
we should depend upon obstruction no concretions
1988.48 -> how does it look in the real life angular
application you encounter this design
1996.48 -> principle every time when you use dependency
injection let me show you some small example
2004.96 -> say we have the service polling service and
you inject this service inside your constructor
2014.96 -> and we have to call super because we extend
widget base all right so once you inject this
2022.32 -> you might think that you depend on concrete class
on the concrete service which is polling service
2029.76 -> but it just looks like this in fact angular under
the hood performs some magic uh and and it creates
2041.6 -> such a thing called injector which acts
exactly like this abstraction layer
2048.88 -> if you don't completely understand what i mean
by that i would recommend you check out my video
2054.48 -> about angular dependency injection and this video
will make everything clear but just to finish
2064.08 -> this example what i mean that we don't depend
on the polling service is that you know that
2071.28 -> we can use dependency providers like use class
as example and we can i don't know provide some
2081.6 -> another polling like this and i have
to create a some specific class and
2091.28 -> and having this setup we are not pointing
to the polling service anymore but rather to
2098.64 -> uh pointing to this another polling class
so so despite we see the reference to some
2106 -> concrete implementation here it doesn't mean
that this concrete implementation will be used
2113.84 -> but i'm going to remove it and i will show you
slightly another example for this everything
2123.68 -> so actually we can create a lot of obstructions
we can create obstructions over abstractions
2131.12 -> and so on and so forth sometimes it is over
engineering sometimes it might be helpful and
2138.64 -> let's imagine that in widget
component inside our wrapper
2146.24 -> we want to get reference to the component which
supports the live reloading and trigger this
2154.56 -> reload method so how i could do it i would
i would call content child and i would say
2164 -> that i want to get reference to the weather
component because it supports live reload
2170.64 -> right so i will say that this is content and
that type will be a weather component like this
2179.84 -> and it might be optional of course and somewhere
let's say in ng after view indeed or sorry content
2190.32 -> in it and let's say if content exists i
want to call the reload method for this
2201.76 -> component and i will remove these things and
the problem is that we depend on the concrete
2214.24 -> content weather content when in fact we might have
a lot of and other different uh contents which
2224 -> are reloadable which are supporting the um
polling mechanism and life reloading right
2231.68 -> so this is a violation of dependency
inversion principle because we depend
2238.8 -> on um concrete implementation rather than
obstruction so let's create this obstruction
2246.32 -> and this abstraction will be represented in
my case by injection token so i can create the
2255.44 -> widget content token and here i will export
constructable content and it's going to be
2266.48 -> new injection token which has value reload
will content right and the interface will be
2279.28 -> reloadable so basically we say that
the value for this injection token
2286.16 -> should support should implement this reloadable
interface so so it should have the reload method
2293.36 -> and its loading property right and now we have to
provide some uh assign some class to this token
2303.52 -> and as we know weather content it is something
which is implement this reloadable interface so
2311.6 -> we will use dependency injection in order
to provide the this component instance as
2320.88 -> a value for our injection token so we
say that provide reloadable content and
2329.84 -> use existing weather content components so once we
try to get reference to their reloadable content
2341.12 -> there will be it will be resolved as an instance
of this class right if you have some problems with
2348.56 -> understanding this i have video which will pop
up right there at the top and there i explain
2356.56 -> in details this mechanism good and now having this
abstraction layer which is represented as a token
2367.52 -> we can replace our concrete implementation
with this token and we can say here that
2376.4 -> the interface will be not the weather component
content but it will be something reloadable
2387.84 -> right so now we don't depend
on the concrete widget content
2394.08 -> we don't care what will be inside we just
care that this component or even directive
2402.4 -> should support this method and these properties
that's it and if we save it and reload our
2416.08 -> page we see that reload is happening called
once for weather component because velocity
2425.2 -> doesn't support this feature and this
anything widget also doesn't support
2433.44 -> reloadable so in our case we have this widget
component which is which is high level model
2441.44 -> and it doesn't depends on the concrete
implementation it depends on the abstraction and
2448.32 -> our concrete implementation also depends on the
on the abstraction implementing this reloadable
2455.44 -> interface and this is how dependency inversion
looks in action all right guys thank you for
2464.4 -> attention it was everything i wanted to tell you
about solid principles in angular don't forget
2472 -> that i have couple of courses about angular
material theming and graphql engine called
2477.76 -> hasura in the video description you will find
links to these courses and discounts which are
2484.8 -> currently active also don't forget to subscribe to
this channel leave your feedback in the comments
2490.8 -> as you can see i listen to your feedback and of
course share this video if you find this useful
2497.68 -> i wish you productive week ahead and
thank you for attention see you in the
2502.16 -> future you
Source: https://www.youtube.com/watch?v=Y-MRJ9QYCvI