Skip to content

Factorize capability core with aggregates[]: functions first, entities/events later #191

@eskenazit

Description

@eskenazit

Component

Core Engine

Problem & Motivation

As a capability author who needs to expose the same domain behavior over multiple transports (REST, MCP, gRPC, and future adapters),

I want to define that domain behavior once and have each adapter reference it,

so that I do not have to duplicate the same “invocable unit” structure across transports, and I can reduce maintenance cost and drift as new adapters are added.

Proposed Solution

Introduce a domain-driven factorization layer that makes a capability’s domain core explicit and reusable across adapters:

  1. Add capability.aggregates[] (optional top-level section)
    • A new section under capability, sibling to exposes and consumes, representing the domain core.
    • Each entry is a DDD aggregate with:
      • label (domain name, stable anchor)
      • namespace (machine-readable qualifier)
      • functions[] (reusable invocable units)
    • Designed to later include entities[] and events[] within the same aggregate boundary.
  2. Enable adapter units to reference domain functions via ref
    • REST operations, MCP tools, and gRPC procedures can use:
      • ref: aggregate-namespace.function-name (e.g. forecast.get-forecast)
    • Resolution rules:
      • ref must resolve at capability load time (unknown aggregate/function fails early).
      • Referenced fields are merged into the adapter-local object.
      • Adapter-local explicit fields override inherited ones.
      • No chained refs (a function cannot ref another function).
  3. Add transport-neutral semantics on invocable units
    • Add safe, idempotent, and cacheable as behavioral intent metadata.
    • Use cases:
      • Design-time: tooling suggests HTTP methods for REST operations derived from functions, while method remains explicit in the spec.
      • Adapter derivations: enable consistent classification for SKILL (read/write) and future adapters like tRPC (query/mutation).

Scope / Notes

  • Backward compatible: existing capabilities remain valid with no changes; aggregates[] is opt-in.
  • The API adapter rename (type: "api"type: "rest") is expected soon; implementation should anticipate it.

Example

capability:

  aggregates:
    - label: "Forecast"
      namespace: "forecast"
      functions:
        - name: "get-forecast"
          description: "Fetch current weather forecast for a location."
          semantics:
            safe: true
            idempotent: true
          inputParameters:
            - name: "location"
              type: "string"
          call: "weather-api.get-forecast"
          with:
            location: "location"
          outputParameters:
            - name: "forecast"
              type: "object"
              mapping: "$.forecast"

  exposes:
    - type: "api" # soon renamed "rest"
      namespace: "forecast-rest"
      resources:
        - path: "/forecast/{location}"
          operations:
            - ref: "forecast.get-forecast"
              method: "GET" # explicit; tooling can suggest based on semantics
              inputParameters:
                - name: "location"
                  in: "path"

    - type: "mcp"
      namespace: "forecast-mcp"
      tools:
        - ref: "forecast.get-forecast"

    - type: "grpc"
      namespace: "forecast-grpc"
      service: "ForecastService"
      procedures:
        - ref: "forecast.get-forecast"

Agent Context (optional)

agent_name: "ADR → Feature Request"
tool: "Notion + GitHub"
llm_model: "Claude Sonnet 4.6"
confidence: "medium"
source_event: "user request"
source page "ADR: Cross-Adapter Factorization: Domain-Driven Core"

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions