From cd4dbfab7b26002962a759497cdabea3ff0a9695 Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Mon, 4 May 2026 20:35:36 -0500 Subject: [PATCH] feat(platform-knowledge): add cross-service design patterns guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the directed dependency principle, resource projection pattern, declaration vs. evaluation separation, status condition scope, inline vs. CRD tradeoffs, and technology-neutral API design — distilled from service design work on the Milo services catalog and telemetry operator. Wires the new skill file into the plan agent (loaded when a feature touches more than one service) and the api-dev agent (loaded when projecting resources into another service's API). Co-Authored-By: Claude Sonnet 4.6 --- plugins/datum-platform/agents/api-dev.md | 1 + plugins/datum-platform/agents/plan.md | 5 +- .../skills/platform-knowledge/SKILL.md | 1 + .../cross-service-patterns.md | 109 ++++++++++++++++++ 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 plugins/datum-platform/skills/platform-knowledge/cross-service-patterns.md diff --git a/plugins/datum-platform/agents/api-dev.md b/plugins/datum-platform/agents/api-dev.md index f04fadf..2b77b1a 100644 --- a/plugins/datum-platform/agents/api-dev.md +++ b/plugins/datum-platform/agents/api-dev.md @@ -55,6 +55,7 @@ Based on the task at hand: | Activity integration (policies) | `capability-activity/SKILL.md`, `capability-activity/implementation.md` | | Activity integration (events) | `capability-activity/emitting-events.md` | | Activity integration (consuming) | `capability-activity/consuming-timelines.md` | +| Projecting resources into another service's API | `platform-knowledge/cross-service-patterns.md` | ### 3. Run Scaffold Scripts diff --git a/plugins/datum-platform/agents/plan.md b/plugins/datum-platform/agents/plan.md index 17fc12e..38aa122 100644 --- a/plugins/datum-platform/agents/plan.md +++ b/plugins/datum-platform/agents/plan.md @@ -23,8 +23,9 @@ Before designing, gather context in this order: 4. Read `k8s-apiserver-patterns/SKILL.md` for storage, types, and validation patterns 5. Read `controller-runtime-patterns/SKILL.md` if controllers are involved 6. Read relevant capability skills (`milo-iam`, `capability-quota`, `capability-activity`) if integration is needed -7. Read `.claude/patterns/patterns.json` for high-confidence patterns to follow or avoid -8. Check `.claude/pipeline/designs/` for related existing designs +7. Read `platform-knowledge/cross-service-patterns.md` if the feature touches more than one service +8. Read `.claude/patterns/patterns.json` for high-confidence patterns to follow or avoid +9. Check `.claude/pipeline/designs/` for related existing designs ## Workflow diff --git a/plugins/datum-platform/skills/platform-knowledge/SKILL.md b/plugins/datum-platform/skills/platform-knowledge/SKILL.md index c32157a..2d33037 100644 --- a/plugins/datum-platform/skills/platform-knowledge/SKILL.md +++ b/plugins/datum-platform/skills/platform-knowledge/SKILL.md @@ -159,3 +159,4 @@ Read the individual capability skills for integration details. ## Related Files - `services-catalog.md` — Catalog of platform services +- `cross-service-patterns.md` — Directed dependency principle, resource projection, declaration vs. evaluation separation, and technology-neutral API design diff --git a/plugins/datum-platform/skills/platform-knowledge/cross-service-patterns.md b/plugins/datum-platform/skills/platform-knowledge/cross-service-patterns.md new file mode 100644 index 0000000..2447a0d --- /dev/null +++ b/plugins/datum-platform/skills/platform-knowledge/cross-service-patterns.md @@ -0,0 +1,109 @@ +# Cross-Service Design Patterns + +This document captures the architectural principles that govern how services in the Datum Cloud platform relate to and depend on each other. + +## Directed Dependency Principle + +Dependencies between services must flow in one direction: **producers push into downstream APIs; downstream services do not watch upstream ones.** + +When Service A's resources need to drive behavior in Service B: +- Service A's controller creates or updates resources in Service B's API +- Service B's controller only ever reconciles its own resources +- Service B has no knowledge of Service A's API + +**Why**: Cross-watch coupling ties two operators' reconciliation loops together, making them harder to deploy, scale, and reason about independently. A projected resource in the downstream API is an explicit, auditable artifact — it's visible in the cluster and has its own status. + +**Counter-pattern to avoid**: Service B watching Service A's CRDs directly. This creates a hidden runtime dependency that isn't expressed in either service's API. + +``` +✓ Correct + Service A controller ──creates──▶ ServiceB/ResourceX + +✗ Avoid + Service B controller ──watches──▶ ServiceA/ResourceY +``` + +## Resource Projection Pattern + +When one service's configuration should drive another service, the producing service projects that configuration as a concrete resource in the consuming service's API. + +``` +ServiceConfiguration (services.miloapis.com) + └─ services controller reconciles + └─ creates ServiceLevelObjective (telemetry.miloapis.com) + └─ telemetry controller reconciles + └─ backend-specific configuration +``` + +The projected resource is: +- **Explicit** — visible in the cluster with its own metadata and status +- **Auditable** — creation, updates, and deletion are tracked +- **Decoupled at runtime** — the consuming operator can be unavailable; projected resources queue up and are reconciled when it recovers + +The producing service imports the consuming service's Go types as a module dependency. This is an acceptable compile-time coupling; runtime coupling is what to avoid. + +## Declaration vs. Evaluation Separation + +Configuration resources declare **intent**. Runtime evaluation — whether the system is actually meeting that intent — is a separate concern owned by a different system. + +| Concern | Owner | Example | +|---------|-------|---------| +| Declaring an SLO target | `ServiceConfiguration` | `spec.serviceLevelObjectives[].target: "99.9"` | +| Evaluating SLO compliance | Telemetry backend | Recording rules, burn-rate alerts | +| Surfacing violations | Alerting infrastructure | Alert firing, notification routing | + +Mixing declaration and evaluation in the same resource creates a resource that reconciles against an external system it has no business querying, and produces confusing status churn on what should be a stable config object. + +## Status Conditions Reflect Configuration Health Only + +A configuration resource's status conditions should reflect whether **the configuration itself is valid and consistent** — not whether the runtime system is meeting the declared targets. + +``` +✓ Appropriate status condition + type: SLOReferencesValid + status: "True" + reason: AllIndicatorsResolved + message: "All SLO indicator references resolve to defined SLIs" + +✗ Not appropriate + type: SLOCompliant + status: "False" + reason: BurnRateTooHigh + message: "Error budget burn rate exceeds threshold" +``` + +The second example belongs in a separate resource or alert, not on the config object. Config objects should be stable; runtime compliance state changes continuously. + +## Inline Definitions vs. Separate CRDs + +When a concept is **inherently per-resource** — it doesn't make sense outside the context of its owner — define it inline as a field, not as a separate CRD. + +Promote to a separate CRD only when: +- The concept needs to be shared across multiple resource types +- The concept has its own independent lifecycle (create, update, delete independent of the owner) +- The concept needs its own RBAC surface + +**Example**: SLIs on `ServiceConfiguration` are defined inline because they are always per-service and have no value outside of a specific service's configuration. + +## Technology-Neutral APIs + +API schemas should be **backend-agnostic**. Translation to specific backends (Prometheus, Datadog, OpenTelemetry, etc.) is a deployment and configuration concern owned by the consuming operator — not expressed in the API type. + +```yaml +# ✓ Technology-neutral +spec: + serviceLevelIndicators: + - name: request-success-rate + type: Ratio + ratio: + good: { metric: http_requests_total, filters: ['{code!~"5.."}'] } + total: { metric: http_requests_total } + +# ✗ Backend-coupled +spec: + prometheus: + recordingRules: + - expr: sum(rate(http_requests_total{code!~"5.."}[5m])) +``` + +Backend-neutral APIs allow the platform to support multiple telemetry backends without API changes, and keep infrastructure-specific knowledge out of the service contract.