DEV Community

Dzmitry Harunou
Dzmitry Harunou

Posted on • Edited on

3

Clean Architecture for Frontend Applications

The article shares my experience of implementing Clean Architecture in frontend applications.

Repository with example:
https://github.com/harunou/react-tanstack-react-query-clean-architecture

Introduction to Clean Architecture

Clean Architecture is a "divide et impera" framework introduced by Robert C. Martin (Uncle Bob). It defines a set of principles and patterns that allow for building maintainable and scalable applications. A description can be found in Uncle Bob's article The Clean Architecture.

The most confusing aspect I face when working on Clean Architecture implementation in frontend projects is the principle of building an application independent of frameworks. This goal is more idealistic than pragmatic for real-world frontend applications. Even if a frontend application is tied to a framework, Clean Architecture still provides organizational clarity, creates isolated portable code units, and governs application flows.

By isolation of a code unit, I mean that the unit is a separate entity that can be developed, tested, and maintained independently.

By portable code unit, I mean that the unit can be moved from one framework to another without changing own core logic.

I feel free to use the API provided by a framework for units implementation until there is a clear requirement for the application to support multiple frameworks throughout its lifecycle.

In addition to the benefits mentioned in Uncle Bob's article, it is worth adding:

  • Limited context when working with a codebase. This helps to avoid keeping the whole application in mind and reduces cognitive load. This is achieved by isolating code units.
  • Unified control and data flow throughout the application. Debugging is made easier as at every point in the application, it is clear what will be called next and where the data flows from and to.
  • Testable code with clear boundaries for unit and integration tests, where tests are inline application specifications.
  • Resistance to code degradation even after multiple modifications by AI Assistants.

Clean Architecture Implementation

The diagram below represents Clean Architecture implementation for a typical frontend application with a store and API integration. The implementation can be used with any modern reactive frontend framework, like React, Vue, Svelte, or Angular.

basic clean architecture diagram

Next diagram represents an extended implementation of Clean Architecture for a typical frontend application, showing additional units into which an application can be factored.

extended clean architecture diagram

NOTE: the double lines on both diagrams are representing the boundaries between the units. Typically the data that crosses the boundaries is simple data structures, for example Data Transfer Objects (DTOs) or plain objects.

Definition of units

  • Entities Store: An aggregate unit that maintains a collection of enterprise business entities and/or application business entities and their states.
  • Use Case Interactor: Unit that orchestrates the flow of data in the application by coordinating entities, gateways, and transactions to fulfill specific user goals, implements application business rules.
  • Transaction: Unit with logic that transitions a store between two valid states, ensuring business rules are maintained.
  • Selector: Unit that derives values or data structures from the state without modifying it, implementing read-only queries against the state, implements application business rules.
  • Gateway: Unit that isolates external resources by providing interfaces for data access, mapping data from external resources into entities, and potentially caching data.
  • Effect: Unit that is responsible for encapsulating logic that interacts with external resources through gateways, managing side effects, and handling asynchronous operations.
  • Controller: Unit that handles input data from the view and converts it into use case invocations.
  • Presenter: Unit that transforms the application state into output data suitable for the view, often using selectors.
  • View: Unit that is responsible for displaying information to the user based on the data prepared by the presenter and for capturing user input and transferring it to the controller.
  • External Resource: External systems or services that the application interacts with, such as APIs, databases, storages, or other applications.

Definition of concepts utilized by the units

  • Enterprise Business Entity: Unit that encapsulates enterprise business rules and data.
  • Enterprise Business Rules and Data: The most general and high-level rules and data that would exist even if the application didn't. These are enterprise-wide rules that rarely change and are independent of any specific application.
  • Application Business Entity: Unit that encapsulates application-specific business rules and data.
  • Application Business Rules and Data: Rules and data specific to the application's functionality and presentation. This includes how business concepts are presented to users, interaction flows, UI state management, and application-specific behaviors. These are more likely to change compared to enterprise rules.
  • State: The value of a store at a given point in time, typically represented as an object structure.
  • Valid State: One of a finite number of store values that is conceptually considered valid according to business and application rules.

Core and Stability Gradient

The image represents a stability gradient. The most stable components, which are less likely to change, form the "core" of the application, while more volatile components reside outside.

core and stability gradient diagram

Flow of Control

The flow of control follows the same principle as defined in Clean Architecture: it starts with the controller, goes through the use case, and ends with the presenter.

flow of control diagram

Flow of Data

The data flow is unified. Data originates from user interactions/events captured by the view, follows the flow of control, and returns back to the view.

Pessimistic Flow of Data

In the pessimistic flow of data, the data captured by the view is processed by external resources before reaching the core and then returning to the view.

NOTE: Selectors can depend on gateways to obtain data fetch statuses (loading, error, success) or cached data. This is particularly applicable when the gateway implementation supports such features, for example, when using Tanstack React Query.

pessimistic flow of data diagram

Optimistic Flow of Data

In the optimistic flow of data, the data captured by the view is processed by the core first and then by the external resources. The first time, data reaches the view after the core has processed it. The second time (optional), data reaches the view after the external resources have processed it.

NOTE: Selectors can depend on gateways to obtain data fetch statuses (loading, error, success) or cached data. This is particularly applicable when the gateway implementation supports such features, for example, when using Tanstack React Query.

optimistic flow of data diagram

Testing

The layered structure creates natural boundaries for different types of tests, making them more stable. For example, tests written for units that belong to the application core are less likely to change when the views or external resources evolve.

NOTE: Resource integration tests evaluate how the gateway integrates with the external resource.

testing diagram

Drivers and Alternative Interfaces

While the primary interface is typically the view rendered in a browser, Clean Architecture allows for multiple ways to interact with the application core through different drivers during runtime. In fact, the view itself is a driver. One common alternative driver I implement is a CLI.

drivers diagram

Application Scale and Clean Architecture

Clean Architecture principles are scale-agnostic and can be effectively applied to applications of any size. Whether you're working on an enterprise-scale SPA or a simple single-file React app, the architectural boundaries and patterns remain valuable. To benefit, developers should have a clear understanding of how their code maps to Clean Architecture units while maintaining unified control and data flows.

scale diagram

Web Workers and Browser Storage (extra)

Web Workers and browser storage mechanisms (e.g., localStorage, etc.) are suggested to be treated as external resources and accessed through gateways. Later, they can be easily replaced with a backend service.

web workers diagram

Conclusion

Regardless of which libraries and frameworks an application is built with, from a Clean Architecture perspective, they are all tools. Clean Architecture guides us in dividing the application codebase into isolated, reusable units with predictable dependencies and a clean flow of control.

Heroku

Amplify your impact where it matters most — building exceptional apps.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

Real Talk: Realistic Voice AI with ElevenLabs

ElevenLabs is joining us to talk about how to power your applications with lifelike speech. Learn how to use ElevenLabs to enhance user interactions, build low-latency conversational agents, and tap into one of the leading AI voice generators.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️