Introdução: Por que Swift para microsserviços?
Tradicionalmente, Swift é associado ao desenvolvimento para iOS e macOS, no entanto, com o surgimento de frameworks server-side como Vapor e Kitura, a linguagem se tornou uma ferramenta poderosa para criar arquitetura de microsserviços. Microsserviços em Swift permitem usar uma única linguagem tanto no cliente quanto no servidor, o que simplifica a manutenção da base de código e acelera o desenvolvimento. Neste artigo, abordaremos os princípios-chave da construção de microsserviços em Swift, veremos exemplos práticos com Vapor, Combine e gRPC, e discutiremos questões de implantação.
Parte Principal
1. Arquitetura de microsserviços em Swift: princípios básicos
A arquitetura de microsserviços envolve a divisão da aplicação em serviços pequenos e fracamente acoplados. Cada serviço é responsável por sua própria lógica de negócios e pode ser desenvolvido, implantado e escalado de forma independente. Para Swift, isso significa:
- Divisão por domínios (DDD): cada microsserviço representa um bounded context separado.
- Bancos de dados independentes: cada serviço usa seu próprio BD (PostgreSQL, SQLite, Redis).
- Comunicação orientada a eventos: através de filas (RabbitMQ, Kafka) ou HTTP/gRPC.
Exemplo de estrutura básica de projeto no Vapor:
// Estrutura de pastas do microsserviço "UserService"UserService/├── Sources/│ ├── App/│ │ ├── Controllers/│ │ ├── Models/│ │ ├── Migrations/│ │ └── configure.swift├── Tests/├── Package.swift└── DockerfileCada microsserviço deve ter seu próprio Package.swift e ponto de entrada main.swift.
2. Criando um microsserviço no Vapor com REST API
Vapor é o framework server-side mais popular para Swift. Vamos criar um microsserviço simples para gerenciamento de usuários.
2.1 Configuração do projeto
Instale o Vapor Toolbox e crie um novo projeto:
brew install vaporvapor new UserService --template=api2.2 Model e Controller
Vamos definir o model User com Fluent ORM:
import Fluentimport Vapor
final class User: Model, Content { static let schema = "users" @ID(key: .id) var id: UUID? @Field(key: "name") var name: String @Field(key: "email") var email: String init() { } init(id: UUID? = nil, name: String, email: String) { self.id = id self.name = name self.email = email }}Vamos criar um controller com operações CRUD:
struct UserController: RouteCollection { func boot(routes: RoutesBuilder) throws { let users = routes.grouped("users") users.get(use: index) users.post(use: create) users.group(":userID") { user in user.get(use: show) user.put(use: update) user.delete(use: delete) } } // GET /users func index(req: Request) async throws -> [User] { try await User.query(on: req.db).all() } // POST /users func create(req: Request) async throws -> User { let user = try req.content.decode(User.self) try await user.save(on: req.db) return user } // GET /users/:id func show(req: Request) async throws -> User { guard let user = try await User.find(req.parameters.get("userID"), on: req.db) else { throw Abort(.notFound) } return user } // PUT /users/:id func update(req: Request) async throws -> User { guard let user = try await User.find(req.parameters.get("userID"), on: req.db) else { throw Abort(.notFound) } let updated = try req.content.decode(User.self) user.name = updated.name user.email = updated.email try await