Einleitung: Warum Swift für Microservices?
Traditionell wird Swift mit der iOS- und macOS-Entwicklung assoziiert, aber mit dem Aufkommen von serverseitigen Frameworks wie Vapor und Kitura ist die Sprache zu einem leistungsstarken Werkzeug für den Aufbau von Microservice-Architekturen geworden. Microservices in Swift ermöglichen die Verwendung einer einzigen Sprache sowohl auf dem Client als auch auf dem Server, was die Wartung der Codebasis vereinfacht und die Entwicklung beschleunigt. In diesem Artikel werden wir die wichtigsten Prinzipien des Aufbaus von Microservices in Swift erläutern, praktische Beispiele mit Vapor, Combine und gRPC untersuchen und Bereitstellungsfragen diskutieren.
Hauptteil
1. Microservice-Architektur in Swift: Grundlegende Prinzipien
Die Microservice-Architektur beinhaltet die Aufteilung einer Anwendung in kleine, lose gekoppelte Dienste. Jeder Dienst ist für seine eigene Geschäftslogik verantwortlich und kann unabhängig entwickelt, bereitgestellt und skaliert werden. Für Swift bedeutet dies:
- Domain-Driven Design (DDD): Jeder Microservice repräsentiert einen separaten, begrenzten Kontext.
- Unabhängige Datenbanken: Jeder Dienst verwendet seine eigene Datenbank (PostgreSQL, SQLite, Redis).
- Ereignisgesteuerte Kommunikation: über Warteschlangen (RabbitMQ, Kafka) oder HTTP/gRPC.
Beispiel einer grundlegenden Projektstruktur in Vapor:
// Folder structure of the "UserService" microserviceUserService/├── Sources/│ ├── App/│ │ ├── Controllers/│ │ ├── Models/│ │ ├── Migrations/│ │ └── configure.swift├── Tests/├── Package.swift└── DockerfileJeder Microservice sollte seine eigene Package.swift und einen eigenen Einstiegspunkt main.swift haben.
2. Erstellen eines Microservices in Vapor mit einer REST-API
Vapor ist das beliebteste serverseitige Framework für Swift. Schauen wir uns die Erstellung eines einfachen Microservices für die Benutzerverwaltung an.
2.1 Projekteinrichtung
Installieren Sie die Vapor Toolbox und erstellen Sie ein neues Projekt:
brew install vaporvapor new UserService --template=api2.2 Modell und Controller
Definieren wir das User-Modell mit 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 }}Erstellen wir einen Controller mit CRUD-Operationen:
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