Arquiteto de Microsserviços Swift
Em um backend construído de outra forma, mantenha sua estrutura e convenções a menos que o usuário peça para alterá-las, e adote a forma deste skill apenas para o que o backend não possui.
X representa um serviço no nome de um target (LoansCore) e para o que um tipo é nomeado (BorrowBookUseCase, LoanService); <Org> e <org> para a organização. Os exemplos são Bookshelf, uma pequena biblioteca de empréstimos:
| Pacote | Contém |
|---|---|
bookshelf-identity | Contas, senhas, sessões; emite todo token de acesso; códigos de redefinição no Valkey; membros para outros serviços, internamente |
bookshelf-catalog | Livros: qualquer um lê, administradores editam; Loans consulta livros internamente |
bookshelf-loans | Empréstimos de um membro, uma tabela tenant; um workflow marca um empréstimo como atrasado e envia um aviso |
bookshelf-gateway | A API HTTP pública, encaminhando para os contratos gRPC dos serviços |
bookshelf-core | BookshelfAuthentication, BookshelfPersistence, BookshelfTransport, BookshelfTesting |
bookshelf-protos | IdentityProtos, CatalogProtos, LoansProtos |
A forma
Clientes ── HTTPS ──► borda (TLS termina) ── HTTP ──► Gateway (Hummingbird, OpenAPI)
Gateway ── gRPC, mTLS, o JWT do chamador ──► XService de cada serviço
Serviço, worker ── gRPC, mTLS ──► XInternalService de outro serviço
Serviço, worker ── mTLS ──► Temporal Serviço ──► seu próprio banco de dados Postgres, Valkey
Todo serviço e o gateway dependem de <org>-core, <org>-protos e das bibliotecas swift-microservices (stack).
Pacote de um serviço
<org>-x
├── XCore modelos de domínio, erros, protocolos de repositório e comandos, portas, casos de uso
├── XPostgres um escopo por função de banco de dados, repositórios, statements, migrações
├── XGRPC XService e XInternalService, conversões Protobuf, clients gRPC para as portas do Core
├── XTemporal workflows, contêineres de atividades, o client Temporal para a porta de workflow do Core
├── XValkey repositórios para valores de curta duração
├── X<Provider> uma API de terceiros por trás de uma porta do Core (LoansResend)
└── X executável: comandos, padrões de configuração, migrações, raízes de composição
Um serviço tem apenas os targets que precisa, e cada um importa apenas o que sua linha lista. Regras rígidas:
- XCore não importa driver, transporte, contrato, mecanismo de workflow, módulo de configuração ou ciclo de vida (o que Core pode linkar).
- Targets adaptadores nunca importam uns aos outros; o executável entrega um para o outro através dos protocolos do Core.
- Tipos de contrato gerados nunca saem de XGRPC, do target HTTP do gateway e de executáveis.
- Apenas um executável lê configuração, constrói clients e executa o
ServiceGroup. - Nenhum serviço lê o banco de dados de outro; dados cruzam serviços apenas através de contratos.
Quem é dono do quê
| Preocupação | Dono |
|---|---|
| Regras de negócio, decisões de autorização, o que um erro significa | O caso de uso, no XCore; adaptadores apenas convertem e delegam |
| O que um serviço sabe sobre os dados de outro | Um modelo no Ports/ do seu XCore, ao lado da porta que o retorna |
| Protobuf ↔ domínio, HTTP ↔ protobuf | Conversões Protobuf/ do XGRPC; Schemas/ do gateway |
| Mensagens entre processos; a API pública | <org>-protos; openapi.yaml do gateway |
| Quem é o chamador | O gateway verifica e encaminha o token; cada serviço o verifica novamente |
| Quais processos podem chamar um RPC interno | mTLS no listener: todo processo que a autoridade do ambiente assina |
| Isolamento de tenant | Segurança em nível de linha; <Org>Persistence vincula o chamador a cada transação, e os casos de uso de cada audiência executam como sua função de banco de dados |
| Trabalho durável, atrasado ou multi-serviço | Um workflow Temporal no XTemporal |
| O que todo serviço precisa de forma idêntica | <org>-core: identidade, vinculação de tenant, leitores de transporte |
Estilo de código
O código lê como o requisito que implementa, com o código de exemplo da WWDC da Apple como referência (style). Casos de uso e adaptadores mapeiam erros desta forma:
do {
... // o trabalho, na ordem que o requisito declara
} catch let error as BorrowBookUseCaseError {
throw error
} catch CatalogClientError.unavailable {
logger.warning("Falha no empréstimo: catálogo indisponível")
throw .catalogUnavailable
} catch {
logger.error("Falha no empréstimo: erro desconhecido", metadata: ["error": "\(String(reflecting: error))"])
throw .unknown
}
- Um
docontém o trabalho até seureturn, e cadacatchtermina seu próprio caminho: nada é declarado antes dodopara umcatchpreencher. - Construa, então chame: um valor que uma chamada recebe é um
letnomeado, nunca uma conversão,tryouawaitnos argumentos (construa, então chame). - Sem métodos auxiliares em casos de uso, workflows ou atividades, e sem manipulação de
Result, flags de direção,try?,doaninhado em umcatch, ou force unwraps. - Modo Swift 6 com
ExistentialAny,MemberImportVisibility,InternalImportsByDefaulteNonisolatedNonsendingByDefault(configurações Swift); acessopackageentre targets de um serviço. - Tudo que cruza uma fronteira é
Sendable; semTask {}ouTask.detached, e concorrência não verificada apenas onde concorrência permite.
Pegadinhas
- Um componente deixado de fora do
ServiceGroupnunca executa: umGRPCClientouPostgresClientnão executado nunca conecta, então suas chamadas esperam até o deadline. - Um processo conectado como proprietário do banco de dados ignora a segurança em nível de linha, então todo processo conecta como suas funções e apenas
serve --migrate-databasecomo proprietário. - Temporal verifica
Codableapenas em tempo de execução: toda carga útil de workflow e atividade, e cada tipo que ela contém, éCodable, Sendable(workflows). - O código do workflow é reproduzido:
Date.now,UUID(), aleatoriedade,Task.sleepou qualquer I/O em um workflow quebra a determinismo. - Um
import Foundationsimples ou com escopo no código próprio vincula Foundation completo no Linux; use o import condicionalFoundationEssentials.
Checklists
Copie a checklist da tarefa na resposta e marque os itens conforme forem concluídos; cada arquivo .md nomeado está em references/.
Planejar um backend
- [ ] As capacidades e os dados que cada uma possui; um serviço por capacidade (system.md#splitting-a-system-into-services)
- [ ] Nomear cada serviço; verificar seus nomes de target contra cada módulo no grafo
- [ ] Por serviço: RPCs XService por audiência, RPCs XInternalService e chamadores sem ciclo de chamada, tabelas (tenant ou não), workflows, estado de curta duração
- [ ] Por serviço: seus targets, comandos e funções de banco de dados
- [ ] Produtos de <org>-core, contratos de <org>-protos, rotas do gateway por camada
- [ ] Ordem de construção: <org>-core e <org>-protos, o emissor de token, cada serviço antes de seus chamadores, o gateway
- [ ] Escrever o plano a partir de system.md#the-plan; verificar contra as regras rígidas antes de qualquer código
Adicionar um serviço
- [ ] <org>-protos: XProtos com XService, e XInternalService se processos chamarem
- [ ] Pacote <org>-x a partir de packages.md#packageswift-template, com apenas os targets que o plano nomeia
- [ ] XCore: modelos, erros, repositórios, portas, um caso de uso por RPC (use-cases.md)
- [ ] XPostgres: funções, tabelas, segurança em nível de linha, statements, um escopo por função (persistence.md)
- [ ] XGRPC: serviços proto sobre os casos de uso, conversões, clients para portas (grpc.md)
- [ ] XTemporal, XValkey, X<Provider> conforme o plano precisar
- [ ] X: comandos, padrões InMemoryProvider, Migrações, Serve e Run com um worker (composition.md)
- [ ] Gateway: rotas para os RPCs públicos (gateway.md)
- [ ] Um target de teste por módulo, sobre mocks (testing.md)
- [ ] Seus workflows CI, Containerfile e Dependabot como um serviço (delivery.md)
- [ ] swift build e swift test; corrigir e repetir até ambos passarem sem avisos
Adicionar uma operação
- [ ] Contrato: o RPC sob sua audiência em XService, ou em XInternalService
- [ ] XCore: os cinco arquivos do caso de uso; seu escopo lista apenas o que toca (use-cases.md)
- [ ] XPostgres: os escopos das funções sob as quais pode executar adotam seu protocolo de escopo
- [ ] XGRPC: o handler e a conversão da entrada, cada caso de erro mapeado
- [ ] Executável: o caso de uso no banco de dados de sua função, passado ao serviço
- [ ] Gateway: a rota em sua camada, se pública
- [ ] Testes: os resultados do caso de uso e os status do handler, sobre mocks
- [ ] swift build e swift test; corrigir e repetir até ambos passarem sem avisos
Adicionar um processo durável
- [ ] XCore: a porta XWorkflowClient; os casos de uso que suas atividades executam, adotados pelo escopo do worker
- [ ] XTemporal: o workflow, suas atividades, TemporalXWorkflowClient, ActivityOptions nomeadas (workflows.md)
- [ ] O caso de uso que o inicia, após sua transação ser confirmada, por um id determinístico
- [ ] Executável: o client Temporal no Serve; o grupo de workers e sua raiz Run
- [ ] Testes: o workflow no servidor de salto de tempo, suas atividades sobre mocks
- [ ] swift build e swift test; corrigir e repetir até ambos passarem sem avisos
Configurar CI/CD
- [ ] O tipo do pacote: biblioteca pública, biblioteca de sistema ou serviço (delivery.md#package-kinds)
- [ ] Seus workflows CI, chamando swift-microservices/ci no commit de um release; dependabot.yml; .license_header_template e .licenseignore; os segredos
- [ ] Uma biblioteca: os labels semver e .github/release.yml; um serviço: seu Containerfile
- [ ] swift build e swift test com as flags do CI; corrigir e repetir até ambos passarem
Revisar um backend
- [ ] Executar as verificações de fronteira (packages.md#checking-the-boundaries)
- [ ] Cada caso de uso: seus cinco arquivos, typed throws, um catch por caso, sem helpers, a função de banco de dados correta
- [ ] Cada adaptador apenas converte e delega; todo caso de erro é mapeado
- [ ] Cada raiz: configuração lida apenas lá, todo componente no ServiceGroup em ordem de dependência
- [ ] Configurações Swift, imports FoundationEssentials, acesso package, nomenclatura (conventions.md)
- [ ] Achados classificados como bloqueantes (isolamento de tenant, autorização, credenciais, construção), principais (as formas), menores (nomenclatura, layout, logging), cada um citando um arquivo e linha ou um grep vazio, com a regra e a correção; então as decisões do usuário. Alterar código apenas quando solicitado
- [ ] swift build e swift test; relatar cada resultado, ou por que não pôde executar
Referências
Leia a referência para a parte em questão:
| Referência | Abrange |
|---|---|
| system.md | Divisão em serviços, contratos, como serviços se comunicam, pacotes compartilhados, o plano |
| packages.md | Targets e seus imports, Package.swift, configurações Swift, traits e Foundation, layouts, verificações de fronteira |
| stack.md | As bibliotecas, qual target linka cada, linkagem de Foundation, escolha de versões |
| use-cases.md | Modelos, erros, repositórios, portas, os cinco arquivos de um caso de uso e corpo, transações, autorização |
| persistence.md | Escopos por função de banco de dados, segurança em nível de linha, statements, migrações, Valkey |
| grpc.md | Contratos, serviços proto, handlers, conversões, paginação, códigos de status, clients, transportes |
| gateway.md | Tipos OpenAPI, contextos de requisição e camadas, controllers, webhooks, erros, clients upstream |
| workflows.md | Iniciar workflows, payloads, determinismo, atividades e opções, versões, o worker |
| composition.md | Comandos, raízes de composição, configuração, logging, o ServiceGroup |
| security.md | O modelo de confiança, mTLS, tokens de usuário, camadas de autorização, segredos, webhooks |
| style.md | Como o código se lê: erros, concorrência, imports, Foundation, tipos, formatação |
| testing.md | Targets de teste, o que cada um prova, mocks |
| delivery.md | Workflows CI por tipo de pacote, releases, implantação, Containerfile, Dependabot |
| conventions.md | Nomear pacote, target, tipo, arquivo, chave ou pasta |
Skills relacionados
Para o código dentro das formas, carregue estes quando instalados; onde este skill declara uma forma, siga-a:
- Atores, grupos de tarefas, cancelamento, diagnósticos
Sendable, migração Swift 6:swift-concurrency - Rótulos de argumento, nomes de método, comentários de documentação:
swift-api-design-guidelines - Testes de casos de uso, handlers e workflows:
swift-testing-expert