Appendix B - 18. Service-flow / API model
The Structure of Service-flow / API model — its shape at a glance:
One dialect model is the source of truth. Generators emit access policy, the service catalog, and env wiring from it; bidirectional parity gates hold every tree to an entity and every handler to its spec.
flowchart LR
M[(Service model)] --> Gen[Generators]
Gen --> Policy[/Access policy/]
Gen --> Env[/Env wiring/]
Reality[(Trees + handlers)] --> P{Parity gates}
M --> P
P -->|either side diverges| Fail([build blocked])
Accessible description: one service model feeds generators that emit access policy and env wiring, and also feeds bidirectional parity gates that compare it against the real trees and handlers. Divergence on either side blocks the build.
Projected from the catalogue entry system-models / Service-flow / API model.
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — A Backstage-dialect model of the service-oriented architecture — every service, its APIs, inter-service auth, URL wiring, and frontend trees — that is the source of truth the deployment, NetworkPolicy, and API docs are generated from and validated against.
Motivation
A service-oriented deployment has many moving parts that must agree: inter-service auth headers, URL env vars, NetworkPolicy, the SOA_SERVICES deploy table, the public-API contract, the frontend trees. Kept in sync by hand across code + YAML + deploy scripts, they drift — a service gains an endpoint the NetworkPolicy doesn't allow, a handler diverges from its spec, a frontend tree exists with no entity. Each drift is a production-shaped bug, and there are many surfaces to drift.
Applicability
- A service-architecture dialect (here Backstage) expressive enough for the SOA fields.
- Generators that emit the real artifacts from the model.
- Bidirectional parity gates so neither the model nor reality can drift unilaterally.
Structure
The Structure diagram appears at the top of this page.
Sample Code
The model is the source of truth generators emit from and parity gates check both ways: every declared service maps to a real handler tree, and every tree maps back to a service — so no copy is hand-synced and none can silently disagree.
import sys
# The service model: the single source of truth for the SOA.
MODEL = {"web": ["/upload", "/status"], "worker": ["/handle"]}
def generate_policy() -> list[str]:
return [f"allow {svc} -> {ep}" for svc, eps in MODEL.items() for ep in eps]
def parity(real_trees: dict[str, list[str]]) -> list[str]:
"""Bidirectional: every model service ↔ a real tree, every real endpoint ↔ the model."""
findings = []
for svc, eps in MODEL.items():
real = set(real_trees.get(svc, []))
findings += [f"{svc}{e}: declared, no handler" for e in set(eps) - real]
findings += [f"{svc}{e}: handler with no model entry" for e in real - set(eps)]
for svc in real_trees.keys() - MODEL.keys():
findings.append(f"service '{svc}' exists but is unmodeled")
return findings
if __name__ == "__main__":
# `scan_handler_trees` enumerates the real service->endpoints map from the code.
findings = parity(scan_handler_trees())
for f in findings:
print(f"SOA-DRIFT: {f}")
sys.exit(1 if findings else 0)
Consequences
- A new service/endpoint/tree ⇒ a model edit or a parity-gate failure (deliberately).
- Dialect lock-in. Adopting Backstage's schema inherits its conventions (a deliberate "adopt the canonical schema" trade).
- Generator + gate maintenance across several surfaces.
Example use within DocAble
- The Backstage-dialect service YAML — source of truth for inter-service auth, URLs, NetworkPolicy, and the SOA deploy table.
- The web-API (OpenAPI), wire-contract, and config schemas.
- Drift gates: the service-flow parity lint (tree↔yaml), the public-API drift lint (handler↔spec).
Related Patterns
- Bridge — agents query it (query-surface
service-flow/web-api) to reason about the SOA (agent side) ◀──▶ it generates & governs the deployed system — NetworkPolicy, wiring, API docs (product side). The clearest bridge: one model, both faces. - Enabler — feeds model-driven-codegen.
- Counterpart — drift-parity-gates: the bidirectional parity lints.