React + Arquitectura limpia
⏳ 5 min
Aproximación a una arquitectura limpia con React y Typescript.
En esta ocasión quiero mostraros una aproximación a lo que sería una arquitectura limpia utilizando React + Typescript, y digo aproximación porque obviamente siempre se puede hacer mejor.
Primero de todo, ponte cómodo/a, prepárate una taza de ☕️ y vamos a ello.
El primer punto será cómo vamos a organizar nuestras carpetas, y así tener una vista general de cómo estará estructurado el proyecto.
proyecto/
├── src/
│ ├── Domain/
│ ├── Application/
│ ├── Infrastructure/
│ ├── Presentation/
Perfecto, ahora que ya tenemos una vista general de las estructura del proyecto, vamos a sumergirnos en cada una de estas ‘capas’.
Domain
Este es el corazón de nuestra aplicación. Aquí es donde definimos nuestras entidades de negocio, que son como los personajes principales de nuestra historia. Por ejemplo, si estuviéramos construyendo una aplicación de tipo blog, podríamos tener una entidad Post
que represente una entrada de blog en sí misma. Además, aquí es donde residen nuestros repositorios
, que son los elementos encargados de comunicarse con nuestra base de datos o cualquier otra fuente de datos externa.
Application
Esta capa es donde ocurre la magia. Aquí definimos nuestros casos de uso, que son como las aventuras que nuestros personajes (las entidades) van a vivir. Siguiendo con nuestro ejemplo de la aplicación de blog, podríamos tener un caso de uso llamado getPosts
que se encargue de todo el proceso de pedir el listado de entradas de blog; otro ejemplo sería createPost
, que se encargará de todo el proceso de creación de una nueva entrada. Esta capa actúa como el director de la orquesta, coordinando cómo se utilizan nuestras entidades y repositorios para lograr los objetivos de la aplicación. En definitiva, es donde se encuentra la lógica de negocio.
Infrastructure
Aquí es donde todo se conecta con el mundo exterior. Tenemos nuestros Data Transfer Objects (DTOs), que son como los mensajeros que llevan la información de un lugar a otro, especialmente útiles cuando trabajamos con servicios web o APIs. También encontramos nuestras implementaciones concretas de los repositorios, donde definimos cómo interactuar con nuestras fuentes de datos reales, ya sea una base de datos, una API REST o cualquier otra cosa. Y por último, pero no menos importante, aquí también podemos encontrar utilidades para manejar cosas como, por ejemplo, solicitudes HTTP.
Presentation
Esta es la capa donde la magia de la experiencia de usuario (UX) cobra vida. Es donde nuestras interfaces de usuario (UI) toman forma. Aquí es donde definimos nuestros componentes de React (o cualquier otro framework que estemos usando) y nuestras páginas.
Con estas cuatro capas bien definidas y organizadas, tenemos una base sólida para construir una aplicación robusta, mantenible y escalable.
Pasamos a la acción
Llegados a este punto… ¡creo que va tocando ver cómo se traduce toda esta teoría en código real!
¡Vamos a ello!
Capa de Dominio
// 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
}
Capa Aplicación
// Application/useCases/getPostUseCase.ts
export class GetPostsUseCase {
postRepository: PostRepositoryImpl
constructor() {
this.postRepository = new PostRepositoryImpl()
}
execute() {
return this.postRepository.getPosts()
}
}
Capa de Infrastructura
// Infrastructure/DTOs/PostDTO.ts
export interface PostDTO {
id: number
title: string
body: string
userId: number
}
En este punto te muestro una implementación de una utilidad para manejar peticiones HTTP:
// 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
Y continuamos con la implementación del repositorio:
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,
}
})
}
}
Capa de presentación
Vamos con un par de ejemplos de UI con los que mostraremos el listado de entradas:
Un componente:
// Presentation/components/PostItem.tsx
export default function PostItem({ id, title, body }: Post) {
return (
<li key={id}>
<h3>{title}</h3>
<p>{body}</p>
</li>
)
}
Una página:
// 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>
</>
)
}
¡Y esto es todo!
🎉 Hemos hecho un pequeño recorrido por los fundamentos de cómo estructurar una aplicación con React y TypeScript usando arquitectura limpia. Desde organizar nuestras carpetas hasta escribir el código para cada capa.
Espero que ahora tengas una mejor idea de cómo mantener tu proyecto bien organizado y fácil de mantener.
💪 Recuerda, una buena estructura no solo hace que tu código sea más limpio y manejable, sino que también facilita la vida de cualquier desarrollador que trabaje contigo (¡o incluso a ti mismo en el futuro!). Así que, la próxima vez que empieces un proyecto, no subestimes el poder de una buena arquitectura.