Pourquoi l'architecture traditionnelle ne répond-elle pas aux défis modernes ?
\n\nAvez-vous déjà été confronté à une situation où l'ajout simple d'une nouvelle fonctionnalité dans une application Swift se transforme en cauchemar ? Les bugs pleuvent, les données se perdent et la logique s'étend à des proportions incroyables ? Cela vous dit quelque chose ? Bienvenue dans le monde où CQRS et Event Sourcing deviennent vos meilleurs amis.
\n\nCes patrons ne sont pas de simples mots à la mode. C'est une façon de penser qui change radicalement l'approche de construction des applications en Swift. Et aujourd'hui, nous allons les analyser avec des exemples concrets. Sans blabla, sans théories absconses — seulement de la pratique.
\n\nQu'est-ce que CQRS ? Diviser pour mieux régner
\n\nCQRS (Command Query Responsibility Segregation) est un principe qui dit : "Arrêtez de mélanger la lecture et l'écriture dans une même classe". Cela semble simple, mais les conséquences sont énormes.
\n\nDans l'architecture CRUD (Create, Read, Update, Delete) classique, vous utilisez un seul modèle pour tout. Par exemple, User. Il est à la fois sauvegardé, lu et mis à jour. Et si pour la lecture vous avez besoin de certains champs, et pour l'écriture d'autres ? Les béquilles commencent : champs optionnels, drapeaux, mappings...
\n\nCQRS propose de séparer les responsabilités :
\n- \n
- Commandes (Commands) — modifient l'état. Elles mutent les données, mais ne retournent rien. \n
- Requêtes (Queries) — lisent les données. Elles ne modifient pas l'état, seulement retournent un résultat. \n
En Swift, cela peut ressembler à ceci :
\n\n// Commande — uniquement mutation\nstruct CreateUserCommand {\n let id: UUID\n let name: String\n let email: String\n}\n\n// Requête — uniquement lecture\nstruct GetUserQuery {\n let userId: UUID\n}\n\n// Gestionnaire de commande\nclass CreateUserCommandHandler {\n func handle(_ command: CreateUserCommand) {\n // Logique de création d'utilisateur\n print("Utilisateur créé : \\(command.name)")\n }\n}\n\n// Gestionnaire de requête\nclass GetUserQueryHandler {\n func handle(_ query: GetUserQuery) -> User? {\n // Logique de récupération d'utilisateur\n return User(id: query.userId, name: "Alexis", email: "alex@example.com")\n }\n}\n\nVous voyez ? Aucun mélange. Chaque classe fait exactement une chose. Cela simplifie les tests, la maintenance et la mise à l'échelle.
\n\nEvent Sourcing : Stocker l'historique, pas l'état
\n\nPassons maintenant au Event Sourcing — un patron qui ne stocke pas l'état actuel d'un objet, mais toute la séquence d'événements qui y ont conduit.
\n\nImaginez un compte bancaire. Au lieu de stocker le solde (1000 euros), vous stockez une liste d'événements : "Ouverture du compte", "Dépôt de 500", "Retrait de 200", "Dépôt de 700". Pour connaître le solde actuel, vous rejouez simplement tous les événements. Cela offre des possibilités incroyables : audit, annulation de modifications, analytique.
\n\nImplémentons cela en Swift :
\n\n// Événement\nprotocol Event {}\n\nstruct AccountCreated: Event {\n let accountId: UUID\n let initialBalance: Double\n}\n\nstruct MoneyDeposited: Event {\n let accountId: UUID\n let amount: Double\n}\n\nstruct MoneyWithdrawn: Event {\n let accountId: UUID\n let amount: Double\n}\n\n// Agrégat — restaure l'état à partir des événements\nclass BankAccount {\n private(set) var balance: Double = 0\n private(set) var isActive: Bool = false\n private(set) var accountId: UUID?\n\n func apply(_ event: Event) {\n switch event {\n case let created as AccountCreated:\n accountId = created.accountId\n balance = created.initialBalance\n isActive = true\n case let deposited as MoneyDeposited:\n balance += deposited.amount\n case let withdrawn as MoneyWithdrawn:\n balance -= withdrawn.amount\n default:\n break\n }\n }\n\n // Restauration à partir de l'historique des événements\n static func load(from history: [Event]) -> BankAccount {\n l