When I saw for the first time the acronym IDP (Individual Development Plan), I didn’t know what it was or how to create one. In the talk with my Tech Leader, we agreed that a topic could involve areas for improvement that I myself identified. However, Looking at my list of knowledge that I wanted to develop, I felt lost where to start and which area I should focus on first.
Definition
Based on these reflection, I defined my initial objectives:
1 - To improve my technical skills
2 - To document the entire project in English
Defining the subject wasn’t simple, because I tried not only considering my development, but how the IDP could add value to the company. It would be something useful, something I could apply in my daily life, and something that would help me making more informed architectural decisions in the future.
I thought: working as Full-Stack is impossible to know everything, but dominating principles of architecture makes all the difference, independently of the technology stack used.
So, I chose to study and apply SOLID (Single Responsibility, Open–Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion principles) in practice.
Based on these motivations I identified one more objective:
3 - To be a developer capable of applying best practices from the idealization step of a project.
My main motivation for choosing this subject came from my own experience. Many projects that I worked, I didn't have a mentor and learned by making mistakes, delivering the best I could in the shortest time possible.
Understanding SOLID better, would help me developing more organized and scalable systems, this directly impacts the quality of my work.
Planning
I organized a list of stacks and practices that I would love to apply:
1 - Laravel, using Eloquent, which is already part of my routine. To focus only on the concept and architecture rather than reinventing already established functionalities.
2 - Vue.js with Composition API
3 - ESLint and Prettier, ensuring that the code follows the standards without triggering errors in the IDE extensions.
4 - I was unsure whether to use Service Layer or Repository Pattern, I chose Service Layer, better explained in the details below.
Not everything I defined at the beginning was delivered, such as authentication, CORS (Cross-Origin Resource Sharing), deployment, and git hooks. I plan to do this level of implementation at another time. I focused only on best practices and architecture.
With that, I had the subject of the project: A map of urban legends, where I apply best practices, refine my skills in Laravel and Vue.js, and learn more about SOLID principles.
Practice
Why the Composition API?
I started with Vue.js using the Options API, but in some projects, I realized that the complexity grew along with the file size. I started working with the Composition API on ongoing projects and really enjoyed the experience, as it facilitates reusage and organization.
Applying SOLID principles to the front-end isn't so obvious, but I focused on separating responsibilities and making the code reusable.
I understood that the components should only handle UI
I created a src/api/ folder containing:
-
connect.js→ responsible for configuring the connection to the API -
legend.js→ responsible for querying the API endpoints
Each file has a single responsibility.
The components do not depend on Axios, but only on the functions implemented inside legend.js.
For example, if I replace Axios for GraphQL, nothing in the component needs to be changed.
With this structure, the UrbanLegendMap.vue component only needs to handle UI (Leaflet), only consumes domain functions, and does not load HTTP logic inside of it.
Why the Service Layer?
In projects, it's very difficult to change the ORM, so implementing the Repository Pattern would only add an unnecessary layer and reproduce old habits. I understood that Repositories would perform a role that Eloquent already does. There would be no gain.
Therefore, the Service Layer makes business decisions, handles approaches, and deals with any reusable logic, becoming responsible for the business rules.
I understood that each class only needs one reason to be changed
The controller of the project was only responsible for receiving the request, calling the service interface, and returning an HTTP response, for example:
public function store(StoreUrbanLegendRequest $request)
{
$legend = $this->service->create($request->validated());
return (new UrbanLegendResource($legend))
->response()
->setStatusCode(Response::HTTP_CREATED);
}
No validation logic or business rules should exist in the Controller.
Inside the project, each time a new legend is created, a slug is generated to be accessed via URL.
This responsibility was assigned to the Model.
Therefore:
- Validations are handled by Form Requests.
- Business rules are handled by the Service.
- Slug generation, UUIDs, and relationships are handled by the Model.
This kept each part of the system with well-defined responsibilities.
I understood that a structure doesn't need to be altered, but it should always be open for extension.
The controller only knows the service's interface, it doesn't depend on the service's implementation. If it's necessary to migrate the service's implementation to an external API, for example, then you just need to create another service that implements the same interface.
Nothing in the controller needs to change, and the service and controller depend on the same logical interface.
I understood that testing features can be beneficial
I usually only test the service or use the case of the application, but since I was dealing with a very well-structured project, I opted to create tests in two ways: feature tests and unit tests.
With feature tests, I can test the complete flow: route + middleware + request + service + model + resource, for example:
public function test_validates_required_fields_and_returns_422(): void
{
User::factory()->create();
$res = $this->withHeaders([
'Authorization' => 'Bearer ' . $this->token,
])->postJson('/api/legend', $this->payload([
'title' => '',
]));
$res->assertStatus(422)
->assertJsonValidationErrors(['title']);
}
This doesn't mean I'll always test features, but since my front-end depends exclusively on the external API, I thought it was important to have this test to verify if the response was in accordance with HTTP principles, and it also helped me writing the documentation.
Unit tests validated the business rule in isolation, for example:
public function test_list_returns_filtered_legends(): void
{
$this->service->create($this->payload([
'title' => 'Lenda - Brasília',
'city' => 'Brasília',
]));
$this->service->create($this->payload([
'title' => 'Lenda - Florianópolis',
'city' => 'Florianópolis',
]));
$results = $this->service->list(['city' => 'Brasília']);
$this->assertCount(1, $results);
$this->assertEquals('Brasília', $results->first()->city);
}
I understood that this IDP changed my way of thinking
Throughout this IDP, my first and main objective was to improve my technical knowledge. I sought to understand each step and each implementation and to understand the reasoning behind each line. I consulted many articles to understand not only the theory but also to have practical examples.
By creating a project applying SOLID principles in practice, I understood that each architectural decision impacts the rest of the system. It's much better to have code that does what it's supposed to with an easily maintainable architecture.
This study wasn't just technical; it also changed my way of thinking. Currently, before writing code, I reflect on:
- Who is responsible for this?
- Does this class have a single reason to change?
- Are there any unnecessary methods or functions?
- Can it be extended without breaking?
- Will I have a headache if I need to perform maintenance?
These questions have become part of my daily routine. That's exactly what I was looking for when I chose SOLID as my main topic: to learn something that wasn't just applicable to the IDP, but that could accompany my professional life from now on.
Although not all planned items were completed (authentication), I consider the IDP successful. The process brought me technical maturity and a much more solid understanding of how to structure a project, from idealization onwards, because I didn't just learn a technology, but I learned to think about architecture intentionally.
I learned that good practices don't depend on the stack, but on conscious decisions
And this evolution doesn't end here.
Link of project: https://github.com/kapcruz/urbanlegendmaps
Referencies:
O que é SOLID: O guia completo para você entender os 5 princípios da POO
Top comments (0)