Weekly React Rundown
React and S.O.L.I.D. Principles: Building Robust Applications (Part 2)
A SOLID Approach to Engineering
Co-authored by Thays Casado
1. Introduction
Welcome to the second article of our exciting series, "React and S.O.L.I.D. Principles: Building Robust Applications." For this piece, we'll continue developing our Task Management application while exploring the last three S.O.L.I.D. principles: Liskov Substitution Principle (L.S.P.), Interface Segregation Principle (I.S.P.), and Dependency Injection Principle (D.I.P.).
If you just landed on this article or are unsure what's happening, peek at the previous work here. You can find and download the source code from this repository.
2. Levelling Up with Liskov & Friends
This part of the project structure will focus on applying and leveraging each principle to enhance our current state. The expected outcome for this section is understanding the impact of the current design on our application.
The updated hierarchy can be seen below:
src/
βββ components/
β βββ Filter/
β βββ TaskList/
β βββ TaskItem/
β βββ TaskDialog/
β βββ Header/
βββ constants/
βββ state/
β βββ taskSlice.js
β βββ filterSlice.js
βββ hooks/
β βββ useTasksHook.js
β βββ useFiltersHook.js
βββ utils/
β βββ taskUtils.js
βββ App.js
2.1. Liskov Substitution Principle (L.S.P.)
- Components Flexibility: Ensure that your
TaskItem
,TaskDialog
, and other components adhere to standard interfaces or prop types. It will make them easily replaceable or extendable without affecting the parent components likeTaskList
or pages that use them. - Future Enhancements: As your application grows, you might introduce nef tasks (e.g., priority tasks, tasks with sub-tasks). The L.S.P. will guide the design so that these new components can be integrated seamlessly without modifications to existing components.
2.2. Interface Segregation Principle (I.S.P.)
- Focused Hooks: In the
hooks
directory, you can create hooks that serve single responsibilities. We created theuseTasks
for fetching and managing tasks, and for this part, we will create theuseFilter
for filtering information out of a given list. This keeps your components lean and focused on their U.I. roles. - Decoupled State Management: The
state
directory houses Redux slices or similar state management logic, ensuring components only subscribe to the minimal state they need. For instance, thistaskSlice.js
is used by task-related components without forcing them to know about unrelated state parts, and we will create a new slice namedfilterSlice.js
to handle filtering logic separately. This allows for a more focused and modular approach to state management, where the componentTaskList
can interact with both slices to display tasks based on selected filters without being directly coupled to the filtering logic itself.
2.3. Dependency Inversion Principle (D.I.P.)
- Decoupling Components from Direct Dependencies: Utilize the
hooks
andstate
directories to abstract the logic for data fetching, state management, and business rules. For example,useTaskService
could abstract away the API calls, allowing components likeTaskDialog
to be more testable and flexible regarding data sources. - Enhanced Testability and Flexibility: With D.I.P., your components become easier to test and evolve. For instance, changing the backend API or switching state management libraries would have minimal impact on your component layer, which depends on abstractions rather than concrete implementations.
It's important to reinforce that this project does not implement a backend. If you want me to show you how you could simulate or integrate with one, let me know in the comments! π
3. Conclusion
In the pursuit of building a robust and scalable React application, the transition from a basic project structure to one that incorporates S.O.L.I.D. principles has evolved. The updated structure, as shown above, is a testament to the transformative power of these principles.
With a nod to the Liskov Substitution Principle, components like TaskItem
and TaskDialog
are now designed to be easily interchangeable, promoting a seamless evolution as the application grows.
The Interface Segregation Principle has given rise to hooks like useTasksHook
and useFiltersHook
, which encapsulates specific functionalities, thereby streamlining components to their essential U.I. roles.
The Dependency Inversion Principle has further decoupled components from direct dependencies, paving the way for a codebase that is as testable as it is adaptable to change.
While the backend isn't part of this project's scope, the groundwork laid here provides a clear pathway for future integrations. Should there be an interest in backend simulations or integration techniques, the conversation can certainly extend in that direction.
Like what you see?