Vai al contenuto

Che cos’è la tecnologia MACH

In poche parole, MACH rappresenta dei principi di progettazione alla base della moderna tecnologia aziendale. Costruite per integrarsi facilmente, queste tecnologie aiutano le aziende a districarsi e ad abbandonare gli strumenti legacy.

Queste tecnologie sono promosse dalla MACH Alliance, un consorzio di aziende tecnologiche nato con l’obiettivo di promuovere queste pratiche e principi nell’industria del commercio digitale. L’obiettivo è consentire alle aziende di adottare soluzioni tecnologiche più flessibili, interoperabili e scalabili per soddisfare le esigenze del commercio digitale moderno.

Le aziende membri della MACH Alliance collaborano per condividere conoscenze, risorse e best practice. L’obiettivo è aiutare le organizzazioni a migliorare l’agilità, la velocità di sviluppo e l’innovazione nelle loro operazioni di commercio digitale.

Il nome “MACH” è l’acronimo di Microservizi, API-first, SaaS cloud-native, Headless. I quattro principi chiave sono di seguito esposti:

  1. Microservices: L’architettura basata su microservizi suddivide un’applicazione complessa in servizi più piccoli, autonomi e gestibili. Questo approccio rende più facile sviluppare, testare e implementare singoli componenti in modo indipendente. Inoltre, consente la scalabilità orizzontale, dove i singoli microservizi possono essere scalati separatamente in base alle esigenze di carico. Ciascun microservizio, grazie alla sua particolare indipendenza, può essere sviluppato con linguaggi di programmazione e tecnologie diverse tra di loro, a condizione che comunichino attraverso API standard.
  2. API-First: L’adozione di un approccio “API-First” implica che la progettazione e lo sviluppo di un’applicazione inizino con la definizione delle interfacce di programmazione delle applicazioni (API). Questo favorisce la creazione di sistemi più aperti, flessibili e interoperabili. Le API chiare e ben documentate semplificano l’integrazione di servizi e applicazioni di terze parti.
  3. Cloud-Native: Essere “Cloud-Native” significa sfruttare appieno le risorse e i servizi offerti dai fornitori di servizi cloud. Questo può includere l’uso di servizi come piattaforma, l’elasticità della scalabilità e la gestione dinamica delle risorse. L’architettura cloud-native è in grado di adattarsi dinamicamente alle variazioni del carico di lavoro.
  4. Headless: Un’architettura “Headless” separa il backend (logica di business e dati) dalla presentazione frontend (interfaccia utente). Ciò offre maggiore flessibilità nel design e nell’implementazione delle interfacce utente su diversi dispositivi. Ad esempio, è possibile utilizzare più frontend per dispositivi mobili, desktop, dispositivi IoT, ecc.

Un esempio concettuale

Consideriamo un’azienda di ecommerce che adotta i principi della MACH Alliance per migliorare la sua architettura tecnologica. Immaginiamo che questa azienda stia cercando di rinnovare il suo sistema di e-commerce per renderlo più flessibile, scalabile e orientato al futuro. In questo esempio, l’azienda applica i principi della MACH Alliance per creare un sistema di e-commerce più flessibile, scalabile e orientato al futuro. Questo approccio consente loro di adattarsi rapidamente alle nuove esigenze di mercato e di integrare facilmente nuovi servizi o canali di vendita.

  1. Microservices (Microservizi): l’azienda suddivide il suo sistema monolitico in vari microservizi autonomi, ognuno responsabile di una funzionalità specifica. Ad esempio, potrebbe avere microservizi separati per la gestione del carrello, l’elaborazione degli ordini, la gestione degli utenti e così via.
  2. API-First (API come Priorità): prima di iniziare a sviluppare i microservizi, l’azienda progetta chiaramente le API che definiranno come i diversi servizi comunicheranno tra loro. Ad esempio, l’API di gestione del carrello potrebbe definire come aggiungere o rimuovere articoli, mentre l’API di pagamento gestirà le transazioni.
  3. Cloud-Native (Nativo del Cloud): l’architettura cloud-native facilita l’implementazione di nuove funzionalità e aggiornamenti senza tempi di inattività significativi.
  4. Headless (Senza Testa): separando il frontend dal backend consente lo sviluppo di diverse interfacce utente per dispositivi mobili, desktop o altri canali, senza dover modificare la logica di business sottostante. Ad esempio, potrebbe avere un’app mobile dedicata e un sito web desktop, entrambi utilizzando gli stessi servizi di backend.

