Introducción: ¿Por qué Swift para microservicios?
Tradicionalmente, Swift se asocia con el desarrollo para iOS y macOS, sin embargo, con la aparición de frameworks de servidor como Vapor y Kitura, el lenguaje se ha convertido en una herramienta poderosa para crear arquitecturas de microservicios. Los microservicios en Swift permiten usar un único lenguaje tanto en el cliente como en el servidor, lo que simplifica el mantenimiento de la base de código y acelera el desarrollo. En este artículo, analizaremos los principios clave para construir microservicios en Swift, veremos ejemplos prácticos con Vapor, Combine y gRPC, y también discutiremos cuestiones de despliegue.
Parte principal
1. Arquitectura de microservicios en Swift: principios básicos
La arquitectura de microservicios implica dividir la aplicación en servicios pequeños y débilmente acoplados. Cada servicio es responsable de su propia lógica de negocio y puede ser desarrollado, desplegado y escalado de forma independiente. Para Swift, esto significa:
- División por dominios (DDD): cada microservicio representa un bounded context separado.
- Bases de datos independientes: cada servicio utiliza su propia BD (PostgreSQL, SQLite, Redis).
- Comunicación orientada a eventos: a través de colas (RabbitMQ, Kafka) o HTTP/gRPC.
Ejemplo de estructura básica de un proyecto en Vapor:
// Estructura de carpetas del microservicio "UserService"UserService/├── Sources/│ ├── App/│ │ ├── Controllers/│ │ ├── Models/│ │ ├── Migrations/│ │ └── configure.swift├── Tests/├── Package.swift└── DockerfileCada microservicio debe tener su propio Package.swift y punto de entrada main.swift.
2. Creación de un microservicio en Vapor con REST API
Vapor es el framework de servidor más popular para Swift. Veamos la creación de un microservicio simple para la gestión de usuarios.
2.1 Configuración del proyecto
Instale Vapor Toolbox y cree un nuevo proyecto:
brew install vaporvapor new UserService --template=api2.2 Modelo y controlador
Definamos el modelo User con 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 }}Creemos un controlador con operaciones 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