encore-service

Par encoredev · skills

Structurer et organiser les services Encore.ts.

npx skills add https://github.com/encoredev/skills --skill encore-service

Structure du Service Encore

Instructions

Créer un Service

Chaque service Encore a besoin d'un fichier encore.service.ts :

// encore.service.ts
import { Service } from "encore.dev/service";

export default new Service("my-service");

Structure Minimale d'un Service

my-service/
├── encore.service.ts    # Définition du service (obligatoire)
├── api.ts               # Points de terminaison API
└── db.ts                # Base de données (si nécessaire)

Patterns d'Application

Service Unique (Recommandé au Démarrage)

Idéal pour les nouveaux projets - commencez simple, divisez plus tard si nécessaire :

my-app/
├── package.json
├── encore.app
├── encore.service.ts
├── api.ts
├── db.ts
└── migrations/
    └── 001_initial.up.sql

Multi-Service

Pour les systèmes distribués avec des limites de domaine claires :

my-app/
├── encore.app
├── package.json
├── user/
│   ├── encore.service.ts
│   ├── api.ts
│   └── db.ts
├── order/
│   ├── encore.service.ts
│   ├── api.ts
│   └── db.ts
└── notification/
    ├── encore.service.ts
    └── api.ts

Grande Application (Basée sur des Systèmes)

Groupez les services connexes dans des systèmes :

my-app/
├── encore.app
├── commerce/
│   ├── order/
│   │   └── encore.service.ts
│   ├── cart/
│   │   └── encore.service.ts
│   └── payment/
│       └── encore.service.ts
├── identity/
│   ├── user/
│   │   └── encore.service.ts
│   └── auth/
│       └── encore.service.ts
└── comms/
    ├── email/
    │   └── encore.service.ts
    └── push/
        └── encore.service.ts

Appels Entre Services

Importez les autres services depuis ~encore/clients :

import { user } from "~encore/clients";

export const getOrderWithUser = api(
  { method: "GET", path: "/orders/:id", expose: true },
  async ({ id }): Promise<OrderWithUser> => {
    const order = await getOrder(id);
    const orderUser = await user.get({ id: order.userId });
    return { ...order, user: orderUser };
  }
);

Quand Diviser les Services

Divisez quand vous avez :

Signal Action
Besoins de mise à l'échelle différents Diviser (ex. : auth vs analytics)
Cycles de déploiement différents Diviser
Limites de domaine claires Diviser
Tables de base de données partagées Garder ensemble
Logique étroitement couplée Garder ensemble
Juste pour organiser le code Utiliser des dossiers, pas des services

Service avec Middleware

import { Service } from "encore.dev/service";
import { middleware } from "encore.dev/api";

const loggingMiddleware = middleware(
  { target: { all: true } },
  async (req, next) => {
    console.log(`Request: ${req.requestMeta?.path}`);
    return next(req);
  }
);

export default new Service("my-service", {
  middlewares: [loggingMiddleware],
});

Ciblage du Middleware

Contrôlez les points de terminaison auxquels le middleware s'applique :

// S'appliquer à tous les points de terminaison
middleware({ target: { all: true } }, handler);

// S'appliquer uniquement aux points de terminaison authentifiés
middleware({ target: { auth: true } }, handler);

// S'appliquer uniquement aux points de terminaison exposés (publics)
middleware({ target: { expose: true } }, handler);

// S'appliquer uniquement aux points de terminaison bruts
middleware({ target: { isRaw: true } }, handler);

// S'appliquer uniquement aux points de terminaison de streaming
middleware({ target: { isStream: true } }, handler);

// S'appliquer aux points de terminaison avec des tags spécifiques
middleware({ target: { tags: ["admin", "internal"] } }, handler);

Objet de Requête du Middleware

L'objet de requête fournit accès à :

const myMiddleware = middleware(
  { target: { all: true } },
  async (req, next) => {
    // Pour les API typées et de streaming
    const meta = req.requestMeta;  // { method, path, pathParams }

    // Pour les points de terminaison bruts
    const rawReq = req.rawRequest;
    const rawRes = req.rawResponse;

    // Pour les points de terminaison de streaming
    const stream = req.stream;

    // Données personnalisées à passer aux gestionnaires
    req.data = { startTime: Date.now() };

    const resp = await next(req);

    // Modifier les en-têtes de réponse
    resp.header.set("X-Response-Time", `${Date.now() - req.data.startTime}ms`);

    return resp;
  }
);

Directives

  • Les services ne peuvent pas être imbriqués dans d'autres services
  • Commencez avec un service, divisez quand il y a une raison claire
  • Utilisez ~encore/clients pour les appels entre services (jamais d'importations directes)
  • Chaque service peut avoir sa propre base de données
  • Les noms de service doivent être en minuscules et descriptifs
  • Ne créez pas de services juste pour organiser le code - utilisez des dossiers à la place

Skills similaires