Cet article est une continuation de l'histoire de la rédaction d'un blog de démonstration sur les microservices (les parties précédentes peuvent être lues ici:
Partie 1 «Description générale de l'architecture» ,
Partie 2 «API Gateway» ,
Partie 3 «Service User» ). Cet article se concentrera sur la mise en œuvre du microservice Post (articles).
La principale caractéristique du microservice est qu'il implémente différents types de connexions avec d'autres services. Par exemple, avec le service Commentaires (commentaires), le type de communication un-à-plusieurs est implémenté (un article peut avoir plusieurs commentaires) et les services Utilisateur et Catégorie ont des connexions plusieurs-à-un (c'est-à-dire qu'un utilisateur peut avoir plusieurs articles et un catégories peuvent être plusieurs articles).
En termes de fonctionnalité, les méthodes suivantes seront mises en œuvre dans le service postal:
- Journalisation des demandes de service et des états intermédiaires (le mécanisme est décrit en détail dans la partie 3 «Service utilisateur») avec TraceId (identique à l'api-gw émis; voir la partie 2 «Passerelle API» )
- Fonctions CRUD (créer, lire, éditer, supprimer des enregistrements dans la base de données - MongoDB).
- Fonctions de recherche: rechercher tous les articles, rechercher par catégorie, rechercher par auteur
Traditionnellement, nous allons commencer à créer un microservice en le décrivant dans un profil.
Ensuite, nous générons le cadre du microservice. Pour ce faire, accédez au répertoire racine du projet et exécutez la commande sh ./bin/protogen.sh.
Super! La plupart du travail pour nous a été fait par le générateur de code, nous avons juste eu à écrire une implémentation des fonctions de l'application. Ouvrez le fichier ./services/post/functions.go et écrivez l'implémentation.
Considérez les principaux fragments de la fonction Créer.
1. Analysez le contexte de l'appel et obtenez des informations sur l'utilisateur.
... md,_:=metadata.FromIncomingContext(ctx) var userId string if len(md["user-id"])>0{ userId=md["user-id"][0] } ...
2. Vérifiez les paramètres de la requête et s'ils contiennent des valeurs non valides, renvoyez l'erreur correspondante.
... if in.Title==""{ return nil,app.ErrTitleIsEmpty } ...
3. Enregistrez la publication dans la base de données (mongoDB).
... collection := o.DbClient.Database("blog").Collection("posts") post:=&Post{ Title:in.Title, SubTitle:in.SubTitle, Content:in.Content, Status:app.STATUS_NEW, UserId:userId, Categories:in.Categories, } insertResult, err := collection.InsertOne(context.TODO(), post) if err != nil { return nil,err } ...
4. Nous recevons l'ID de l'enregistrement créé, nous l'ajoutons à la réponse et nous renvoyons la réponse.
... if oid, ok := insertResult.InsertedID.(primitive.ObjectID); ok { post.Slug=fmt.Sprintf("%s",oid.Hex()) }else { err:=app.ErrInsert return out,err } out.Post=post return out,nil ...
J'ai mentionné plus tôt que le service postal est intéressant pour ses connexions avec d'autres services. Ceci est clairement démontré par la méthode Get (get Post par l'ID donné).
Tout d'abord, lisez le mongoDB Post:
... collection := o.DbClient.Database("blog").Collection("posts") post:=&Post{} id, err := primitive.ObjectIDFromHex(in.Slug) if err != nil { return nil,err } filter:= bson.M{"_id": id} err= collection.FindOne(context.TODO(), filter).Decode(post) if err != nil { return nil,err } ...
Ici, tout est plus ou moins simple. Tout d'abord, convertissez la chaîne en ObjectID, puis utilisez-la dans le filtre pour rechercher l'enregistrement.
Maintenant, nous devons enrichir l'enregistrement de publication résultant avec des données sur l'auteur. Pour ce faire, accédez au service utilisateur et obtenez un enregistrement pour l'ID utilisateur spécifié. Vous pouvez le faire comme suit:
...
Je tiens à noter que j'utilise délibérément deux termes différents utilisateur et auteur, car Je crois qu'ils se situent dans des contextes différents. L'utilisateur concerne l'authentification des noms d'utilisateur / mots de passe et d'autres attributs et fonctions d'une manière ou d'une autre liés à la sécurité et à l'accès. L'auteur est une entité sur les articles publiés, les commentaires, etc. L'entité auteur est née dans le contexte de la publication, en utilisant les données de l'utilisateur comme base. (J'espère avoir réussi à expliquer la différence;)
L'étape suivante consiste à lire les données des catégories connexes à partir du service de catégorie. Je ne suis pas sûr de proposer la meilleure option (j'espère que la communauté la corrige). L'essence de l'approche est la suivante: nous faisons UNE demande au service de catégorie et soustrayons TOUTES les catégories existantes, puis dans le service de poste, nous sélectionnons uniquement les catégories liées à la poste. L'inconvénient de cette approche est la surcharge pour les données transmises, plus - nous ne faisons qu'une seule demande. Parce que le nombre de catégories n'est certainement pas énorme, je pense que les frais généraux peuvent être négligés.
...
La prochaine chose que nous devrions faire est d'obtenir tous les commentaires connexes. Ici, la tâche est similaire à la tâche avec des catégories, sauf que dans le cas des catégories Id, les catégories liées ont été stockées dans Post, dans le cas des commentaires, en revanche, l'ID de la publication parent est stocké directement dans les commentaires enfants. En fait, cela simplifie considérablement la tâche, car tout ce que nous devons faire est de faire une demande au service Commentaires en indiquant le message parent et de traiter le résultat - dans une boucle, ajoutez au message tous les commentaires connexes
...
Et renvoyez le message collecté
... out.Post=post return out,nil ...
Dans l'interface Web, nous avons implémenté la navigation par catégorie et par auteur. C'est-à-dire lorsqu'un utilisateur clique sur une catégorie, une liste de tous les articles liés à la catégorie sélectionnée s'affiche. Et lorsqu'il clique sur l'auteur, une liste d'articles s'affiche en conséquence, où l'utilisateur sélectionné est indiqué par l'auteur.
Il existe deux méthodes pour implémenter cette fonctionnalité dans le service Post:
GetPostCategory - Renvoie une structure PostCategory qui contient l'ID, le nom de la catégorie et la collection d'articles associés
GetAuthor - Renvoie une structure d'auteur qui contient des attributs utilisateur (FirstName, LastName, etc.) et une collection de
messages associés.
Je ne décrirai pas en détail la mise en œuvre de ces méthodes afin de ne pas les répéter. Ils sont basés sur les mêmes fragments de code que ceux décrits ci-dessus.