How to Build a Microservice Helper in Python

A beginner-friendly Python helper that validates input, normalizes service responses, and simulates user management—showing clean patterns for microservice development.

Easy Python 3.9+ Aug 9, 2026 Microservices patterns 12 views 0 copies

Python code

79 lines
Python 3.9+
import json
from typing import Any, Dict, List


class DataValidator:
    """Simple validator for common data patterns."""

    @staticmethod
    def is_valid_email(value: str) -> bool:
        """Check if value looks like an email."""
        return "@" in value and "." in value.split("@")[-1]

    @staticmethod
    def is_positive_integer(value: Any) -> bool:
        """Check if value is a positive integer."""
        return isinstance(value, int) and value > 0


class ServiceResponse:
    """Normalize service responses for downstream consumers."""

    def __init__(self, success: bool, data: Any = None, error: str = None):
        self.success = success
        self.data = data
        self.error = error

    def to_dict(self) -> Dict[str, Any]:
        """Convert response to a serializable dictionary."""
        return {
            "success": self.success,
            "data": self.data,
            "error": self.error,
        }

    def to_json(self) -> str:
        """Serialize response to JSON string."""
        return json.dumps(self.to_dict())


class UserService:
    """Simulated microservice for user operations."""

    def __init__(self):
        self._users: List[Dict[str, Any]] = []

    def create_user(self, email: str, age: int) -> ServiceResponse:
        """Create a new user with validation."""
        if not DataValidator.is_valid_email(email):
            return ServiceResponse(success=False, error="Invalid email format")
        if not DataValidator.is_positive_integer(age):
            return ServiceResponse(success=False, error="Age must be a positive integer")

        user = {"id": len(self._users) + 1, "email": email, "age": age}
        self._users.append(user)
        return ServiceResponse(success=True, data=user)

    def get_user(self, user_id: int) -> ServiceResponse:
        """Fetch a user by ID."""
        for user in self._users:
            if user["id"] == user_id:
                return ServiceResponse(success=True, data=user)
        return ServiceResponse(success=False, error=f"User {user_id} not found")


if __name__ == "__main__":
    service = UserService()

    # Create users
    response1 = service.create_user("alice@example.com", 25)
    response2 = service.create_user("bob@example.com", -5)  # Invalid age
    response3 = service.create_user("invalid-email", 30)    # Invalid email

    # Fetch a user
    response4 = service.get_user(1)
    response5 = service.get_user(99)  # Not found

    # Print all responses as JSON
    for response in [response1, response2, response3, response4, response5]:
        print(response.to_json())

Output

stdout
{"success": true, "data": {"id": 1, "email": "alice@example.com", "age": 25}, "error": null}
{"success": false, "data": null, "error": "Age must be a positive integer"}
{"success": false, "data": null, "error": "Invalid email format"}
{"success": true, "data": {"id": 1, "email": "alice@example.com", "age": 25}, "error": null}
{"success": false, "data": null, "error": "User 99 not found"}

How it works

This code introduces three reusable classes that reflect microservices best practices: a validator, a response wrapper, and a service layer. The DataValidator keeps validation logic isolated and testable. ServiceResponse normalizes every result into a consistent shape (success, data, error), which makes integration with REST APIs and clients predictable. The UserService simulates a bounded context, hiding its internal list and exposing clear create/get operations. By returning ServiceResponse objects instead of raw data or exceptions, callers can check success without try/except, and the to_json method shows how to serialize responses for HTTP outputs.

Common mistakes

  • Raising exceptions for expected validation failures instead of returning an error response
  • Exposing internal data structures directly, breaking encapsulation
  • Forgetting to handle missing keys or invalid types in the incoming data
  • Using a single monolithic class instead of separating validation and response concerns

Variations

  1. Use a dataclass for `ServiceResponse` to reduce boilerplate and add type hints.
  2. Store users in a dictionary keyed by ID for O(1) lookups instead of a list scan.

Real-world use cases

  • REST API endpoints validating request bodies and returning consistent JSON error responses.
  • Service-to-service calls where a common response envelope standardizes success and failure signals.
  • Data ingestion pipelines screening incoming records before processing per domain rules.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Microservices patterns

Related tutorials and quizzes for this topic.