How to Build an In-Memory Service Registry Mock in Python
A simple in-memory ServiceRegistry class to register, retrieve, list, and unregister microservice endpoints or configs using a dict, with KeyError guards.
Python code
32 linesclass ServiceRegistry:
def __init__(self):
self._services = {}
def register(self, name, service):
self._services[name] = service
def unregister(self, name):
if name not in self._services:
raise KeyError(f"Service '{name}' not found")
del self._services[name]
def get(self, name):
if name not in self._services:
raise KeyError(f"Service '{name}' not found")
return self._services[name]
def list_services(self):
return sorted(self._services.keys())
if __name__ == "__main__":
registry = ServiceRegistry()
registry.register("database", {"host": "localhost", "port": 5432})
registry.register("cache", {"host": "localhost", "port": 6379})
print("Registered services:", registry.list_services())
print("Database config:", registry.get("database"))
print("Cache config:", registry.get("cache"))
registry.unregister("cache")
print("After unregistering cache:", registry.list_services())
Output
Registered services: ['database', 'cache']
Database config: {'host': 'localhost', 'port': 5432}
Cache config: {'host': 'localhost', 'port': 6379}
After unregistering cache: ['database']
How it works
The ServiceRegistry class wraps an internal dict _services that stores services by name. register simply adds a new key-value pair, while get and unregister use membership checks (in) to raise a descriptive KeyError if the service doesn't exist. list_services uses sorted() on the dict keys to return a deterministic, alphabetically ordered list. Because the registry is in-memory, it's ideal for mocking in tests or for single-process prototypes, not for distributed production use.
Common mistakes
- Forgetting to check existence before `unregister`, which silently removes nothing or throws a raw KeyError.
- Using `self._services.keys()` directly in `list_services` without sorting, leading to non-deterministic output order.
- Sharing a single registry across threads or processes without locks, causing race conditions.
- Storing mutable service objects and unintentionally modifying them from outside the registry.
Variations
- Use a `defaultdict` if you want unregistered lookups to return a default value instead of raising.
- Add a `threading.RLock` to make the registry thread-safe.
- Return a copy of the service (e.g., `copy.deepcopy`) to prevent external mutation.
Real-world use cases
- Unit test a microservice that discovers dependencies via a lightweight, in-memory registry substitute.
- Prototype service discovery config in a monolith before moving to a real service mesh or DNS-based registry.
- In a CLI tool, maintain a registry of pluggable commands registered at startup from different modules.
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.