Clean Architecture: Applying with React
Tags: react, typescript, cleanarchitecture, webdev
Published: 2022-07-24 12:54:55

This text is part of a series of texts about [Clean Architecture](https://dev.to/rubemfsv/clean-architecture-the-concept-behind-the-code-52do) analysis applied with different frameworks and languages.

The purposes of this text are in line with those of the previous text, which are: I. Show an architectural division of a React application using Clean Architecture; II. Guide the implementation of new features in this proposed architecture.

## Architectural Division

The initial step is to analyze how the division is performed.

```
cypress/
src/
  data/
    protocols/
    test/
    usecases/
  domain/
    errors/
    models/
    test/
    usecases/
  infra/
    cache/
    http/
    test/
  main/
    adapters/
    config/
    decorators/
    factories/
      cache/
      decorators/
      http/
      pages/
      usecases/
    routes/
    scripts/
    index.tsx
  presentation/
    assets/
    components/
    hooks/
    pages/
    protocols/
    routes/
    styles/
    test/
  requirements/
  validation/
    errors/
    protocols/
    test/
    validators/
```

In detail, the purpose of each file structure is the following:

- **cypress**: It contains the application's end-to-end test files (for large projects, this folder is recommended to be in a separate project, so that the team responsible for e2e tests can take care of it, as they do not need to know the project code).
- **src**: Contains all files needed for the application.
 - **Data**: The data folder represents the data layer of the Clean Architecture, being dependent on the domain layer. Contains the implementations of business rules that are declared in _domain_.
 - **Domain**: Represents the domain layer of the Clean Architecture, the innermost layer of the application, not having any dependency on any other layer, where it contains the business rules.
 - **Infra**: This folder contains the implementations referring to the HTTP protocol and the cache, it is also the only place where you will have access to external dependencies related to these two items mentioned. This folder also contains most of the external libraries.
 - **Main**: It corresponds to the main layer of the application, where the interfaces developed in the presentation layer are integrated with the business rules created in the folders that represent the innermost layers of the Clean Architecture. All this is due to the use of design patterns such as Factory Method, Composite and Builder.
 - **Presentation**: This folder contains the visual part of the application, with its pages, components, _hooks_, assets and styling.
- **Requirements**: Contains documented system requirements.
- **Validation**: Where it contains the implementations of the validations used in the fields.

Unlike the [approach with Flutter](https://dev.to/rubemfsv/clean-architecture-applying-with-flutter-487b/) - where there was a central folder where all the tests were concentrated - in this approach the tests are located in the respective folders inside the `src`.

## Implementation Guide

In this section, a recommended logical sequence will be described for a better implementation performance of React systems using this architecture.

In order to simplify the explanation, unit tests will not be described in detail. However, it is strongly recommended to start with unit tests before development (TDD) of each step using the _requirements_ to support the scenarios. And after finalizing the scenarios, test the end to end flow (if it is one of the main ones, keep in mind the [test pyramid](https://martinfowler.com/articles/practical-test-pyramid.html)).

The following demonstration is the creation of the Login flow to log into an application.

### First step: Create business rules in the domain layer

Inside src/domain/usecases, create `authentication.ts`. This file will be an interface that will describe the authentication business rule.

```
import { AccountModel } from '@/domain/models/';

export interface IAuthentication {
  auth(params: Authentication.Params): Promise<Authentication.Model>;
}

export namespace Authentication {
  export type Params = {
    email: string;
    password: string;
  };

  export type Model = AccountModel;
}
```

As we can see, it is an interface that has an `auth()` function that receives the  _Authentication.Params_ which are declared in a namespace below - containing the parameters type (email and password) and the model type (_AccountModel_) - and expects to return an _Authentication.Model_ asynchronously.

_AccountModel_ is a named export of the model created in src/domain/models that represents the token that is returned after authentication to persist the session.

```
export type AccountModel = {
  accessToken: string;
};
```

### Second step: Implement the rules in the data layer

In this layer, we create the use case to implement the rule created previously in the domain layer, but inside src/data/usecases.

The file usually looks like the example below.

```
import { IHttpClient, HttpStatusCode } from '@/data/protocols/http';
import { UnexpectedError, InvalidCredentialsError } from '@/domain/errors';
import { IAuthentication, Authentication } from '@/domain/usecases';

export class RemoteAuthentication implements IAuthentication {
  constructor(
    private readonly url: string,
    private readonly httpClient: IHttpClient<RemoteAuthenticationamespace.Model>
  ) {}

  async auth(
    params: Authentication.Params
  ): Promise<RemoteAuthenticationamespace.Model> {
    const httpResponse = await this.httpClient.request({
      url: this.url,
      method: 'post',
      body: params,
    });

    switch (httpResponse.statusCode) {
      case HttpStatusCode.ok:
        return httpResponse.body;
      case HttpStatusCode.unauthorized:
        throw new InvalidCredentialsError();
      default:
        throw new UnexpectedError();
    }
  }
}

export namespace RemoteAuthenticationamespace {
  export type Model = Authentication.Model;
}
```

As we can see, the _RemoteAuthentication_ class implements the _IAuthentication_ interface, receiving the HTTP client and the url for the request. In the `auth()` function it receives the parameters, and calls the httpClient passing the url, the method (in this case it is _post_) and the body (which are the parameters). This return is a _httpResponse_ of the type referring to the _Authentication.Model_ that has a response status code, and which, depending on its result, gives the respective return - and may return the value expected by the request or an error.

The status codes are the [HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status):

```
export enum HttpStatusCode {
  ok = 200,
  created = 201,
  noContent = 204,
  badRequest = 400,
  unauthorized = 401,
  forbidden = 403,
  notFound = 404,
  serverError = 500,
}
```

### Third step: Implement the pages in the _presentation_ layer

To simplify the understanding, only code snippets referring to the authentication function call will be presented. The Login screen contains more actions and details that go beyond authentication. Consider the page prototype below for easier visualization.

![Login page](https://camo.githubusercontent.com/0d99b7013bf6f71b20c1cf31c66b26795e83668722f05055c58e895df67da5ce/68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f75706c6f6164732f61727469636c65732f76797275763565726f633165623570376665726a2e706e67)

In src/presentation/pages/ the Login page will be created, which is composed by components, methods and functions. The component that calls the authentication function is the `<Button/>` that is contained in the form to get the input values, as shown in the following code snippet:

```
<form
  data-testid="loginForm"
  className={Styles.form}
  onSubmit={handleSubmit}
> 
  <Input
    autoComplete="off"
    title="Enter your e-mail"
    type="email"
    name="email"
  />
  <Input
    autoComplete="off"
    title="Enter your password"
    type="password"
    name="password"
    minLength={6}
  />
  <Button
    className={Styles.loginBtn}
    type="submit"
    disabled={state.isFormInvalid}
    title="Login"
    data-testid="loginButton"
  />
</form>
``` 
When clicking on the `Button`, the `handleSubmit()` that is in the `onSubmit` of the `form` is called.

```
const handleSubmit = async (
    event: React.FormEvent<HTMLFormElement>
  ): Promise<void> => {
    event.preventDefault();
    try {
      const account = await authentication.auth({
        email: state.email,
        password: state.password,
      });

      setCurrentAccount(account);
      history.replace('/');
    } catch (error) {
      // Error handling here
    }
  };
```

Where the `authentication.auth()` on click will call a _factory_ (we'll see later) to do the authentication. In this case, it is passing the parameters captured by the input and the value returned from the request is saved in the cache through `setCurrentAccount(account)`.

### Fourth step: Connect all layers for requests to work

After everything is implemented, now just connect all the parts. For this, the design pattern [Factory Method](https://refactoring.guru/design-patterns/factory-method) is used.

Inside src/main/factories/usecases we create the _factory_ of the use case being implemented. In the case of this example, it is related to authentication.

The _makeRemoteAuthentication_ is created, which returns the _RemoteAuthentication_ that receives as a parameter the _factory_ of the _Http Client_ and the _factory_ that creates the URL. The URL of the API you want to request is passed as a parameter along with the _factory_ that creates the URL. In the example it is the URL that ends with _/login_.

```
import { RemoteAuthentication } from '@/data/usecases/';
import { IAuthentication } from '@/domain/usecases';
import { makeAxiosHttpClient, makeApiUrl } from '@/main/factories/http';

export const makeRemoteAuthentication = (): IAuthentication => {
  const remoteAuthentication = new RemoteAuthentication(
    makeApiUrl('/login'),
    makeAxiosHttpClient()
  );

  return remoteAuthentication;
};
```

After that, in src/main/factories/pages, the folder for the Login factories is created. In pages with forms, form validations are also injected, but as the focus of this text is on integrations, we will leave this point out of the explanation.

```
import React from 'react';
import { Login } from '@/presentation/pages';
import { makeRemoteAuthentication } from '@/main/factories/usecases/';

const makeLogin: React.FC = () => {
  const remoteAuthentication = makeRemoteAuthentication();

  return (
    <Login
      authentication={remoteAuthentication}
    />
  );
};

export default makeLogin;
```

A `makeLogin` const representing the _factory_ is created. It has `makeRemoteAuthentication` which is injected inside the Login page created in the _presentation_ layer so that the page has access to these requests.

### Fifth step: Apply the page created in the application

Finally, it is necessary to call the Login _factory_ in the application, so that it can be accessed by the user.

In the _router.tsx_ file located in src/main/routes, add the _factory_ page created into the _Switch_ inside _BrowserRouter_. The route is passed in the _path_, in this case it is _/login_, and the page in the _component_, which in this case is the pointer to the `makeLoginPage` _factory_ . This logic is used with all other pages, only changing from Route to PrivateRoute if the route is authenticated. The code looks like this below.

```
const Router: React.FC = () => {
  return (
    <ApiContext.Provider
      value={{
        setCurrentAccount: setCurrentAccountAdapter,
        getCurrentAccount: getCurrentAccountAdapter,
      }}
    >
      <BrowserRouter>
        <Switch>
          <Route exact path="/login" component={makeLogin} />
          <PrivateRoute exact path="/" component={makeDashboard} />
        </Switch>
      </BrowserRouter>
    </ApiContext.Provider>
  );
};
```

## Conclusion

Clean Architecture despite being a bit complex to understand and implement at the beginning - and even seem redundant -, abstractions are necessary. Several design patterns are applied to ensure the quality and independence of the code, facilitating the evolution and independent maintenance of the framework. In cases like this, if you want to change the framework from _React_ to _Angular_ or any other _Typescript_ based framework, just change the presentation layer and make adjustments to the dependencies.

Following the development process and understanding why you are doing it in such a way makes code production easier. After a while it ends up being done naturally, as it has a linear development process: I. Use case in the domain layer; II. Use case in the data layer; III. Creation of UI in the presentation layer; IV. Creation of _factories_ to integrate all layers into the main layer; V. And the call of the main _factory_ in the application routes.

As the example has many abstracted parts, it is recommended that you read the code for the hidden parts for a better understanding. [In this repository](https://github.com/rubemfsv/clean-react-app) you can access abstracted code similar to the one given in this example.

You can also access this architecture just by running the `npx @rubemfsv/clean-react-app my app` command, similar to create-react-app, but in a cleaner and more scalable way. Find out how to do it reading [this post](https://dev.to/rubemfsv/set-up-a-react-app-using-clean-architecture-by-running-one-command-133c).

## References

* Rodrigo Manguinho https://github.com/rmanguinho/clean-react
* MARTIN, Robert C. Clean Architecture: A Craftsman’s Guide to Software Structure and Design. 1st. ed. USA: Prentice Hall Press, 2017. ISBN 0134494164.