Un esempio pratico in Node.js

Creare un’applicazione completa basata sui principi della MACH Alliance richiede un’analisi attenta dei requisiti dell’utente e coinvolgerebbe i quattro aspetti fondamentali. Per tali motivi, il codice codice da produrre sarebbe considerevole. Volendo immaginare un esempio molto semplificato, possiamo implementare un mini sistema e-commerce, focalizzando la nostra attenzione sull’aggiunta di articoli al carrello.

Per arrivare a questo obiettivo, possiamo immaginiamo di avere due microservizi: uno per la gestione del catalogo degli articoli e un altro per la gestione del carrello. Utilizzeremo anche un’API per consentire la comunicazione tra questi servizi. E’ importante ribadire che i due microservizi sono totalmente indipendenti tra di loro, quindi potremmo pensare di utilizzare anche due linguaggi di programmazione diversi. Il collante che li tiene uniti e che li fa comunicare tra di loro, è appunto lo “scambio di messaggi” attraverso le API.

In questo esempio, siccome non vogliamo farci del male, utilizzeremo solo Node.js ed Express come framework e creeremo un’applicazione molto semplificata che gestisce un catalogo di prodotti e un carrello degli acquisti. Qualora non sia ancora stato installato, dovrà essere fatto attraverso il comando npm install express.

Gestione del catalogo catalogService.js

const express = require('express');
const app = express();
const port = 3001;

const catalog = {
  '1': { name: 'Prodotto 1', price: 20.0 },
  '2': { name: 'Prodotto 2', price: 30.0 },
  // Altri articoli nel catalogo
};

app.get('/catalog/:itemId', (req, res) => {
  const itemId = req.params.itemId;
  const itemDetails = catalog[itemId];
  if (itemDetails) {
    res.json(itemDetails);
  } else {
    res.status(404).json({ message: 'Prodotto non trovato nel catalogo' });
  }
});

app.listen(port, () => {
  console.log(`Microservizio Catalogo in esecuzione su http://localhost:${port}`);
});

Gestione del carrello cartService.js

const express = require('express');
const app = express();
const port = 3002;

const cart = {};

app.post('/cart/add', express.json(), (req, res) => {
  const { itemId, quantity } = req.body;
  const itemDetails = catalogService.getItem(itemId);

  if (itemDetails) {
    if (cart[itemId]) {
      cart[itemId].quantity += quantity;
    } else {
      cart[itemId] = { name: itemDetails.name, price: itemDetails.price, quantity };
    }
    res.json({ message: 'Prodotto aggiunto al carrello con successo' });
  } else {
    res.status(404).json({ message: 'Prodotto non trovato nel catalogo' });
  }
});

app.get('/cart', (req, res) => {
  res.json(cart);
});

app.listen(port, () => {
  console.log(`Microservizio Carrello in esecuzione su http://localhost:${port}`);
});
  • il microservizio di Catalogo gestisce le richieste per ottenere dettagli sugli articoli nel catalogo.
  • Il microservizio di Carrello gestisce le richieste per aggiungere articoli al carrello e ottenere il contenuto del carrello.
  • L’API coordina l’interazione tra i microservizi di Catalogo e Carrello, consentendo agli utenti di aggiungere articoli al carrello e ottenere il contenuto del carrello.

L’interfaccia utente (front-end)

