Martin Joo
Martin Joo

@mmartin_joo

25 Tweets 6 reads Jan 23, 2023
🔥I just realized I never wrote anything about SOLID principles.
Shame on me… Let’s discuss all of them!
- Single-responsibility principle
- Open-closed principle
- Liskov substitution principle
- Interface segregation principle
- Dependency inversion principle
đź§µKeep Reading
1/24 Single-responsibility principle
Each class should have only one reason to change.
A great example is data and its representation. Usually, they change for different reasons. Hence it's probably a safe bet to decouple the query layer from the representation layer.
2/24 SRP
Which is the defacto industry standard nowadays, and one of the reasons why API+SPA is so popular. But it wasn't always the case.
Take a look at this class:
3/24 SRP
I couldn't count how many times I see something like that. This is a modern example of mixing the data and the representation layer in one class.
When we see a legacy project where there is a single PHP file with HTML, PHP, and MySQL in it, we cry in pain.
4/24 SRP
Of course, this resource is much better than that, but in fact, it has similar issues:
- The array is similar to HTML. It's the representation of the data.
- The Eloquent query is similar to MySQL. It's the query layer.
- And the whole class is in PHP.
5/24 SRP
Fortunately, the fix is pretty easy:
6/24 SRP
So we moved the query to the User model. But now we have another problem: the User model becomes a monster after just a few weeks.
So it’s probably a good idea to move that query into the Post model:
7/24 SRP
Now If a PM says: "can we please change the definition of 'most popular posts'?" I know I need to go to the Post model. And we also saved the User model. But we can easily have the same problem with the Post model as well.
8/24 SRP
This is why I prefer using single-use-case actions. And I think this is why they are getting more and more popular in the Laravel community. It looks like this:
9/24 SRP
Now we have two well-defined classes:
- UserResource is responsible only for the representation and it has one reason to change.
- GetMostPopularPosts is responsible only for the query and it has one reason to change.
10/24 Open-closed principle
A class should be open for extension but closed for modification
It sounds weird, I know. So let me show you an example.
11/24 OCP
We're working on a social app with users, posts, comments, and likes. Users can like posts, so you implement this feature in the Post model. But now, users also want to like comments. You have two choices:
12/24 OCP
- Copy the like-related features into the Comment model
- You implement a generic trait that can be used in any model
Of course, we want the second option. It looks something like that:
13/24 OCP
Now let's say we need to add a chat to the app, and of course, users want to like messages. So we do this:
14/24 OCP
This is pretty standard, right? But think about what happened here.
We just added new functionality to multiple classes without changing them!
We extended our classes instead of modifying them. And this is a huge win in the long term.
Traits are cool!
15/24 OCP
My upcoming book “Laravel Concepts” will contain a much more complicated example of OCP using products, their different variations, and orders.
You can join the waiting list here:
laravel-concepts.io
16/24 Liskov Substitution Principle
Each base class can be replaced by its subclasses
It sounds obvious and I think this is the easiest principle to comply with. However, there are some important things.
Consider this scenario:
17/24 LSP
We have an abstract EmailProvider and we use both MailChimp and ConvertKit for some reason. These classes should behave exactly the same way, no matter what.
This sounds obvious, however, there are some important thing that needs to be satisfied:
18/24 LSP
- Same method signatures with the exact same types
- Same return types. If you return an array you should use the exact same shape! Or use DTOs instead.
- The same exceptions should be thrown from each method
19/24 Interface Segregation Principle
You should have many small interfaces instead of a few huge ones
To be honest, this is the easiest principle to follow. You always want to write small interfaces or abstract classes with only a few methods.
20/24 ISP
Fortunately, in Laravel traits are pretty popular which is the best example of small interfaces:
21/24 Dependency Inversion Principle
Depend upon abstraction, not concretion.
Whenever you have a parent class and one or more subclasses you should use the parent class as a dependency. For example:
22/24 DIP
According to DIP, we want to do something like this:
23/24 DIP
So every class should depend on the abstract MarketDataProvider not on the concrete implementation.
Fortunately, this is pretty standard in Laravel thanks to its awesome service container.
24/24 Thank you!
Thank you if you’re still here!
I’m working on a book called “Laravel Concepts” which will go into MUCH more detail on the SOLID principles and other concepts.
laravel-concepts.io

Loading suggestions...