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