Per completare l’esempio, possiamo creare un’applicazione frontend usando HTML, CSS e JavaScript per consentire agli utenti di interagire con l’API. Di seguito, ti mostro un esempio di frontend molto basico che potrebbe essere utilizzato per visualizzare il catalogo, aggiungere prodotti al carrello e visualizzare il contenuto del carrello.

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>MACH eCommerce</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 20px;
    }

    h1, h2 {
      color: #333;
    }

    #catalog, #cart {
      display: flex;
      flex-wrap: wrap;
    }

    .product, .cart-item {
      border: 1px solid #ccc;
      padding: 10px;
      margin: 10px;
      max-width: 200px;
    }

    button {
      cursor: pointer;
    }
  </style>
</head>
<body>

  <h1>MACH eCommerce</h1>

  <h2>Catalogo</h2>
  <div id="catalog"></div>

  <h2>Carrello</h2>
  <div id="cart"></div>

  <script src="app.js"></script>
</body>
</html>

Lo script da utilizzare in app.js:

document.addEventListener('DOMContentLoaded', () => {
  const catalogElement = document.getElementById('catalog');
  const cartElement = document.getElementById('cart');

  // Funzione per ottenere il catalogo dal server
  async function getCatalog() {
    try {
      const response = await fetch('http://localhost:3000/catalog');
      const catalog = await response.json();
      displayCatalog(catalog);
    } catch (error) {
      console.error('Errore durante il recupero del catalogo:', error);
    }
  }

  // Funzione per visualizzare il catalogo
  function displayCatalog(catalog) {
    catalogElement.innerHTML = '';
    for (const itemId in catalog) {
      const item = catalog[itemId];
      const productElement = createProductElement(itemId, item);
      catalogElement.appendChild(productElement);
    }
  }

  // Funzione per creare un elemento prodotto nel catalogo
  function createProductElement(itemId, item) {
    const productElement = document.createElement('div');
    productElement.className = 'product';
    productElement.innerHTML = `
      <h3>${item.name}</h3>
      <p>Prezzo: $${item.price.toFixed(2)}</p>
      <button onclick="addToCart('${itemId}')">Aggiungi al carrello</button>
    `;
    return productElement;
  }

  // Funzione per aggiungere un prodotto al carrello
  async function addToCart(itemId) {
    try {
      const quantity = prompt('Inserisci la quantità desiderata:', '1');
      if (quantity === null || isNaN(quantity)) {
        return;
      }

      const response = await fetch('http://localhost:3000/add_to_cart', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ itemId, quantity: parseInt(quantity, 10) }),
      });

      const result = await response.json();
      alert(result.message);
      getCart();
    } catch (error) {
      console.error('Errore durante l\'aggiunta al carrello:', error);
    }
  }

  // Funzione per ottenere e visualizzare il contenuto del carrello
  async function getCart() {
    try {
      const response = await fetch('http://localhost:3000/get_cart');
      const cart = await response.json();
      displayCart(cart);
    } catch (error) {
      console.error('Errore durante il recupero del carrello:', error);
    }
  }

  // Funzione per visualizzare il carrello
  function displayCart(cart) {
    cartElement.innerHTML = '';
    for (const itemId in cart) {
      const cartItem = cart[itemId];
      const cartItemElement = createCartItemElement(itemId, cartItem);
      cartElement.appendChild(cartItemElement);
    }
  }

  // Funzione per creare un elemento prodotto nel carrello
  function createCartItemElement(itemId, cartItem) {
    const cartItemElement = document.createElement('div');
    cartItemElement.className = 'cart-item';
    cartItemElement.innerHTML = `
      <h3>${cartItem.name}</h3>
      <p>Prezzo: $${(cartItem.price * cartItem.quantity).toFixed(2)}</p>
      <p>Quantità: ${cartItem.quantity}</p>
    `;
    return cartItemElement;
  }

  // Avvio dell'applicazione: ottenere il catalogo e il carrello iniziale
  getCatalog();
  getCart();
});

ùin questa applicazione frontend molto basic, gli utenti possono visualizzare il catalogo, aggiungere prodotti al carrello e visualizzare il contenuto del carrello utilizzando la funzione fetch per effettuare richieste HTTP verso l’API. I dati recuperati vengono visualizzati dinamicamente sulla pagina HTML.