» Python: Building Event-Driven Microservices with Kafka » 3. Consumer: Trend Service » 3.4 Service Integration

Service Integration

Let‘s call the Trend Service to show trends from the bookstore Web Service.

Add config items in service/web/infrastructure/config/config.py:

@@ -25,11 +25,17 @@ class ApplicationConfig:
     templates_dir: str
 
 
+@dataclass
+class RemoteServiceConfig:
+    trend_url: str
+
+
 @dataclass
 class Config:
     app: ApplicationConfig
     db: DBConfig
     mq: MQConfig
+    remote: RemoteServiceConfig
 
 
 def parseConfig(filename: str) -> Config:
@@ -38,5 +44,6 @@ def parseConfig(filename: str) -> Config:
         return Config(
             ApplicationConfig(**data['app']),
             DBConfig(**data['db']),
-            MQConfig(**data['mq'])
+            MQConfig(**data['mq']),
+            RemoteServiceConfig(**data['remote'])
         )

Put in config values in service/web/config.yml:

@@ -12,3 +12,5 @@ mq:
   brokers:
     - localhost:9094
   topic: "lr-book-searches"
+remote:
+  trend_url: "http://localhost:8001/trends"

Call the trend service in service/web/application/executor/book_operator.py:

@@ -2,9 +2,11 @@ from dataclasses import asdict
 from datetime import datetime
 import json
 from typing import Dict, List
+import urllib.request
+
 
 from .. import dto
-from ....domain.model import Book
+from ....domain.model import Book, Trend
 from ...domain.gateway import BookManager
 from ...infrastructure.mq import MQHelper
 
@@ -30,6 +32,11 @@ class BookOperator():
             self.mq_helper.send_event(query, json_data)
         return books
 
+    def get_trends(self, trend_url: str) -> List[Trend]:
+        with urllib.request.urlopen(trend_url) as response:
+            data = response.read()
+            return json.loads(data.decode('utf-8'))
+
 
 def _convert(b: Book) -> Dict:
     d = asdict(b)

Integrate the trend service results in service/web/adapter/router.py:

@@ -7,11 +7,13 @@ from fastapi.templating import Jinja2Templates
 from ..application.executor import BookOperator
 from ..application import WireHelper, dto
 from ...domain.model import Book
+from ..infrastructure.config.config import RemoteServiceConfig
 
 
 class RestHandler:
-    def __init__(self, logger: logging.Logger, book_operator: BookOperator):
+    def __init__(self, logger: logging.Logger, remote: RemoteServiceConfig, book_operator: BookOperator):
         self._logger = logger
+        self.remote = remote
         self.book_operator = book_operator
 
     def create_book(self, b: dto.Book):
@@ -30,9 +32,10 @@ class RestHandler:
             raise HTTPException(status_code=404, detail="Failed to get books")
 
 
-def make_router(app: FastAPI, templates_dir: str, wire_helper: WireHelper):
+def make_router(app: FastAPI, templates_dir: str, remote: RemoteServiceConfig, wire_helper: WireHelper):
     rest_handler = RestHandler(
         logging.getLogger("lr-event"),
+        remote,
         BookOperator(wire_helper.book_manager(),
                      wire_helper.message_queue_helper())
     )
@@ -42,11 +45,14 @@ def make_router(app: FastAPI, templates_dir: str, wire_helper: WireHelper):
     @app.get("/", response_class=HTMLResponse)
     async def index_page(request: Request, q: str = ""):
         books = rest_handler.book_operator.get_books(0, q)
+        trends = rest_handler.book_operator.get_trends(
+            rest_handler.remote.trend_url)
         return templates.TemplateResponse(
             name="index.html", context={
                 "request": request,
                 "title": "LiteRank Book Store",
                 "books": books,
+                "trends": trends,
                 "q": q,
             }
         )

Pass in config items in service/web/main.py:

@@ -9,4 +9,4 @@ CONFIG_FILENAME = "service/web/config.yml"
 c = parseConfig(CONFIG_FILENAME)
 wire_helper = WireHelper.new(c)
 app = FastAPI()
-make_router(app, c.app.templates_dir, wire_helper)
+make_router(app, c.app.templates_dir, c.remote, wire_helper)

Render the trends in service/web/adapter/templates/index.html:

@@ -38,17 +38,19 @@
         <!-- Trends Section -->
         <div class="mb-8">
             <h2 class="text-2xl font-bold mb-4">Trends</h2>
-            <div class="grid grid-cols-3 gap-4">
-                <!-- Trend items can be dynamically generated here -->
-                <div class="bg-white p-4 rounded-md border-gray-300 shadow">
-                    Book 1
-                </div>
-                <div class="bg-white p-4 rounded-md border-gray-300 shadow">
-                    Book 2
-                </div>
-                <div class="bg-white p-4 rounded-md border-gray-300 shadow">
-                    Book 3
+            <div class="grid grid-cols-5 gap-2">
+            {% for trend in trends %}
+                <div class="bg-white p-4 rounded-md border-gray-300 shadow mt-2">
+                    <div>#<b><a href="/?q={{trend.query}}">{{trend.query}}</a></b></div>
+                    {% for book in trend.books %}
+                        <div class="font-serif border-t py-1 mt-1">
+                            {{book.title}}
+                            <span class="italic text-sm text-gray-500">by</span>
+                            <span class="font-mono italic text-sm">{{book.author}}</span>
+                        </div>
+                    {% endfor %}
                 </div>
+            {% endfor %}
             </div>
         </div>

Restart your web and trend services, you should see something like this on the index page.

Trends

First service is integrated successfully! 🎉!