Au début je voulais écrire un commentaire sur l'article " J'ai souffert de terribles architectures en C # pendant dix ans ... ", mais j'ai réalisé deux choses:
- Il y a trop de pensées à partager.
- Pour un tel volume, le format des commentaires n'est pas pratique pour l'écriture ou pour la lecture.
- Je lis Habr depuis longtemps, parfois je commente, mais je n'ai jamais écrit d'articles.
- Je ne suis pas doué pour les listes numérotées.
Avertissement: je ne critique pas @ pnovikov ou son idée en général. Le texte est de grande qualité (je me sens comme un éditeur expérimenté), je partage certaines de mes réflexions. Il existe de nombreuses architectures, mais ce n'est pas grave (oui, cela ressemble au titre d'un film coréen).
Cependant, allons dans l'ordre. D'abord, mon avis sur ce qui affecte l'architecture, puis sur les points controversés de l'article sur la "fixation d'architectures". Je vais également vous dire ce qui fonctionne bien pour nous - peut-être que cela sera utile à quelqu'un.
Et bien sûr, tout ce qui est dit ici est une opinion personnelle basée sur mon expérience et mes biais cognitifs.
À propos de mon avis
Je prends souvent des décisions architecturales. Une fois - grande, une fois - petite. Parfois, je propose une architecture à partir de zéro. Eh bien, comme à partir de zéro - bien sûr, tout a été inventé avant nous, mais nous ne savons pas quelque chose, nous devons donc l'inventer. Et pas par amour pour la construction de vélos (disons, non seulement par amour pour cela), mais parce que pour certaines tâches, il n'y avait pas de solution toute faite qui conviendrait à tous les paramètres.
Pourquoi est-ce que je pense que des architectures complètement différentes ont le droit d'exister? On pourrait supposer que la programmation est un art, pas un métier, mais je ne le ferai pas. Mon avis: une fois un art, une fois un métier. Ce n'est pas à propos de ça. L'essentiel est que les tâches soient différentes. Et les gens. Pour clarifier, les tâches sont des exigences commerciales.
Si un jour mes tâches deviennent du même type, j'écrirai ou demanderai à quelqu'un d'écrire un réseau neuronal (ou peut-être qu'un script suffira) qui me remplacera. Et moi, je ferai quelque chose de moins sombre. Jusqu'à ce que mon et, j'espère, votre apocalypse personnelle ne soit pas venue, réfléchissons à la manière dont les tâches et autres conditions affectent la variété des architectures. TL&DR; - varié .
Performances versus évolutivité
C'est peut-être la raison la plus correcte pour changer l'architecture. À moins, bien sûr, qu'il ne soit plus facile d'adapter l'ancienne architecture aux nouvelles exigences. Mais ici, il est difficile de dire brièvement quelque chose d'utile.
Horaire
Disons que les termes (je me suis assuré deux fois que j'ai écrit avec un "o") sont très serrés. Ensuite, nous n'avons pas le temps de choisir, encore moins de concevoir une architecture - prenez des outils familiers et fouillez. Mais il y a une nuance - parfois des projets complexes ne peuvent être réalisés à temps qu'en appliquant (et peut-être en inventant) quelque chose de fondamentalement nouveau. Quelqu'un pourrait dire qu'inviter un client dans un bain est une technique ancienne, mais je parle d'architecture maintenant ...
Quand le timing est confortable - cela s'avère souvent être une situation paradoxale - il semble que vous pouvez trouver quelque chose de nouveau, mais pourquoi? Certes, beaucoup succombent avec succès à la tentation d'entreprendre un autre projet plus brûlant et de ramener la situation au précédent.
Dans ma pratique, le timing conduit rarement à des révolutions en architecture, mais cela arrive. Et c'est génial.
Vitesse et qualité de développement
Cela arrive - l'équipe (ou quelqu'un de la direction) remarque que la vitesse de développement a ralenti ou que de nombreux bogues se sont produits pendant l'itération. Souvent, la «mauvaise architecture» est blâmée pour cela. Parfois - à juste titre. Plus souvent - tout comme l'accusé le plus commode (surtout si l'équipe n'a pas son «parent»).
En principe, dans certains cas, tout se résume au facteur temps. Et dans d'autres - pour la maintenabilité, nous y reviendrons plus tard.
Maintenabilité
Un sujet ambigu. Parce que tout est très subjectif et dépend beaucoup de quoi. Par exemple - à partir de l'équipe, du langage de programmation, des processus de l'entreprise, du nombre d'adaptations pour différents clients. Parlons du dernier facteur, il me semble le plus intéressant.
Vous avez maintenant créé un projet personnalisé. Avec succès, dans les délais et le budget, le client est satisfait de tout. J'avais ça aussi. Maintenant, vous regardez ce que vous avez utilisé et pensez - alors voilà - une mine d'or! Nous utilisons maintenant tous ces développements, nous allons rapidement créer un produit B2B, et ... Au début, tout va bien. Le produit a été fabriqué, vendu plusieurs fois. Embauché plus de fournisseurs et de développeurs ("plus d'or nécessaire"). Les clients sont satisfaits, ils paient pour le support, de nouvelles ventes se produisent ...
Et puis l'un des clients dit d'une voix humaine - "J'aurais fait cette chose d'une manière complètement différente - combien cela peut-il coûter?" Eh bien, pensez-y - collez quelques if avec un code différent (disons, il n'y avait pas le temps de visser DI), qu'est-ce qui peut arriver?
Et la première fois, rien de grave ne se passera. Je ne conseillerais même pas dans une telle situation de clôturer quelque chose de spécial. Une complication prématurée de l'architecture s'apparente à une optimisation prématurée. Mais quand cela se produit la deuxième et la troisième fois, c'est une raison de se souvenir de choses telles que DI, le modèle de «stratégie», Feature Toggle et d'autres comme eux. Et, pendant un certain temps, cela aidera.
Et puis vient le jour où vous regardez les paramètres du projet (seulement quelques centaines d'options) pour un banc de test spécifique ... Rappelez-vous comment compter le nombre de combinaisons et pensez - comment, votre mère, cela peut-il être testé? Il est clair que dans un monde idéal, c'est simple - après tout, chaque fonctionnalité est conçue et mise en œuvre de telle manière qu'elle n'affecte en rien l'autre, et si c'est le cas, tout cela est prévu et, en général, nos développeurs ne se sont jamais trompés.
Bien sûr, j'ai épaissi les couleurs - vous pouvez mettre en évidence certains ensembles de fonctionnalités utilisées par de vrais clients, écrire plus de tests (comment et lesquels sont un sujet pour une autre conversation) et simplifier un peu la tâche. Mais pensez-y: chaque version majeure doit être testée pour tous les clients. Permettez-moi de vous rappeler que ce n'est pas du B2C, où vous pouvez dire «déployer une fonctionnalité pour 5% des utilisateurs et recueillir des commentaires» - pour le B2B, vous pouvez commencer à recueillir les commentaires des tribunaux… Des
solutions? Par exemple, divisez le produit en modules avec un cycle de vie distinct (sans oublier de tester leur interaction). Cela réduira la complexité de la maintenance, même si cela compliquera le développement. Et maintenant, je ne parle pas du sujet fertile pour les holivars «monolith vs. microservices "- dans un monolithe, vous pouvez également organiser un similaire (bien que plus compliqué, à mon avis).
Et, remarquez, d'un point de vue pragmatique, à chaque étape, nous avions une bonne architecture.
Et à quoi ça sert?
Je ne veux pas vous fatiguer (et moi-même) en énumérant d'autres raisons de changements d'architecture. Convenons maintenant que les architectures ont tendance à évoluer avec le temps, en fonction de nombreux facteurs. Cela signifie: l' architecture idéale qui résout «bien, tous les problèmes» n'existe pas.
Si je ne vous ai pas encore convaincu de cela, regardez la variété des langages de programmation et des frameworks (mais pas dans le frontend - n'ouvrez pas ce sujet). Si quelqu'un dit que c'est mauvais, je suggère de mener une expérience de pensée - imaginez un monde dans lequel il y a un langage de programmation spécifique. Avec une condition importante - vous ne l'aimez pas. Par exemple, parce que vous ne l'avez jamais utilisé et que vous n'avez jamais eu l'intention de le faire.
Et, j'avoue, il y a une autre bonne raison - proposer quelque chose de nouveau, optimiser quelques paramètres, jouer avec des compromis - c'est vraiment amusant. Maintenant que nous sommes tous d'accord (non?) Sur le fait que la diversité dans l'architecture est acceptable ...
Discussion de l'article sur la "fixation des architectures"
Et l'IoC?
À propos d'IoC, je conviens que les chaussures ont une place dans l'armée et que les modules sont universels. Mais voici le reste ...
Si, bien sûr, vous écoutez certains apologistes du "code propre", alors vous pouvez coder une montagne de services, dont chacun aura en moyenne une méthode et demie, et dans la méthode - deux lignes et demie. Mais pourquoi? Honnêtement, vous voulez absolument suivre les principes qui vous aideront à faire face à des problèmes improbables dans un avenir lointain, mais étaler même une logique simple sur des dizaines de fichiers? Ou est-ce suffisant pour vous d'écrire du code fonctionnant décemment maintenant?
En passant, je travaille maintenant sur un module qui sera certainement utilisé dans différents produits et, très probablement, sera activement "syntonisé". Alors là, j'essaye de ne pas "superficiel". Ça ne paie pas. Bien que j'utilise la seule implémentation d'interfaces plus souvent que d'habitude.
Donc, si nous avons des modules et que nous ne sommes pas «mesquins», alors d'où viennent les problèmes de performances IoC ou les «footcloths de configurations IoC» non prises en charge? Je n'ai pas rencontré.
Cependant, je clarifierai nos conditions de travail:
- Nos modules ne sont pas ceux qui «sont fournis par presque tous les frameworks IoC», mais des «modules directs» - qui communiquent entre eux à distance via l'API (parfois, pour des raisons de performances, vous pouvez les mettre dans un seul processus, mais le schéma de travail ne changera pas).
- IoC est utilisé aussi simple que possible et aussi simple que possible - les dépendances sont bloquées dans les paramètres du constructeur.
- Oui, nous avons maintenant une architecture de microservices, mais ici nous essayons de ne pas être trop petits.
Astuce: les interfaces peuvent être conservées dans le même fichier que la classe - c'est pratique (si, bien sûr, vous utilisez un IDE normal, et non un bloc-notes). Je fais des exceptions lorsque les interfaces (ou les commentaires) se développent. Mais ce n'est que du goût, bien sûr.
Quel est le problème avec l'ORM et pourquoi l'accès direct à la base de données?
Oui, je dirai moi-même ce qui ne va pas - beaucoup d'entre eux sont trop éloignés de SQL. Mais pas tout. Par conséquent, au lieu de «durer pendant que l'O / RM supprime 3000 objets» ou d'en créer un autre, trouvez celui qui vous convient.
Conseil: essayez LINQ to DB . Il est bien équilibré, il existe des méthodes de mise à jour / suppression pour plusieurs lignes. Soyez juste prudent - addictif. Oui, il n'y a pas de fonctionnalités EF et un concept légèrement différent, mais j'ai beaucoup plus aimé EF.
Au fait, c'est bien que ce soit une évolution de nos compatriotes. Igor Tkachev - respect (je ne l'ai pas trouvé sur Habré).
UPD: RouRa noté dans les commentaires qu'il existe une extension pour EF Core qui permet les opérations en bloc. Je n'abandonnerai pas LINQ to DB de toute façon, car c'est bien .
Quel est le problème avec les tests de base de données?
Oui, ils seront plus lents que les données en mémoire. Est-ce fatal? Non bien sûr que non. Comment résoudre ce problème? Voici deux recettes qu'il vaut mieux utiliser en même temps.
Recette numéro 1. Vous prenez un développeur sympa qui aime faire toutes sortes de choses sympas et discutez avec lui de la manière de résoudre ce problème à merveille. J'ai de la chance parce queObligera résolu le problème plus rapidement qu'il n'y paraissait (je ne me souviens même pas si nous en avons discuté ou non). Comment? Fabriqué (en un jour, semble-t-il) une usine de test pour ORM, qui remplace le sous-ensemble principal d'opérations par l'accès aux tableaux.
Parfait pour les tests unitaires simples. Une autre option consiste à utiliser SQLite ou quelque chose de similaire au lieu de «grandes» bases de données.
Commentaire de Obliger: . -, , ORM, , SQL . -, , , , , .. . .
Recette numéro 2. Je préfère tester des scénarios métier sur de vraies bases de données. Et si le projet déclare la capacité à prendre en charge plusieurs SGBD, des tests sont effectués pour plusieurs SGBD. Pourquoi? C'est simple. Dans la déclaration "Je ne veux pas tester le serveur de base de données", hélas, il y a une substitution de concepts. Je ne suis pas, vous savez, en train de tester si la jointure fonctionne ou la commande par.
Je teste mon code avec DB. Et sachant que même différentes versions du même SGBD peuvent produire des résultats différents sur les mêmes requêtes ( preuve ), je souhaite vérifier les principaux scénarios exactement sur les bases de données avec lesquelles ce code fonctionnera.
Habituellement, ces tests ressemblent à ceci:
- Pour un groupe de tests (Fixture), il est généré à partir de zéro en utilisant les métadonnées de la base de données. Si nécessaire, les ouvrages de référence nécessaires sont remplis.
- Chaque script ajoute lui-même les données nécessaires lors du passage (les utilisateurs le font aussi). Pas si dans les tests de performance, mais c'est une histoire complètement différente ...
- Après chaque test, les données excédentaires (à l'exception des livres de référence) sont supprimées.
Conseil: si de tels tests sont effectués pour vous objectivement pendant longtemps (et non parce qu'il est temps d'optimiser les requêtes vers la base de données), faites une compilation qui les exécutera moins souvent (catégories de test ou projet séparé pour vous aider). Sinon, les développeurs ne voudront pas exécuter les autres eux-mêmes - des tests rapides.
Transaction et e-mail
J'ajouterai simplement à l'histoire "la transaction dans la base de données pour une raison quelconque a chuté, et l'e-mail a disparu." Et quel plaisir ce sera lorsqu'une transaction attend un serveur de messagerie inaccessible, met tout le système en jeu à cause d'une notification que l'utilisateur envoie ensuite au panier sans lire ...
Certes, j'ai toujours cru que seul juin
Résultat
En général, si @ pnovikov n'a pas l'intention de conquérir le monde avec l'aide de la seule véritable
Je n'utiliserai guère le cadre proposé. La raison est simple - nous avons déjà une architecture idéale ...
PS Si vous avez envie de discuter de quelque chose dans les commentaires, je serai heureux d'y participer.