Swift Microservices Architect
En un backend construido de otra manera, mantén su estructura y convenciones a menos que el usuario pida cambiarlas, y toma la forma de esta habilidad solo para lo que al backend le falte.
X representa un servicio en el nombre de un target (LoansCore) y para lo que se nombra un tipo (BorrowBookUseCase, LoanService); <Org> y <org> para la organización. Los ejemplos son Bookshelf, una pequeña biblioteca de préstamo:
| Paquete | Contiene |
|---|---|
bookshelf-identity | Cuentas, contraseñas, sesiones; emite cada token de acceso; códigos de restablecimiento en Valkey; miembros para otros servicios, internamente |
bookshelf-catalog | Libros: cualquiera lee, los administradores editan; Loans busca libros internamente |
bookshelf-loans | Préstamos de un miembro, una tabla de inquilino; un flujo de trabajo marca un préstamo como vencido y envía un aviso |
bookshelf-gateway | La API HTTP pública, reenviando a los contratos gRPC de los servicios |
bookshelf-core | BookshelfAuthentication, BookshelfPersistence, BookshelfTransport, BookshelfTesting |
bookshelf-protos | IdentityProtos, CatalogProtos, LoansProtos |
La forma
Clientes ── HTTPS ──► borde (TLS termina) ── HTTP ──► Gateway (Hummingbird, OpenAPI)
Gateway ── gRPC, mTLS, el JWT del llamante ──► cada XService del servicio
Servicio, worker ── gRPC, mTLS ──► XInternalService de otro servicio
Servicio, worker ── mTLS ──► Temporal Servicio ──► su propia base de datos Postgres, Valkey
Cada servicio y el gateway dependen de <org>-core, <org>-protos y las librerías swift-microservices (stack).
El paquete de un servicio
<org>-x
├── XCore modelos de dominio, errores, protocolos de repositorio y comandos, puertos, casos de uso
├── XPostgres un ámbito por rol de base de datos, repositorios, sentencias, migraciones
├── XGRPC XService y XInternalService, conversiones Protobuf, clientes gRPC para los puertos de Core
├── XTemporal flujos de trabajo, contenedores de actividades, el cliente Temporal para el puerto de flujo de trabajo de Core
├── XValkey repositorios para valores de corta duración
├── X<Proveedor> una API de terceros detrás de un puerto de Core (LoansResend)
└── X ejecutable: comandos, valores predeterminados de configuración, migraciones, raíces de composición
Un servicio solo tiene los targets que necesita, y cada uno importa solo lo que su fila enumera. Reglas estrictas:
- XCore no importa ningún módulo de driver, transporte, contrato, motor de flujo de trabajo, configuración o ciclo de vida (lo que Core puede enlazar).
- Los targets adaptadores nunca se importan entre sí; el ejecutable pasa uno a otro a través de los protocolos de Core.
- Los tipos de contrato generados nunca salen de XGRPC, el target HTTP del gateway y los ejecutables.
- Solo un ejecutable lee configuración, construye clientes y ejecuta el
ServiceGroup. - Ningún servicio lee la base de datos de otro; los datos cruzan servicios solo a través de contratos.
Quién es dueño de qué
| Preocupación | Dueño |
|---|---|
| Reglas de negocio, decisiones de autorización, qué significa un error | El caso de uso, en XCore; los adaptadores solo convierten y delegan |
| Qué sabe un servicio sobre los datos de otro | Un modelo en su XCore Ports/, junto al puerto que lo devuelve |
| Protobuf ↔ dominio, HTTP ↔ protobuf | Las conversiones Protobuf/ de XGRPC; los Schemas/ del gateway |
| Mensajes entre procesos; la API pública | <org>-protos; el openapi.yaml del gateway |
| Quién es el llamante | El gateway verifica y reenvía el token; cada servicio lo verifica de nuevo |
| Qué procesos pueden llamar a un RPC interno | mTLS en el listener: cada proceso que la autoridad del entorno firma |
| Aislamiento de inquilinos | Seguridad a nivel de fila; <Org>Persistence vincula al llamante a cada transacción, y los casos de uso de cada audiencia se ejecutan como su rol de base de datos |
| Trabajo durable, retrasado o de múltiples servicios | Un flujo de trabajo Temporal en XTemporal |
| Lo que cada servicio necesita de manera idéntica | <org>-core: identidad, vinculación de inquilinos, lectores de transporte |
Estilo de código
El código se lee como el requisito que implementa, con el código de muestra de WWDC de Apple como referencia (estilo). Los casos de uso y adaptadores mapean errores de esta manera:
do {
… // el trabajo, en el orden que el requisito lo establece
} 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
docontiene el trabajo hasta sureturn, y cadacatchtermina su propio camino: nada se declara antes deldopara que uncatchlo llene. - Construye, luego llama: un valor que una llamada toma es un
letcon nombre, nunca una conversión,tryoawaiten los argumentos (construye, luego llama). - Sin métodos auxiliares en casos de uso, flujos de trabajo o actividades, y sin manipulación de
Result, banderas de dirección,try?,doanidado en uncatch, o force unwraps. - Modo Swift 6 con
ExistentialAny,MemberImportVisibility,InternalImportsByDefaultyNonisolatedNonsendingByDefault(configuraciones Swift); accesopackagea través de los targets de un servicio. - Todo lo que cruza un límite es
Sendable; sinTask {}oTask.detached, y concurrencia no verificada solo donde la concurrencia lo permite.
Errores comunes
- Un componente omitido del
ServiceGroupnunca se ejecuta: unGRPCClientoPostgresClientno ejecutado nunca se conecta, por lo que sus llamadas esperan hasta su plazo. - Un proceso conectado como el propietario de la base de datos evita la seguridad a nivel de fila, por lo que cada proceso se conecta como sus roles y solo
serve --migrate-databasecomo el propietario. - Temporal verifica
Codablesolo en tiempo de ejecución: cada carga útil de flujo de trabajo y actividad, y cada tipo que contiene, esCodable, Sendable(flujos de trabajo). - El código del flujo de trabajo se reproduce:
Date.now,UUID(), aleatoriedad,Task.sleepo cualquier E/S en un flujo de trabajo lo rompe (determinismo). - Un
import Foundationsimple o con ámbito en código propio enlaza Foundation completo en Linux; usa la importación condicionalFoundationEssentials.
Listas de verificación
Copia la lista de verificación de la tarea en la respuesta y marca los elementos a medida que se completan; cada archivo .md que nombra está en references/.
Planificar un backend
- [ ] Las capacidades y los datos que cada uno posee; un servicio por capacidad (system.md#splitting-a-system-into-services)
- [ ] Nombrar cada servicio; verificar sus nombres de target contra cada módulo en el grafo
- [ ] Por servicio: XService RPCs por audiencia, XInternalService RPCs y llamantes sin ciclo de llamadas, tablas (inquilino o no), flujos de trabajo, estado de corta duración
- [ ] Por servicio: sus targets, comandos y roles de base de datos
- [ ] Productos de <org>-core, contratos de <org>-protos, rutas del gateway por nivel
- [ ] Orden de construcción: <org>-core y <org>-protos, el emisor de tokens, cada servicio antes de sus llamantes, el gateway
- [ ] Escribir el plan desde system.md#the-plan; verificarlo contra las reglas estrictas antes de cualquier código
Agregar un servicio
- [ ] <org>-protos: XProtos con XService, y XInternalService si los procesos llaman internamente
- [ ] Paquete <org>-x desde packages.md#packageswift-template, con solo los targets que el plan nombra
- [ ] XCore: modelos, errores, repositorios, puertos, un caso de uso por RPC (use-cases.md)
- [ ] XPostgres: roles, tablas, seguridad a nivel de fila, sentencias, un ámbito por rol (persistence.md)
- [ ] XGRPC: servicios proto sobre los casos de uso, conversiones, clientes para puertos (grpc.md)
- [ ] XTemporal, XValkey, X<Proveedor> según lo que el plan necesite
- [ ] X: comandos, valores predeterminados de InMemoryProvider, Migrations, Serve y Run con un worker (composition.md)
- [ ] Gateway: rutas para los RPC públicos (gateway.md)
- [ ] Un target de prueba por módulo, sobre mocks (testing.md)
- [ ] Sus flujos de trabajo CI, Containerfile y Dependabot como servicio (delivery.md)
- [ ] swift build y swift test; corregir y repetir hasta que ambos pasen sin advertencias
Agregar una operación
- [ ] Contrato: el RPC bajo su audiencia en XService, o en XInternalService
- [ ] XCore: los cinco archivos del caso de uso; su ámbito enumera solo lo que toca (use-cases.md)
- [ ] XPostgres: los ámbitos de los roles en los que puede ejecutarse adoptan su protocolo de ámbito
- [ ] XGRPC: el manejador y la conversión de entrada, cada caso de error mapeado
- [ ] Ejecutable: el caso de uso en la base de datos de su rol, pasado al servicio
- [ ] Gateway: la ruta en su nivel, si es público
- [ ] Pruebas: los resultados del caso de uso y los estados del manejador, sobre mocks
- [ ] swift build y swift test; corregir y repetir hasta que ambos pasen sin advertencias
Agregar un proceso durable
- [ ] XCore: el puerto XWorkflowClient; los casos de uso que ejecutan sus actividades, adoptados por el ámbito del worker
- [ ] XTemporal: el flujo de trabajo, sus actividades, TemporalXWorkflowClient, ActivityOptions nombradas (workflows.md)
- [ ] El caso de uso que lo inicia, después de que su transacción se confirma, por un id determinista
- [ ] Ejecutable: el cliente Temporal en Serve; el grupo de workers y su raíz Run
- [ ] Pruebas: el flujo de trabajo en el servidor de salto de tiempo, sus actividades sobre mocks
- [ ] swift build y swift test; corregir y repetir hasta que ambos pasen sin advertencias
Configurar CI/CD
- [ ] El tipo del paquete: librería pública, librería de sistema o servicio (delivery.md#package-kinds)
- [ ] Sus flujos de trabajo CI, llamando a swift-microservices/ci en un commit de una release; dependabot.yml; .license_header_template y .licenseignore; los secretos
- [ ] Una librería: las etiquetas semver y .github/release.yml; un servicio: su Containerfile
- [ ] swift build y swift test con las banderas de CI; corregir y repetir hasta que ambos pasen
Revisar un backend
- [ ] Ejecutar las verificaciones de límites (packages.md#checking-the-boundaries)
- [ ] Cada caso de uso: sus cinco archivos, typethrows, un catch por caso, sin ayudantes, el rol de base de datos correcto
- [ ] Cada adaptador solo convierte y delega; cada caso de error está mapeado
- [ ] Cada raíz: configuración leída solo allí, cada componente en el ServiceGroup en orden de dependencia
- [ ] Configuraciones Swift, importaciones FoundationEssentials, acceso package, nomenclatura (conventions.md)
- [ ] Hallazgos clasificados como bloqueantes (aislamiento de inquilinos, autorización, credenciales, la compilación), mayores (las formas), menores (nomenclatura, diseño, registro), cada uno citando un archivo y línea o un grep vacío, con la regla y la corrección; luego las decisiones del usuario. Cambiar código solo cuando se pida
- [ ] swift build y swift test; informar cada resultado, o por qué no pudo ejecutarse
Referencias
Lee la referencia para la parte en cuestión:
| Referencia | Cubre |
|---|---|
| system.md | División en servicios, contratos, cómo se comunican los servicios, los paquetes compartidos, el plan |
| packages.md | Targets y sus importaciones, Package.swift, configuraciones Swift, traits y Foundation, diseños, verificaciones de límites |
| stack.md | Las librerías, qué target enlaza cada una, enlace de Foundation, elección de versiones |
| use-cases.md | Modelos, errores, repositorios, puertos, los cinco archivos y cuerpo de un caso de uso, transacciones, autorización |
| persistence.md | Ámbitos por rol de base de datos, seguridad a nivel de fila, sentencias, migraciones, Valkey |
| grpc.md | Contratos, servicios proto, manejadores, conversiones, paginación, códigos de estado, clientes, transportes |
| gateway.md | Tipos OpenAPI, contextos de solicitud y niveles, controladores, webhooks, errores, clientes ascendentes |
| workflows.md | Inicio de flujos de trabajo, cargas útiles, determinismo, actividades y opciones, versiones, el worker |
| composition.md | Comandos, raíces de composición, configuración, registro, el ServiceGroup |
| security.md | El modelo de confianza, mTLS, tokens de usuario, capas de autorización, secretos, webhooks |
| style.md | Cómo se lee el código: errores, concurrencia, importaciones, Foundation, tipos, formato |
| testing.md | Targets de prueba, qué prueba cada uno, mocks |
| delivery.md | Flujos de trabajo CI por tipo de paquete, releases, despliegue, el Containerfile, Dependabot |
| conventions.md | Nomenclatura de un paquete, target, tipo, archivo, clave o carpeta |
Habilidades relacionadas
Para el código dentro de las formas, carga estas cuando estén instaladas; donde esta habilidad establece una forma, síguela:
- Actores, grupos de tareas, cancelación, diagnósticos
Sendable, migración a Swift 6:swift-concurrency - Etiquetas de argumento, nombres de método, comentarios de documentación:
swift-api-design-guidelines - Pruebas de casos de uso, manejadores y flujos de trabajo:
swift-testing-expert