首页 > web前端 > js教程 > 可扩展性之路(如何管理前端服务。

可扩展性之路(如何管理前端服务。

WBOY
发布: 2024-09-10 11:11:12
原创
468 人浏览过

简介:正确管理前端服务的重要性

了解如何以可扩展和可读的方式管理服务非常重要,不仅对于应用程序的性能,而且对于维护开发人员的健康和福祉。服务是应用程序中与外部通信的部分,例如调用 API、与数据库交互,甚至管理电话权限(例如访问联系人)。对这些服务的良好管理可确保您的应用程序具有可扩展性,并且不会在将来造成麻烦。

在本文中,我们将探索如何使用存储库模式以可扩展的方式管理前端服务。这种方法允许以受控且清晰的方式访问服务,封装对 API 的调用并促进代码重用及其可维护性。

在本文中,我们将了解如何实施此解决方案、要遵循的良好实践以及它如何帮助您确保代码可扩展且易于维护。

管理服务的基本概念:DTO 和适配器

在组织良好的架构中,了解应用程序不同层的结构非常重要。基础层之一是基础设施层,它是管理与外部交互的服务的地方。

在这个基础设施层中,经常出现的两个关键概念是DTO(数据传输对象)和适配器。

  • DTO(数据传输对象):DTO 是表示 API 或数据库响应中的数据的接口。它们的作用是确保我们从外部服务接收的信息符合我们的应用程序可以轻松处理的特定格式。例如,DTO 可以定义我们从 API 接收的用户对象的结构。

  • 适配器:适配器是负责将数据从 DTO 转换为应用程序域接口的函数,反之亦然。也就是说,它们可以翻译我们接收的数据或翻译我们发送的数据。这样,如果我们收到的信息将来发生变化,我们只需关注适配器,而不是整个应用程序。

DTO和适配器的使用使得基础设施层能够灵活、轻松地适应外部服务的变化,而不影响应用程序逻辑。此外,它在各层之间保持明确的分离,并且每个层都履行其特定的职责。

我们收到的数据示例:

Camino hacia la escalabilidad ( Cómo Gestionar Servicios en Frontend.

// getUserProfile.adapter.ts

const userProfileAdapter = (data: UserProfileDto): UserProfile => ({
    username: data.user_username,
    profilePicture: data.profile_picture,
    about: data.user_about_me,
})

export deafult userProfileAdapter;
登录后复制

我们发送的数据示例:

Camino hacia la escalabilidad ( Cómo Gestionar Servicios en Frontend.



存储库模式
存储库模式基于将数据访问逻辑与应用程序或业务逻辑分开的想法。这提供了一种以集中和封装的方式拥有实体在应用程序中拥有的方法的方法。

最初,我们必须使用该实体将具有的方法的定义来创建存储库的接口。

// UserProfileRepository.model.ts

export interface IUserProfileRepository {
   getUserProfile: (id: string) => Promise<UserProfile>;
   createUserProfile: (payload: UserProfile) => Promise<void>;
}
登录后复制

我们继续创建我们的存储库。

// userProfile.repository.ts

export default function userProfileRepository(): IUserProfileRepository {
  const url = `${BASE_URL}/profile`;

  return {
     getUserProfile: getProfileById(id: string) {
          try {
            const res = await axios.get<UserProfileDto>(`${url}/${id}`);             
            return userProfileAdapter (userDto);
          } catch(error){
            throw error;
          }           
     },
     createUserProfile: create(payload: UserProfile) {
          try {
            const body = createUserProfilAdapter(payload);
            await axios.post(`${url}/create`, payload);
          } catch(error) {
            throw error;
          }
     }
  }
}
登录后复制

这种方法允许我们封装 API 调用,并通过适配器将我们收到的 DTO 格式的数据转换为我们的内部格式。

下面您将看到我们遵循的架构或结构图,其中基础设施层包括服务、DTO 和适配器。这种结构使我们能够清晰地分离业务逻辑和外部数据。

Camino hacia la escalabilidad ( Cómo Gestionar Servicios en Frontend.



错误处理
我们可以通过创建错误处理程序来进一步改进我们的代码。

// errorHandler.ts

export function errorHandler(error: unknown): void {
  if (axios.isAxiosError(error)) {
    if (error.response) {
      throw Error(`Error: ${error.response.status} - ${error.response.data}`);
    } else if (error.request) {
     throw Error("Error: No response received from server");
    } else {
      throw Error(`Error: ${error.message}`);
    }
  } else {
    throw Error("Error: An unexpected error occurred");
  }
}
登录后复制
// userProfile.repository.ts

import { errorHandler } from './errorHandler';

export default function userProfileRepository(): IUserProfileRepository {
  const url = `${BASE_URL}/profile`;

  return {
    async getUserProfile(id: string) {
      try {
        const res = await axios.get<UserProfileDto>(`${url}/${id}`);
        return userProfileAdapter(res.data);
      } catch (error) {
        errorHandler(error);
      }
    },
    async createUserProfile(payload: UserProfile) {
      try {
        const body = createUserProfileAdapter(payload);
        await axios.post(`${url}/create`, body);
      } catch (error) {
        errorHandler(error);
      }
    }
  }
}
登录后复制

Esto nos permite desacoplar la lógica de presentación de la lógica de negocio, asegurando que la capa de UI solo se encargue de manejar las respuestas y actualizar el estado de la aplicación.


Implementación de los servicios
Una vez que hemos encapsulado la lógica de acceso a datos dentro de nuestro repositorio, el siguiente paso es integrarlo con la interfaz de usuario (UI).

Es importante mencionar que no hay una única forma de implementar el patrón Repository. La elección de la implementación dependerá mucho de la arquitectura de tu aplicación y de qué tan fiel quieras a las definiciones de la misma. En mi experiencia, una de las formas más útiles y prácticas de integrarlo ha sido mediante el uso de hooks, el cual desarrollaremos a continuación.

export function useGetUserProfile(id: string) {
  const [data, setData] = useState<UserProfile | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);
  const repository = userProfileRepository();

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const userProfile = await repository.getUserProfile(id);
        setData(userProfile);
      } catch (err) {
        setError((err as Error).message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [id]);

  return { data, loading, error };
}

登录后复制
interface UserProfileProps {
  id: string;
}

export function UserProfile({ id }: UserProfileProps) {
  const { data, loading, error } = useUserProfile(id);

  if (loading) return <Skeleton />;
  if (error) {
      toast({
        variant: "destructive",
        title: "Uh oh! Something went wrong.",
        description: error, 
      });
  }


  return (
    <div>
      <h1>{data?.username}</h1>
      <img src={data?.profilePicture} alt={`${data?.username}'s profile`} />
      <p>{data?.about}</p>
    </div>
  );
}

登录后复制

Ahora, el componente UserProfile no necesita saber nada sobre cómo se obtienen los datos del perfil, solo se encarga de mostrar los resultados o mensajes de error.

Esto se puede mejorar aún más si las necesidades lo requieren, por ejemplo con la utilizacion de react query dentro del hook para tener un mayor control sobre el cacheo y actualización de datos.

export function useGetUserProfile(id: string) {
  const repository = userProfileRepository();

  return useQuery({
    queryKey: ['userProfile', id], 
    queryFn: () => repository.getUserProfile(id), 
  });
 }
登录后复制



Conclusión

En este artículo, hemos explorado cómo implementar el patrón Repository en frontend, encapsulando las llamadas a las APIs y manteniendo una separación clara de responsabilidades mediante el uso de DTOs y adaptadores. Este enfoque no solo mejora la escalabilidad y el mantenimiento del código, sino que también facilita la actualización de la lógica de negocio sin tener que preocuparse por los detalles de la comunicación con los servicios externos. Además, al integrar React Query, obtenemos una capa extra de eficiencia en la gestión de datos, como el cacheo y la actualización automática.

Recuerda que no existe una manera única del manejo de servicios (por ejemplo también existe el patrón sagas para el manejo de side-effects). Lo importante es aplicar las buenas prácticas para asegurarnos de que nuestro código siga siendo flexible, limpio y fácil de mantener en el tiempo.

Espero que esta guía te haya sido útil para mejorar la manera en la que gestionas los servicios en tus proyectos frontend. Si te ha gustado, no dudes en dejar un comentario, darle me gusta o compartir el artículo para que más desarrolladores puedan beneficiarse. ¡Gracias por leer!

以上是可扩展性之路(如何管理前端服务。的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板