React + Clean Architecture

⏳ 5 min

Approach to Clean Architecture with React and Typescript.

This time I want to show you an approach to what a clean architecture would look like using React + Typescript. I say approach because obviously, there’s always room for improvement.

First of all, get comfortable, grab yourself a cup of ☕️, and let’s dive in.

The first point will be how we’re going to organize our folders, giving us an overview of how the project will be structured.

proyecto/
├── src/
│   ├── Domain/
│   ├── Application/
│   ├── Infrastructure/
│   ├── Presentation/

Perfect, now that we have a general overview of the project structure, let’s dive into each of these ‘layers’.

Domain

This is the heart of our application. Here is where we define our business entities, which are like the main characters of our story. For example, if we were building a blog application, we could have a Post entity representing a blog entry itself. Additionally, this is where our repositories reside, which are responsible for communicating with our database or any other external data source.

Application

This layer is where the magic happens. Here we define our use cases, which are like the adventures that our characters (the entities) are going to experience. Continuing with our example of the blog application, we could have a use case called getPosts responsible for the entire process of fetching the list of blog entries; another example would be createPost, which handles the entire process of creating a new entry. This layer acts as the conductor of the orchestra, coordinating how our entities and repositories are used to achieve the application’s objectives. Ultimately, it’s where the business logic resides.

Infrastructure

This is where everything connects with the outside world. We have our Data Transfer Objects (DTOs), which are like messengers carrying information from one place to another, especially useful when working with web services or APIs. We also find our concrete implementations of repositories, where we define how to interact with our actual data sources, whether it’s a database, a REST API, or anything else. And last but not least, here we can also find utilities for handling things like HTTP requests.

Presentation

This is the layer where the magic of the user experience (UX) comes to life. It’s where our user interfaces (UI) take shape. Here is where we define our React components (or any other framework we’re using) and our pages.

With these four well-defined and organized layers, we have a solid foundation for building a robust, maintainable, and scalable application.

Let’s dive into action!

At this point… I think it’s time to see how all this theory translates into real code!

Let’s do it!

Domain layer

// Domain/entities/Post.ts
export interface Post {
  id: number
  title: string
  body: string
}
// Domain/repositories/postRepository.ts
export interface PostRepository {
  getPosts: () => Promise<Post[]>
  // ...resto de métodos
}

Application layer

// Application/useCases/getPostUseCase.ts
export class GetPostsUseCase {
  postRepository: PostRepositoryImpl
  constructor() {
    this.postRepository = new PostRepositoryImpl()
  }

  execute() {
    return this.postRepository.getPosts()
  }
}

Infrastructure layer

// Infrastructure/DTOs/PostDTO.ts
export interface PostDTO {
  id: number
  title: string
  body: string
  userId: number
}

At this point, I’ll show you an implementation of a utility to handle HTTP requests:

// Infrastructure/http/index.ts
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE"
type Headers = {
  [key: string]: string
}
type IHttp<TBody> = {
  token?: string | null
  url: string
  method?: HttpMethod
  headers?: Headers
  body?: TBody
}

async function Http<TResponse, TBody>(options: IHttp<TBody>): Promise<TResponse> {
  const {
    token,
    url,
    method = "GET",
    headers = { ContentType: "application/json" },
    body,
  } = options

  const authToken = {
    ...(headers || {}),
    ...(token ? { Authorization: `Bearer ${token}` } : {}),
  }
  const response = await fetch(url, {
    method,
    headers: { ...headers, ...authToken },
    body: body ? JSON.stringify(body) : undefined,
  })
  return await response.json()
}

export default Http

And we continue with the implementation of the repository:

export class PostRepositoryImpl implements PostRepository {
  async getPosts(): Promise<Post[]> {
    const posts: PostDTO[] = await http({
      url: "https://jsonplaceholder.typicode.com/posts",
      method: "GET",
    })

    return posts.map((postDTO): Post => {
      return {
        id: postDTO.id,
        title: postDTO.title,
        body: postDTO.body,
      }
    })
  }
}

Presentation layer

Let’s go with a couple of UI examples where we’ll display the list of entries:

A component:
// Presentation/components/PostItem.tsx
export default function PostItem({ id, title, body }: Post) {
  return (
    <li key={id}>
      <h3>{title}</h3>
      <p>{body}</p>
    </li>
  )
}
A page:
// Presentation/pages/PostsPage.ts
export default function DemoPage() {
  const [posts, setPosts] = useState<Post[] | null>(null)

  const handleGetPosts = async () => {
    const postsService = new GetPostsUseCase()
    const response = await postsService.execute()
    setPosts(response)
  }

  useEffect(() => {
    handleGetPosts()
  }, [])

  return (
    <>
      <ul>
        {posts &&
          posts.map((item) => {
            return <PostItem id={item.id} title={item.title} body={item.body} />
          })}
      </ul>
    </>
  )
}

And that’s it!

🎉 We’ve taken a brief tour through the fundamentals of structuring an application with React and TypeScript using clean architecture. From organizing our folders to writing the code for each layer.

I hope you now have a better idea of how to keep your project well-organized and easy to maintain.

💪 Remember, a good structure not only makes your code cleaner and more manageable, but it also makes life easier for any developer working with you (or even yourself in the future!). So, next time you start a project, don’t underestimate the power of good architecture.

Happy coding 💻

👈 Go back