Maison >interface Web >js tutoriel >Chemin vers l'évolutivité (Comment gérer les services dans Frontend.

Chemin vers l'évolutivité (Comment gérer les services dans Frontend.

WBOY
WBOYoriginal
2024-09-10 11:11:12470parcourir

Introduction : L'importance d'une gestion correcte des services dans Frontend

Savoir gérer les services de manière évolutive et lisible est très important, non seulement pour les performances de l'application, mais aussi pour maintenir la santé et le bien-être des développeurs. Les services sont la partie de l'application qui communique avec l'extérieur, comme les appels aux API, l'interaction avec les bases de données, ou encore la gestion des autorisations téléphoniques, comme l'accès aux contacts. Une bonne gestion de ces services garantit que votre application peut être évolutive et ne causera pas de maux de tête à l'avenir.

Dans cet article, nous allons explorer comment gérer les services frontend de manière évolutive à l'aide du modèle de référentiel. Cette approche permet d'accéder aux services de manière contrôlée et claire, encapsulant les appels aux API et facilitant la réutilisation du code, ainsi que sa maintenabilité.

Tout au long de cet article, nous verrons comment mettre en œuvre cette solution, les bonnes pratiques à suivre et comment elle peut vous aider à garantir que votre code est évolutif et facile à maintenir.

Concepts fondamentaux pour la gestion des services : DTO et adaptateurs

Dans une architecture bien organisée, il est important de comprendre comment sont structurées les différentes couches d'une application. L'une des couches fondamentales est la couche infrastructure, où sont gérés les services qui interagissent avec l'extérieur.

Au sein de cette couche d'infrastructure, deux concepts clés qui apparaissent souvent sont les DTO (Data Transfer Objects) et les adaptateurs.

  • DTO (Data Transfer Object) : Les DTO sont des interfaces qui représentent des données dans les réponses des API ou des bases de données. Ils servent à garantir que les informations que nous recevons de services externes sont conformes à un format spécifique que notre application peut facilement gérer. Par exemple, un DTO pourrait définir la structure d'un objet utilisateur que nous recevons d'une API.

  • Adaptateurs : les adaptateurs sont des fonctions chargées de traduire les données des DTO vers les interfaces du domaine d'application, ou vice versa. Autrement dit, ils peuvent consister à traduire les données que nous recevons ou à traduire les données que nous envoyons. De cette façon, si les informations que nous recevons changent dans le futur, nous devrons nous concentrer uniquement sur l'adaptateur et non sur toute l'application.

L'utilisation de DTO et d'adaptateurs permet à la couche d'infrastructure d'être flexible et facilement adaptable aux changements de services externes sans affecter la logique applicative. De plus, il maintient une séparation claire entre les couches et que chacun remplisse ses responsabilités spécifiques.

Exemple de données que nous recevons :

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;

Exemple de données que nous envoyons :

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



Le modèle de référentiel
Le modèle de référentiel est basé sur l'idée de séparer votre logique d'accès aux données de votre application ou de votre logique métier. Cela permet d'avoir de manière centralisée et encapsulée les méthodes qu'une entité possède dans votre application.

Dans un premier temps nous devrons créer l'interface de notre référentiel avec la définition des méthodes dont disposera cette entité.

// UserProfileRepository.model.ts

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

Et nous continuons la création de notre référentiel.

// 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;
          }
     }
  }
}

Cette approche nous permet d'encapsuler les appels API et de convertir les données que nous recevons au format DTO vers notre format interne via l'adaptateur.

Vous verrez ci-dessous un schéma de l'architecture ou de la structure que nous suivons, où la couche d'infrastructure comprend des services, des DTO et des adaptateurs. Cette structure nous permet d'avoir une séparation claire entre la logique métier et les données externes.

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



Gestion des erreurs
Nous pouvons encore améliorer notre code en créant un gestionnaire d'erreurs.

// 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!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn