Fermer

février 4, 2025

Du transfert de données simple aux architectures composables modernes

Du transfert de données simple aux architectures composables modernes


Dans le paysage du développement Web d’aujourd’hui, le concept d’une application monolithique est devenu de plus en plus rare. Les applications modernes sont composées de plusieurs services spécialisés, chacun gère les aspects spécifiques de la fonctionnalité. Ce changement ne s’est pas produit du jour au lendemain – c’est le résultat de décennies d’évolution dans notre façon de penser et de mettre en œuvre le transfert de données entre les systèmes. Explorons ce voyage et voyons comment il façonne les architectures modernes, en particulier dans le contexte des solutions CMS sans tête.

Lorsque les ordinateurs ont commencé à se parler, les méthodes étaient remarquablement simples. Dans les premiers jours d’Internet, les systèmes ont échangé des fichiers via FTP ou communiqué via des prises TCP / IP brutes. Cette approche directe a bien fonctionné pour des cas d’utilisation simples, mais a rapidement montré ses limites, car les applications sont devenues plus complexes.

# Basic socket server example
import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('localhost', 12345))
server_socket.listen(1)

while True:
    connection, address = server_socket.accept()
    data = connection.recv(1024)
    # Process data
    connection.send(response)

La vraie percée pour permettre une communication complexe entre les ordinateurs d’un réseau est venue avec l’introduction de Appels de procédure à distance (RPC) Dans les années 1980. RPC a permis aux développeurs d’appeler des procédures sur des systèmes distants comme s’il s’agissait de fonctions locales, abstractant la complexité de la communication réseau. Ce modèle a jeté les bases de nombreuses approches d’intégration modernes que nous utilisons aujourd’hui.

À la base, RPC implémente un modèle client-serveur où le client prépare et sérialise un appel de procédure avec des paramètres, envoie le message à un serveur distant, le serveur désérialise et exécute la procédure, puis renvoie la réponse au client.

Voici un exemple simplifié en utilisant XML-RPC de Python.

# Server
from xmlrpc.server import SimpleXMLRPCServer

def calculate_total(items):
    return sum(items)

server = SimpleXMLRPCServer(("localhost", 8000))
server.register_function(calculate_total)
server.serve_forever()

# Client
import xmlrpc.client

proxy = xmlrpc.client.ServerProxy("http://localhost:8000/")
try:
    result = proxy.calculate_total([1, 2, 3, 4, 5])
except ConnectionError:
    print("Network error occurred")

RPC peut fonctionner à la fois en modes synchrones (blocage) et asynchrones.

Les implémentations modernes telles que le GRPC prennent en charge le streaming et la communication bidirectionnelle. Dans l’exemple ci-dessous, nous définissons un service GRPC appelé Calculator avec deux méthodes RPC, Calculatequi prend un Numbers message et renvoie un Result message, et CalculateStreamqui envoie un flux de Result messages en réponse.

// protobuf
service Calculator {
  rpc Calculate(Numbers) returns (Result);
  rpc CalculateStream(Numbers) returns (stream Result);
}

INTERGATIONS MODERNES: L’essor des services Web et SOA

La fin des années 1990 et le début des années 2000 ont vu l’émergence de Services Web et Architecture axée sur le service (SOA). SOAP (simple protocole d’accès aux objets) est devenu la norme pour l’intégration de l’entreprise, introduisant une approche plus structurée de la communication du système.

<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <m:GetStockPrice xmlns:m="http://www.example.org/stock">
      <m:StockName>IBM</m:StockName>
    </m:GetStockPrice>
  </soap:Body>
</soap:Envelope>

Bien que SOAP ait fourni des fonctionnalités d’entreprise robustes, sa complexité et sa verbosité ont conduit au développement d’alternatives plus simples, en particulier les API REST qui dominent la communication des services Web aujourd’hui.

Mais le repos n’est pas seul. Jetons un coup d’œil à certains modèles d’intégration modernes.

API RESTFULS

Repos (transfert d’état de représentation) est devenu la norme de facto pour les API Web, offrant une approche simple et apatride pour manipuler les ressources. Sa simplicité et sa nature basée sur HTTP le rendent idéal pour les applications Web.

Défini par Roy Fielding en 2000 comme un style architectural en plus des protocoles standard du Web, ses contraintes s’alignent parfaitement sur les objectifs du Web moderne, tels que performance, évolutivité, fiabilitéet visibilité: Client et serveur séparés par une interface et des réponses de communication apatrides couplées sans état.

Dans les applications modernes, les implémentations les plus courantes du protocole REST sont basées sur le format JSON, qui est utilisé pour coder des messages pour les demandes et les réponses.

// Request
async function fetchUserData() {
  const response = await fetch('https://api.example.com/users/123');
  const userData = await response.json();
  return userData;
}

