Architecte Swift Microservices
Dans un backend construit autrement, conserve sa structure et ses conventions sauf si l'utilisateur demande de les changer, et prends une forme de ce skill uniquement pour ce qui manque au backend.
X représente un service dans le nom d'une cible (LoansCore) et pour ce qui donne son nom à un type (BorrowBookUseCase, LoanService) ; <Org> et <org> pour l'organisation. Les exemples sont Bookshelf, une petite bibliothèque de prêt :
| Paquet | Contient |
|---|---|
bookshelf-identity | Comptes, mots de passe, sessions ; émet chaque jeton d'accès ; codes de réinitialisation dans Valkey ; membres pour les autres services, en interne |
bookshelf-catalog | Livres : tout le monde lit, les administrateurs modifient ; Loans consulte les livres en interne |
bookshelf-loans | Les prêts d'un membre, une table multi-locataire ; un workflow marque un prêt comme en retard et envoie un avis |
bookshelf-gateway | L'API HTTP publique, transmettant aux contrats gRPC des services |
bookshelf-core | BookshelfAuthentication, BookshelfPersistence, BookshelfTransport, BookshelfTesting |
bookshelf-protos | IdentityProtos, CatalogProtos, LoansProtos |
La forme
Clients ── HTTPS ──► edge (TLS se termine) ── HTTP ──► Gateway (Hummingbird, OpenAPI)
Gateway ── gRPC, mTLS, le JWT de l'appelant ──► XService de chaque service
Service, worker ── gRPC, mTLS ──► XInternalService d'un autre service
Service, worker ── mTLS ──► Temporal Service ──► sa propre base de données Postgres, Valkey
Chaque service et la passerelle dépendent de <org>-core, <org>-protos et des bibliothèques swift-microservices (stack).
Le paquet d'un service
<org>-x
├── XCore modèles métier, erreurs, protocoles de repository et commandes, ports, cas d'utilisation
├── XPostgres une portée par rôle de base de données, repositories, instructions, migrations
├── XGRPC XService et XInternalService, conversions Protobuf, clients gRPC pour les ports de Core
├── XTemporal workflows, conteneurs d'activités, le client Temporal pour le port de workflow de Core
├── XValkey repositories pour les valeurs éphémères
├── X<Provider> une API tierce derrière un port Core (LoansResend)
└── X exécutable : commandes, valeurs par défaut de configuration, migrations, racines de composition
Un service n'a que les cibles dont il a besoin, et chacune n'importe que ce que sa ligne liste. Règles strictes :
- XCore n'importe aucun pilote, transport, contrat, moteur de workflow, module de configuration ou de cycle de vie (ce que Core peut lier).
- Les cibles adaptateurs ne s'importent jamais mutuellement ; l'exécutable en remet une à une autre via les protocoles de Core.
- Les types de contrat générés ne quittent jamais XGRPC, la cible HTTP de la passerelle et les exécutables.
- Seul un exécutable lit la configuration, construit les clients et exécute le
ServiceGroup. - Aucun service ne lit la base de données d'un autre ; les données traversent les services uniquement via les contrats.
Qui possède quoi
| Préoccupation | Propriétaire |
|---|---|
| Règles métier, décisions d'autorisation, ce que signifie une erreur | Le cas d'utilisation, dans XCore ; les adaptateurs ne font que convertir et déléguer |
| Ce qu'un service sait des données d'un autre | Un modèle dans son Ports/ de XCore, à côté du port qui le retourne |
| Conversion Protobuf ↔ domaine, HTTP ↔ protobuf | Les conversions Protobuf/ de XGRPC ; les Schemas/ de la passerelle |
| Messages entre processus ; l'API publique | <org>-protos ; l'openapi.yaml de la passerelle |
| Qui est l'appelant | La passerelle vérifie et transmet le jeton ; chaque service le vérifie à nouveau |
| Quels processus peuvent appeler un RPC interne | mTLS à l'écouteur : chaque processus dont l'autorité de l'environnement signe |
| Isolation des locataires | Sécurité au niveau des lignes ; <Org>Persistence lie l'appelant à chaque transaction, et les cas d'utilisation de chaque audience s'exécutent sous leur rôle de base de données |
| Travail durable, différé ou multi-service | Un workflow Temporal dans XTemporal |
| Ce dont chaque service a besoin de manière identique | <org>-core : identité, liaison de locataire, lecteurs de transport |
Style de code
Le code se lit comme l'exigence qu'il implémente, avec le code d'exemple WWDC d'Apple comme référence (style). Les cas d'utilisation et les adaptateurs mappent les erreurs de cette façon :
do {
… // le travail, dans l'ordre énoncé par l'exigence
} catch let error as BorrowBookUseCaseError {
throw error
} catch CatalogClientError.unavailable {
logger.warning("Borrowing failed: the catalog is unavailable")
throw .catalogUnavailable
} catch {
logger.error("Borrowing failed: unknown error", metadata: ["error": "\(String(reflecting: error))"])
throw .unknown
}
- Un seul
docontient le travail jusqu'à sonreturn, et chaquecatchtermine son propre chemin : rien n'est déclaré avant ledopour qu'uncatchle remplisse. - Construis, puis appelle : une valeur qu'un appel prend est un
letnommé, jamais une conversion, untryouawaitdans les arguments (construis, puis appelle). - Pas de méthodes d'aide dans les cas d'utilisation, workflows ou activités, et pas de jonglage avec
Result, de drapeaux de direction, detry?, dedoimbriqué dans uncatch, ou de déballage forcé. - Mode Swift 6 avec
ExistentialAny,MemberImportVisibility,InternalImportsByDefaultetNonisolatedNonsendingByDefault(paramètres Swift) ; accèspackageentre les cibles d'un service. - Tout ce qui traverse une frontière est
Sendable; pas deTask {}ouTask.detached, et concurrence non vérifiée uniquement là où la concurrence le permet.
Pièges
- Un composant exclu du
ServiceGroupne s'exécute jamais : unGRPCClientouPostgresClientnon exécuté ne se connecte jamais, donc ses appels attendent jusqu'à leur délai. - Un processus connecté en tant que propriétaire de la base de données contourne la sécurité au niveau des lignes, donc chaque processus se connecte sous son rôle et seul
serve --migrate-databaseen tant que propriétaire. - Temporal vérifie
Codableuniquement à l'exécution : chaque payload de workflow et d'activité, et chaque type qu'il contient, estCodable, Sendable(workflows). - Le code de workflow est rejoué :
Date.now,UUID(), le hasard,Task.sleepou toute E/S dans un workflow le casse (déterminisme). - Un
import Foundationsimple ou limité dans du code propriétaire lie Foundation complète sur Linux ; utilise l'import conditionnelFoundationEssentials.
Listes de vérification
Copie la liste de vérification de la tâche dans la réponse et coche les éléments au fur et à mesure qu'ils sont terminés ; chaque fichier .md qu'elle nomme se trouve dans references/.
Planifier un backend
- [ ] Les capacités et les données que chacune possède ; un service par capacité (system.md#splitting-a-system-into-services)
- [ ] Nommer chaque service ; vérifier ses noms de cible par rapport à chaque module dans le graphe
- [ ] Par service : les RPC XService par audience, les RPC XInternalService et les appelants sans cycle d'appel, les tables (avec ou sans locataire), les workflows, l'état éphémère
- [ ] Par service : ses cibles, commandes et rôles de base de données
- [ ] Les produits de <org>-core, les contrats de <org>-protos, les routes de la passerelle par niveau
- [ ] Ordre de construction : <org>-core et <org>-protos, l'émetteur de jetons, chaque service avant ses appelants, la passerelle
- [ ] Écrire le plan à partir de system.md#the-plan ; le vérifier par rapport aux règles strictes avant tout code
Ajouter un service
- [ ] <org>-protos : XProtos avec XService, et XInternalService si des processus appellent en interne
- [ ] Paquet <org>-x à partir de packages.md#packageswift-template, avec uniquement les cibles que le plan nomme
- [ ] XCore : modèles, erreurs, repositories, ports, un cas d'utilisation par RPC (use-cases.md)
- [ ] XPostgres : rôles, tables, sécurité au niveau des lignes, instructions, une portée par rôle (persistence.md)
- [ ] XGRPC : services proto sur les cas d'utilisation, conversions, clients pour les ports (grpc.md)
- [ ] XTemporal, XValkey, X<Provider> selon les besoins du plan
- [ ] X : commandes, valeurs par défaut de InMemoryProvider, Migrations, Serve, et Run avec un worker (composition.md)
- [ ] Passerelle : routes pour les RPC publics (gateway.md)
- [ ] Une cible de test par module, basée sur des mocks (testing.md)
- [ ] Ses workflows CI, Containerfile et Dependabot en tant que service (delivery.md)
- [ ] swift build et swift test ; corriger et répéter jusqu'à ce que les deux passent sans avertissements
Ajouter une opération
- [ ] Contrat : le RPC sous son audience dans XService, ou dans XInternalService
- [ ] XCore : les cinq fichiers du cas d'utilisation ; sa portée ne liste que ce qu'il touche (use-cases.md)
- [ ] XPostgres : les portées des rôles sous lesquels il peut s'exécuter adoptent son protocole de portée
- [ ] XGRPC : le gestionnaire et la conversion de l'entrée, chaque cas d'erreur mappé
- [ ] Exécutable : le cas d'utilisation sur la base de données de son rôle, passé au service
- [ ] Passerelle : la route dans son niveau, si public
- [ ] Tests : les résultats du cas d'utilisation et les statuts du gestionnaire, basés sur des mocks
- [ ] swift build et swift test ; corriger et répéter jusqu'à ce que les deux passent sans avertissements
Ajouter un processus durable
- [ ] XCore : le port XWorkflowClient ; les cas d'utilisation que ses activités exécutent, adoptés par la portée du worker
- [ ] XTemporal : le workflow, ses activités, TemporalXWorkflowClient, ActivityOptions nommées (workflows.md)
- [ ] Le cas d'utilisation qui le démarre, après la validation de sa transaction, par un identifiant déterministe
- [ ] Exécutable : le client Temporal dans Serve ; le groupe de workers et sa racine Run
- [ ] Tests : le workflow sur le serveur à saut de temps, ses activités basées sur des mocks
- [ ] swift build et swift test ; corriger et répéter jusqu'à ce que les deux passent sans avertissements
Configurer l'intégration continue
- [ ] Le type du paquet : bibliothèque publique, bibliothèque système ou service (delivery.md#package-kinds)
- [ ] Ses workflows CI, appelant swift-microservices/ci à un commit d'une version ; dependabot.yml ; .license_header_template et .licenseignore ; les secrets
- [ ] Une bibliothèque : les labels semver et .github/release.yml ; un service : son Containerfile
- [ ] swift build et swift test avec les drapeaux de CI ; corriger et répéter jusqu'à ce que les deux passent
Réviser un backend
- [ ] Exécuter les vérifications de frontière (packages.md#checking-the-boundaries)
- [ ] Chaque cas d'utilisation : ses cinq fichiers, `typed throws`, un `catch` par cas, pas d'aides, le bon rôle de base de données
- [ ] Chaque adaptateur ne fait que convertir et déléguer ; chaque cas d'erreur est mappé
- [ ] Chaque racine : configuration lue uniquement là, chaque composant dans le ServiceGroup dans l'ordre des dépendances
- [ ] Paramètres Swift, imports FoundationEssentials, accès package, conventions de nommage (conventions.md)
- [ ] Résultats classés par ordre de priorité : bloquants (isolation des locataires, autorisation, identifiants, la construction), majeurs (les formes), mineurs (nommage, disposition, journalisation), chacun citant un fichier et une ligne ou un `grep` vide, avec la règle et la correction ; puis les décisions de l'utilisateur. Modifier le code uniquement si demandé
- [ ] swift build et swift test ; rapporter chaque résultat, ou pourquoi cela n'a pas pu s'exécuter
Références
Lis la référence pour la partie concernée :
| Référence | Couvre |
|---|---|
| system.md | Découpage en services, contrats, comment les services communiquent, les paquets partagés, le plan |
| packages.md | Cibles et leurs imports, Package.swift, paramètres Swift, traits et Foundation, dispositions, vérifications de frontière |
| stack.md | Les bibliothèques, quelle cible lie chacune, liaison de Foundation, choix des versions |
| use-cases.md | Modèles, erreurs, repositories, ports, les cinq fichiers et le corps d'un cas d'utilisation, transactions, autorisation |
| persistence.md | Portées par rôle de base de données, sécurité au niveau des lignes, instructions, migrations, Valkey |
| grpc.md | Contrats, services proto, gestionnaires, conversions, pagination, codes de statut, clients, transports |
| gateway.md | Types OpenAPI, contextes de requête et niveaux, contrôleurs, webhooks, erreurs, clients en amont |
| workflows.md | Démarrage des workflows, payloads, déterminisme, activités et options, versions, le worker |
| composition.md | Commandes, racines de composition, configuration, journalisation, le ServiceGroup |
| security.md | Le modèle de confiance, mTLS, jetons utilisateur, couches d'autorisation, secrets, webhooks |
| style.md | Comment le code se lit : erreurs, concurrence, imports, Foundation, types, formatage |
| testing.md | Cibles de test, ce que chacune prouve, mocks |
| delivery.md | Workflows CI par type de paquet, versions, déploiement, le Containerfile, Dependabot |
| conventions.md | Nommer un paquet, une cible, un type, un fichier, une clé ou un dossier |
Skills associés
Pour le code à l'intérieur des formes, charge ces skills s'ils sont installés ; là où ce skill énonce une forme, suis-la :
- Acteurs, groupes de tâches, annulation, diagnostics
Sendable, migration Swift 6 :swift-concurrency - Étiquettes d'arguments, noms de méthodes, commentaires de documentation :
swift-api-design-guidelines - Tests de cas d'utilisation, de gestionnaires et de workflows :
swift-testing-expert