# Database per Service and Querying Across Services — Monolith vs Microservices

Source: https://www.skillbyai.com/en/monolith-microservices/d-data

> Apply database-per-service and answer queries that span services.

## Each service owns its data

In microservices, each service owns its data, and other services access it only through the service's API or events: **database per service** (which can mean a separate schema on a shared server, or a separate database). The **shared database** anti-pattern, where several services read and write the same tables, couples their deployments and schema changes and defeats independence. Owning data raises a question: how do you answer queries that span services, such as "orders with customer names and product titles"? Options: **API composition** (a composer calls each service and joins in memory; simple but slower and fragile with many calls); **data duplication via events** (the orders service keeps the customer name it needs, updated from `CustomerRenamed` events); and **CQRS read models** that subscribe to events from many services and build a query-friendly view. Accept that data across services is **eventually consistent**.

## API composition for an order details page

Calls run concurrently, and a missing optional piece does not fail the page.

```python
async def order_details(order_id):
    order = await orders_api.get(order_id)                       # required
    customer_task = customers_api.get(order["customerId"])
    products_task = catalogue_api.batch_get([i["sku"] for i in order["items"]])
    shipping_task = fulfilment_api.tracking(order_id)

    customer, products, shipping = await asyncio.gather(
        customer_task, products_task, shipping_task, return_exceptions=True
    )
    return {
        "order": order,
        "customer": None if isinstance(customer, Exception) else customer,
        "products": {} if isinstance(products, Exception) else products,
        "tracking": None if isinstance(shipping, Exception) else shipping,   # degrade gracefully
    }
```

## Separate ledgers per department

Each department keeps its own ledger. If finance wants a combined report, it asks each department for figures or keeps a summary book updated from their monthly reports; it never writes in another department's ledger.

**Quiz:** Why is a shared database between services considered an anti-pattern?

- [ ] Databases cannot handle multiple connections
- [x] Schema changes and deployments become coupled across services, defeating independence
- [ ] It is always slower than API calls
- [ ] It prevents backups

*Answer:* Schema changes and deployments become coupled across services, defeating independence. Shared tables force teams to coordinate changes, recreating monolith coupling over a network.