// Response
{
  "id": "123",
  "name": "John Doe",
  "_links": {
    "self": { "href": "/users/123" },
    "orders": { "href": "/users/123/orders" },
    "preferences": { "href": "/users/123/preferences" }
  }
}

Graphique

GraphQL a émergé des besoins de développement interne de Facebook en 2012 avant d’être open source en 2015. Né des défis de la création d’applications mobiles complexes, il a abordé les limitations des API de repos traditionnelles, en particulier les problèmes de données excessives et sous-fascinantes.

À la base, GraphQL est un langage de requête et un runtime qui fournit un système de type et une récupération de données déclaratives, permettant au client de spécifier exactement ce qu’il veut récupérer à partir du serveur.

// graphql
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  publishDate: String!
}

query GetUserWithPosts {
  user(id: "123") {
    name
    posts(last: 3) {
      title
      publishDate
    }
  }
}

Souvent utilisé pour construire des UIS complexes avec des structures de données imbriquées, des applications mobiles ou des architectures de microservices, il s’est avéré efficace à gérer les exigences de données complexes à grande échelle et offre un écosystème croissant d’outils.

Webhooks

Les applications modernes nécessitent souvent des mises à jour en temps réel. Par exemple, les applications de commerce électronique doivent mettre à jour les niveaux d’inventaire lorsqu’un achat est effectué ou que les applications de gestion de contenu doivent actualiser le contenu mis en cache lorsqu’un document est modifié. Les modèles traditionnels de demande de réponse peuvent avoir du mal à répondre à ces demandes car ils comptent sur les serveurs de sondage des clients pour les mises à jour, ce qui est inefficace et à forte intensité de ressources.

Les webhooks et les architectures axées sur les événements répondent plus efficacement à ces besoins. WebHooks permet aux serveurs d’envoyer des notifications en temps réel à des clients ou à d’autres systèmes lorsque des événements spécifiques se produisent. Cela réduit le besoin d’un sondage continu. Les architectures motivées par des événements vont plus loin en découplant les composants d’application. Les services peuvent publier et s’abonner aux événements de manière asynchrone, ce qui rend le système plus évolutif, réactif et plus simple.

import fastify from 'fastify';

const server = fastify();
server.post('/webhook', async (request, reply) => {
  const event = request.body;
  
  if (event.type === 'content.published') {
    await refreshCache();
  }
  
  return reply.code(200).send();
});

Il s’agit d’une fonction Node.js simple qui utilise Fastify pour configurer un serveur Web. Il répond au point final /webhookvérifie le type champ de la demande JSON et rafraîchisse un cache si l’événement est de type content.published.

Avec toutes ces informations de fond et connaissances techniques, il est plus facile d’imaginer l’état actuel du développement des applications Web, où Une seule application monolithique n’est plus la réponse aux besoins de l’entreprisemais un nouveau paradigme a émergé: l’architecture composable.

Architecture composable et CMS sans tête

Cette évolution nous a conduits au concept d’architecture composable, le moment où les applications sont construites par combiner des services spécialisés. C’est là que les solutions CMS sans tête ont un avantage clair, servant d’exemple parfait de la façon dont les modèles d’intégration modernes se réunissent.

Les plates-formes CMS sans tête séparent la gestion du contenu de la présentation de contenu, vous permettant de créer des frondules spécialisées en s’appuyant sur un backend de contenu entièrement traduit. Ce découplage facilite Réutilisation de contenu, échelle indépendanteet le flexibilité pour utiliser une technologie ou un service dédié pour chaque partie du système.

Prendre Blok par exemple. StoryBlok est un CMS sans tête conçu pour aider les développeurs à créer des applications flexibles, évolutives et composables. Le contenu est exposé via API, REST ou GraphQL; Il offre une longue liste d’événements qui peuvent déclencher un webhook. Les éditeurs sont satisfaits d’un excellent éditeur visuel, où ils peuvent voir des changements en temps réel, et de nombreuses intégrations sont disponibles prêtes à l’emploi via un marché.

Imaginez ce ContentDeliveryService Dans votre application, où vous pouvez interagir avec l’API REST de StoryBlok en utilisant le Client JS open source:

import StoryblokClient from "storyblok-js-client";

class ContentDeliveryService {
  constructor(private storyblok: StoryblokClient) {}

  async getPageContent(slug: string) {
    const { data } = await this.storyblok.get(`cdn/stories/${slug}`, {
      version: 'published',
      resolve_relations: 'featured-products.products'
    });

    return data.story;
  }

  async getRelatedContent(tags: string[]) {
    const { data } = await this.storyblok.get('cdn/stories', {
      version: 'published',
      with_tag: tags.join(',')
    });

    return data.stories;
  }
}

La dernière pièce du puzzle est un véritable exemple d’intégration.

Encore une fois, beaucoup sont déjà disponibles sur le marché StoryBlok, et vous pouvez facilement les contrôler à partir du tableau de bord. Cependant, pour tirer parti entièrement l’architecture composable, nous pouvons utiliser l’outil le plus puissant de la main du développeur: le code.

Imaginons une plate-forme de commerce électronique moderne qui utilise StoryBlok comme centre de contenu, Shopify for Inventory and Orders, Algolia pour la recherche de produits et Stripe for Payments.

Une fois chaque compte configuré et que nous avons nos jetons d’accès, nous pourrions rapidement construire une page frontale pour notre magasin. Ce n’est pas un code prêt pour la production, mais juste pour avoir une idée rapide, utilisons React pour créer la page pour un seul produit qui intègre nos services.

Tout d’abord, nous devons initialiser nos clients:

import StoryblokClient from "storyblok-js-client";
import { algoliasearch } from "algoliasearch";
import Client from "shopify-buy";


const storyblok = new StoryblokClient({
  accessToken: "your_storyblok_token",
});
const algoliaClient = algoliasearch(
  "your_algolia_app_id",
  "your_algolia_api_key",
);
const shopifyClient = Client.buildClient({
  domain: "your-shopify-store.myshopify.com",
  storefrontAccessToken: "your_storefront_access_token",
});

Étant donné que nous avons créé un blok dans StoryBlok qui contient des informations sur les produits tels que le product_idnous pourrions écrire un composant qui prend le productSlugrécupère le contenu du produit de StoryBlok, les données d’inventaire de Shopify et certains produits connexes de l’indice d’algolie:

async function fetchProduct() {
  // get product from Storyblok
  const { data } = await storyblok.get(`cdn/stories/${productSlug}`);

  // fetch inventory from Shopify
  const shopifyInventory = await shopifyClient.product.fetch(
    data.story.content.product_id
  );

  // fetch related products using Algolia
  const { hits } = await algoliaIndex.search("products", {
    filters: `category:${data.story.content.category}`,
  });
}

Nous pourrions alors définir un état de composant simple:

const [productData, setProductData] = useState(null);
const [inventory, setInventory] = useState(null);
const [relatedProducts, setRelatedProducts] = useState([]);

useEffect(() =>
  // ...
  // combine fetchProduct() with setState to update the state
  // ...

  fetchProduct();
}, [productSlug]);

Et renvoyer un modèle avec toutes nos données:

<h1>{productData.content.title}</h1>
<p>{productData.content.description}</p>
<h2>Price: ${inventory.variants[0].price}</h2>
<h3>Related Products</h3>
<ul>
  {relatedProducts.map((product) => (
    <li key={product.objectID}>{product.name}</li>
  ))}
</ul>

Nous pourrions ensuite utiliser une approche axée sur des événements et créer un serveur qui écoute les événements de notre boutique et traite la caisse avec Stripe (crédits à Manuel Spigolon pour Ce tutoriel):

const stripe = require('stripe')

module.exports = async function plugin (app, opts) {
  const stripeClient = stripe(app.config.STRIPE_PRIVATE_KEY)

  server.post('/create-checkout-session', async (request, reply) => {
    const session = await stripeClient.checkout.sessions.create({
      line_items: [...], // from request.body
      mode: 'payment',
      success_url: "https://your-site.com/success",
      cancel_url: "https://your-site.com/cancel",
    })

    return reply.redirect(303, session.url)
  })
// ...

Et avec cette approche, chaque service est indépendant des autres, ce qui nous aide à atteindre nos objectifs commerciaux (performance, évolutivité, flexibilité) avec une bonne expérience de développeur et une application plus petite et plus simple qui est plus facile à entretenir.

Conclusion

L’intégration entre les CMS sans tête et les services Web modernes représente l’état actuel et futur des applications Web haute performance. En utilisant des services spécialisés et découplés, les développeurs peuvent se concentrer sur la logique métier et l’expérience utilisateur. Un écosystème composable est non seulement modulaire mais également résilient aux besoins en évolution de l’entreprise moderne.

Ces intégrations mettent en évidence l’importance de maîtriser les architectures basées sur API et de comprendre comment les différents outils peuvent s’inscrire harmonieusement dans une pile technologique plus grande.

Dans le paysage numérique d’aujourd’hui, le succès réside dans le choix des outils qui offrent une flexibilité et une efficacité, s’adapter à l’évolution des demandes et créer des applications qui sont à l’épreuve des défis de demain.

Si vous souhaitez plonger plus profondément dans les intégrations que vous pouvez construire avec StoryBlok et d’autres services, consultez Page des intégrations de StoryBlok. Vous pouvez également aller plus loin en créant vos propres plugins avec Développement du plugin de StoryBlok ressources.

Smashing Editorial
(YK)






Source link