'O Primo Piatto
“‘O primmo piatto nun è ‘a fine — è ‘o principio d’o pranzo.” (The first course is not the end — it is the beginning of the meal.)
Your kitchen is configured and your first node is running. Now it’s time to serve your first dish. We’re going to build a simple publish/subscribe system using GarlicBreadcast — Pasta Protocol’s distributed event bus. By the end of this guide you will have two logical services communicating asynchronously across the kitchen: an order producer that publishes new orders, and an order consumer that processes them.
La Struttura del Progetto
We’ll build a minimal but complete example. Create the following files alongside your kitchen.ricetta:
my-kitchen/├── kitchen.ricetta├── kitchen.ts # KitchenManager bootstrap├── producer.ts # Order publisher└── consumer.ts # Order subscriberIl File di Configurazione
Update your kitchen.ricetta to include the ordini topic we’ll be using:
kitchen: name: "Trattoria da Giovanni" region: "napoli-1" log_level: VOCE
nodes: - id: "cucina" host: "localhost" port: 3000 role: primary
garlicbreadcast: topics: - ordini - conferme
healthcheck: endpoint: /sono-vivo interval_cotture: 5 threshold: 3Il Bootstrap della Cucina
import { KitchenManager } from '@pasta-protocol/core';
export async function bootstrapKitchen() { const kitchen = await KitchenManager.fromRicetta('./kitchen.ricetta'); await kitchen.addNode('cucina');
process.on('SIGTERM', () => kitchen.shutdown('SIGTERM received')); process.on('SIGINT', () => kitchen.shutdown('SIGINT received'));
return kitchen;}Il Produttore
The producer publishes an Ordine event every time a customer places an order. Notice that we define the event shape as a TypeScript interface — GarlicBreadcast is fully generic and preserves your types end to end.
import type { KitchenManager } from '@pasta-protocol/core';
interface Ordine { id: string; piatto: string; tavolo: number; timestamp: number;}
export function createProducer(kitchen: KitchenManager) { const bus = kitchen.garlicBreadcast;
return { async pubblicaOrdine(piatto: string, tavolo: number): Promise<void> { const ordine: Ordine = { id: crypto.randomUUID(), piatto, tavolo, timestamp: Date.now(), };
await bus.publish('ordini', ordine);
kitchen.logger.voce('Ordine pubblicato', { ordineId: ordine.id, piatto, tavolo }); }, };}The highlighted lines (3–5) show the Ordine interface that forms the contract between producer and consumer. In a real application you’d extract this into a shared types.ts file so both sides are guaranteed to agree on the shape.
Il Consumatore
The consumer subscribes to the ordini topic and processes each incoming event. Subscriptions are persistent: if the node temporarily disconnects and reconnects, GarlicBreadcast replays any missed messages (subject to the configured retention window).
import type { KitchenManager } from '@pasta-protocol/core';
interface Ordine { id: string; piatto: string; tavolo: number; timestamp: number;}
async function elaboraOrdine(ordine: Ordine): Promise<void> { kitchen.logger.voce('Elaborazione ordine', { ordineId: ordine.id }); // Simulate kitchen preparation time await new Promise((resolve) => setTimeout(resolve, 500)); kitchen.logger.voce('Ordine pronto', { ordineId: ordine.id, piatto: ordine.piatto });}
export function createConsumer(kitchen: KitchenManager) { const bus = kitchen.garlicBreadcast;
bus.subscribe<Ordine>('ordini', async (ordine) => { await elaboraOrdine(ordine);
await bus.publish('conferme', { ordineId: ordine.id, statoOrdine: 'PRONTO', tavolo: ordine.tavolo, }); });
kitchen.logger.voce('Consumer attivo sul topic "ordini"');}The highlighted lines (8–12) show elaboraOrdine — the actual business logic handler. Keeping the handler function separate from the subscription setup means you can unit test it without needing a real GarlicBreadcast instance.
Mettere Tutto Insieme
Finally, wire the bootstrap, producer, and consumer into your application entry point:
import { bootstrapKitchen } from './kitchen.js';import { createProducer } from './producer.js';import { createConsumer } from './consumer.js';
const kitchen = await bootstrapKitchen();
createConsumer(kitchen);const producer = createProducer(kitchen);
// Simulate three incoming ordersawait producer.pubblicaOrdine('Spaghetti alle Vongole', 5);await producer.pubblicaOrdine('Rigatoni all\'Amatriciana', 3);await producer.pubblicaOrdine('Penne al Pesto', 7);
await kitchen.startCooking();Run it:
npx ts-node main.tsYou should see interleaved VOCE log entries as the producer publishes and the consumer processes each order. The conferme topic will receive a confirmation message for each successfully elaborated order.
{"level":"VOCE","msg":"Consumer attivo sul topic \"ordini\""}{"level":"VOCE","msg":"Ordine pubblicato","ordineId":"a1b2c3","piatto":"Spaghetti alle Vongole","tavolo":5}{"level":"VOCE","msg":"Elaborazione ordine","ordineId":"a1b2c3"}{"level":"VOCE","msg":"Ordine pubblicato","ordineId":"d4e5f6","piatto":"Rigatoni all'Amatriciana","tavolo":3}{"level":"VOCE","msg":"Ordine pronto","ordineId":"a1b2c3","piatto":"Spaghetti alle Vongole"}Prossimi Passi
Your first piatto is served. The kitchen is running, nodes are communicating, and events are flowing. From here you can:
- Explore the Architecture — understand how
KitchenManager,GarlicBreadcast, andTermometrocollaborate in La Cucina. - Learn the full API — browse all available classes and methods in ‘O Menu.
- Apply DOC patterns — learn the certified architectural patterns that guarantee your system stays al dente under production load in the Ricettario.
Buon appetito — e buona fortuna con ‘a vostra prima cucina distribuita.