# Architecture overview

> One public door, one private corridor, and a service for each kind of data.

Source: https://docs.dashfordevs.com/v1/how-it-works/architecture · Topic: How the platform works · Updated: 2026-11-07

DashForDevs used to be one large application. It is now a set of **services**, each owning one kind of data in its own database, behind two gateways. The public contract (the `/api/...` paths, the keys, the JSON) did not change, so existing sites kept working through the move.

```dfd-diagram
{
  "type": "flow",
  "title": "Who talks to whom",
  "layout": "TB",
  "groups": [
    { "id": "public", "label": "Public internet" },
    { "id": "edge", "label": "Edge (TLS, firewall)" },
    { "id": "corridor", "label": "Private network: the corridor" },
    { "id": "private", "label": "Private network: services" },
    { "id": "isolated", "label": "Isolated" }
  ],
  "nodes": [
    { "id": "site", "label": "Your site / SDK", "kind": "client", "group": "public" },
    { "id": "browser", "label": "Dashboard user", "kind": "person", "group": "public" },
    { "id": "papi", "label": "papi", "kind": "gateway", "group": "edge", "detail": "Public API. Checks API keys, origins, rate limits, organization status. Signs an identity. Never stores business data." },
    { "id": "bff", "label": "Dashboard (BFF)", "kind": "app", "group": "edge", "detail": "The dashboard's own server. Holds your session; your browser never holds a token." },
    { "id": "accounts", "label": "accounts", "kind": "service", "group": "edge", "detail": "Identity provider: sign-in, two-factor, OIDC." },
    { "id": "tapi", "label": "tapi", "kind": "gateway", "group": "corridor", "detail": "Private gateway. Routes /<service>/v1/... between services. Not on the internet." },
    { "id": "inventory", "label": "inventory", "kind": "service", "group": "private" },
    { "id": "orders", "label": "orders", "kind": "service", "group": "private" },
    { "id": "customers", "label": "customers", "kind": "service", "group": "private" },
    { "id": "vault", "label": "customer PHI vault", "kind": "data", "group": "isolated", "detail": "Only reachable from the customers service. On no gateway." },
    { "id": "content", "label": "content", "kind": "service", "group": "private" },
    { "id": "more", "label": "8 more services", "kind": "service", "group": "private", "detail": "shipping, payments, billing, emails, analytics, automations, logs and cloud." }
  ],
  "edges": [
    { "from": "site", "to": "papi", "label": "X-API-Key" },
    { "from": "browser", "to": "bff", "label": "session cookie" },
    { "from": "bff", "to": "accounts", "label": "OIDC", "style": "dashed" },
    { "from": "papi", "to": "tapi", "label": "signed identity" },
    { "from": "bff", "to": "tapi", "label": "your token" },
    { "from": "tapi", "to": "inventory" },
    { "from": "tapi", "to": "orders" },
    { "from": "tapi", "to": "customers" },
    { "from": "tapi", "to": "content" },
    { "from": "tapi", "to": "more" },
    { "from": "customers", "to": "vault", "label": "private link" }
  ],
  "steps": [
    { "title": "Two kinds of callers", "focus": ["site", "browser"], "text": "Websites and tools call with an API key. People use the dashboard with a signed-in session." },
    { "title": "Two front doors", "focus": ["papi", "bff", "accounts"], "text": "Keys go to papi. Sessions go to the dashboard's own server, which signs people in through accounts." },
    { "title": "One private corridor", "focus": ["tapi"], "text": "Both doors reach services only through tapi, on the private network." },
    { "title": "Services own their data", "focus": ["inventory", "orders", "customers", "content", "more"], "text": "Each service has its own database and its own audit log. No service reads another's tables." },
    { "title": "The vault", "focus": ["customers", "vault"], "text": "Customer personal data sits in a vault that only the customers service can reach." }
  ]
}
```

## The services

| Service | Owns | Personal data? |
| --- | --- | --- |
| accounts | People, sign-in, two-factor, sessions, OIDC | Account holders only |
| organizations | Organizations, members, roles, permissions, compliance checklist | No |
| inventory | Catalog, sizes, stock, locations, reviews, bundles | No |
| orders | Orders, carts, checkout, discounts, loyalty, tax, finance | Yes, encrypted |
| customers | Shoppers, tags, segments, bans, calendar | Ids only; details in the vault |
| customer PHI vault | Customer names, contacts, addresses, notes, survey answers | Yes, encrypted, isolated |
| content | Blog, page groups, media, legal documents, docs | No |
| shipping | Carrier connections, label rules, shipping locations | Credentials, encrypted |
| payments | Stores' payment processors, charges, subscriptions | Card tokens only, never card numbers |
| billing | What DashForDevs charges organizations | No |
| emails | Inbox, contact forms, transactional mail | Yes |
| analytics | De-identified reports | None, by construction |
| automations | Flows, runs, webhooks | Ids only at rest |
| logs | Traces of every action, per organization | Field names only |
| cloud | Apps, databases, projects for DashForDevs Cloud | No |

## Rules every service follows

-   **One writer per table.** Exactly one service writes each kind of data.
-   **Organization from identity, never from the request.** A service takes the organization from the signed identity or the verified session, never from a header or parameter the caller chose.
-   **Two checks.** The gateway checks the caller, and the service checks again. One bug is not a breach.
-   **Ids, not values, in logs and events.** Audit rows, traces and events carry record ids and field names only.
-   **Postgres only**, one database per service, on AWS, encrypted at rest.
