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.
Python code
79 linesimport 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
{"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
- Use a dataclass for `ServiceResponse` to reduce boilerplate and add type hints.
- 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
More from Microservices patterns
- BFF aggregation pattern: combine multiple service responses in Python easy
- Backward Compatible Schema Evolution in Python medium
- Bulkhead Thread Pool per Service Mock in Python medium
- CQRS with Separate Read and Write Repositories in Python medium
- Cache-Aside Pattern in Python: Per-Service Mock easy
- Consumer Driven Contract Pact Mock in Python medium
Keep learning
Related tutorials and quizzes for this topic.