How to implement the Database per service pattern in Python

Simulate separate databases per microservice in Python using dataclasses and in-memory dictionaries, showing how services own their data independently.

Medium Python 3.10+ Aug 9, 2026 Microservices patterns 12 views 0 copies

Python code

61 lines
Python 3.10+
import json
from dataclasses import dataclass, asdict
from typing import Dict, List


@dataclass
class User:
    id: int
    name: str
    email: str


@dataclass
class Order:
    id: int
    user_id: int
    product: str
    amount: float


class UserServiceDB:
    """Simulates a separate database for the User service."""
    def __init__(self) -> None:
        self._users: Dict[int, User] = {}

    def create_user(self, user: User) -> User:
        self._users[user.id] = user
        return user

    def get_user(self, user_id: int) -> User | None:
        return self._users.get(user_id)


class OrderServiceDB:
    """Simulates a separate database for the Order service."""
    def __init__(self) -> None:
        self._orders: Dict[int, Order] = {}

    def create_order(self, order: Order) -> Order:
        self._orders[order.id] = order
        return order

    def get_orders_for_user(self, user_id: int) -> List[Order]:
        return [o for o in self._orders.values() if o.user_id == user_id]


if __name__ == "__main__":
    user_db = UserServiceDB()
    order_db = OrderServiceDB()

    user_db.create_user(User(id=1, name="Alice", email="alice@example.com"))
    order_db.create_order(Order(id=101, user_id=1, product="Laptop", amount=1200.00))
    order_db.create_order(Order(id=102, user_id=1, product="Mouse", amount=25.50))

    user = user_db.get_user(1)
    orders = order_db.get_orders_for_user(1)

    print(json.dumps({
        "user": asdict(user) if user else None,
        "orders": [asdict(o) for o in orders]
    }, indent=2))

Output

stdout
{
  "user": {
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com"
  },
  "orders": [
    {
      "id": 101,
      "user_id": 1,
      "product": "Laptop",
      "amount": 1200.0
    },
    {
      "id": 102,
      "user_id": 1,
      "product": "Mouse",
      "amount": 25.5
    }
  ]
}

How it works

This code models the database per service pattern by giving each microservice its own dedicated data store — UserServiceDB and OrderServiceDB each hold only their relevant entities. Dataclasses provide a clean, typed structure for the domain models, while asdict converts them to plain dictionaries for easy JSON serialization. The services expose narrow, domain-specific APIs (e.g., get_orders_for_user) instead of generic database access, mirroring how real microservices enforce data ownership boundaries. In production you'd replace the in-memory dictionaries with actual databases like PostgreSQL or DynamoDB, but the service boundary and ownership concept stays identical.

Common mistakes

  • Letting one service query another service's database directly, breaking the ownership boundary
  • Returning raw database objects instead of serializable DTOs or dicts
  • Forgetting that `User | None` requires Python 3.10+ for the union type syntax
  • Copying entities between services instead of using API calls or events

Variations

  1. Use SQLite with separate `.db` files per service instead of in-memory dicts
  2. Add repository interfaces to abstract the storage backend for easier testing

Real-world use cases

  • Designing a microservices architecture where a User service owns profiles and an Order service owns transactions independently.
  • Prototyping service boundaries in a monorepo before splitting into separate deployable services.
  • Writing unit tests that mock service databases without needing a real database connection.

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.