Blog

One Capability Layer Over Twenty Microservices

Kin Lane ·May 12, 2026
Table of contents

I am six posts into walking through the Naftiko Framework use cases, and this is the one that every platform engineer I talk to has lived through personally. The microservice refactor that was supposed to simplify everything. The one that produced twenty small, focused, independently deployable services. The one the client teams still complain about.

Use case six: Rightsize a set of microservices. Create a simpler capability layer over many microservices to reduce client complexity and improve consistency.

What clients actually experience

Here is the honest version of what a client team sees when they try to integrate with a modern microservice estate.

Twenty endpoints. Twenty base URIs. Four or five different auth patterns because the teams that built each service made local decisions at different moments in time. Some services want a bearer token. One of them wants an API key in a header that is slightly different than the other services’ API keys. One still wants basic auth because it was built by the team that owned the legacy monolith before the split. One requires a signed cookie nobody documented.

Payload shapes vary too. Some return JSON directly. Some wrap everything in an envelope with pagination metadata that is subtly different than the envelope the neighboring service uses. One returns Protobuf. One returns XML because that contract was locked with an integration partner two years ago.

All of this is fine — from the producer side. Each team owns its service, its contract, its release cadence. That is the point of microservices. But the client is absorbing all of it. The client is the one writing a dispatcher function that knows which of the twenty services to call, with which auth, with which payload translation, to do one business task.

That client complexity is a tax. The capability layer is how you stop charging it.

What the capability does

A Naftiko capability can declare more than one consumed adapter. Each adapter gets its own namespace, its own base URI, its own authentication, its own headers. The exposes block presents one business-oriented tool surface on top.

Here is the shape:

naftiko: "1.0.0-alpha1"

info:
  title: Customer Account Overview
  description: "One business view assembled over the customer, billing, and support microservices"

capability:
  consumes:
    - namespace: customer
      type: http
      baseUri: "https://customer-svc.internal"
      authentication:
        type: bearer
        token: ""
      headers:
        - name: X-Service-Client
          value: naftiko-capability
      resources:
        - name: customers
          path: "/v2/customers/{customerId}"
          operations:
            - name: get-customer
              method: GET
              inputParameters:
                - name: customerId
                  in: path
                  type: string
                  required: true
                  pattern: "^CUST-[0-9]{6}$"

    - namespace: billing
      type: http
      baseUri: "https://billing-svc.internal"
      authentication:
        type: apiKey
        in: header
        name: X-Billing-Key
        value: ""
      resources:
        - name: accounts
          path: "/v1/accounts/{customerId}/balance"
          operations:
            - name: get-balance
              method: GET
              inputParameters:
                - name: customerId
                  in: path
                  type: string
                  required: true

    - namespace: support
      type: http
      baseUri: "https://support-svc.internal"
      authentication:
        type: basic
        username: ""
        password: ""
      resources:
        - name: tickets
          path: "/tickets"
          operations:
            - name: list-open-tickets
              method: GET
              inputParameters:
                - name: customerId
                  in: query
                  type: string
                  required: true

  exposes:
    - type: rest
      basePath: "/api/customer-overview"
      endpoints:
        - method: GET
          path: "/{customerId}"
          description: "Full customer overview assembled from customer, billing, and support services"
          inputParameters:
            - name: customerId
              in: path
              type: string
              required: true
              pattern: "^CUST-[0-9]{6}$"
          steps:
            - call: customer.get-customer
              with:
                customerId: ""
            - call: billing.get-balance
              with:
                customerId: ""
            - call: support.list-open-tickets
              with:
                customerId: ""
          outputParameters:
            - name: customer
              type: object
              mapping: "$.steps.get-customer.response.data"
            - name: balance
              type: number
              mapping: "$.steps.get-balance.response.amount"
            - name: openTickets
              type: number
              mapping: "$.steps.list-open-tickets.response.totalCount"

Three microservices. Three different auth patterns. One exposed endpoint.

Three dimensions

Technology. The capability absorbs the differences. Bearer on one service, API key on the next, basic auth on the third — all declared, all namespace-scoped, none of it bleeding into the client. Input parameters are unified in the exposed surface with regex patterns validating them before any call goes out. The output is one typed JSON object. The client stops caring which service produced which field.

Business. Client teams stop writing dispatcher code. Platform teams get one place to change auth, one place to change headers, one place to introduce a new microservice behind the same business capability. A product manager who asks “how do I get customer overview” gets one answer instead of a whiteboard full of arrows.

Politics. This is where I see the most leverage. Microservice estates drift because every service team owns its own surface. That is correct governance at the service level. It is catastrophic governance at the client level. The capability layer gives the platform team a surface they can govern without asking twenty service teams to change their contracts. Nobody loses autonomy. The client gets a clean front door. The platform team gets a file in Git that it can lint with Spectral, version, and review.

What the framework gives you here

The features that matter specifically for this use case, from the wiki:

  • Multiple consumed adapters per capability — with namespace-scoped auth, headers, and base URIs so each microservice is isolated
  • Declarative source HTTP adapter and target REST adapter — with support for JSON and conversion from YAML, Protobuf, XML, Avro, and CSV
  • Sequence of steps with calls — so one exposed endpoint can fan out across services
  • Unified parameter handling — input parameters in query, header, path, cookie, and body, validated once at the capability layer
  • Regex pattern validation on string inputs — so malformed customer IDs never reach any of the twenty microservices

That last one matters more than it looks. Validation at the capability layer is validation that twenty service teams do not have to implement twenty times.

Where this keeps showing up

Every platform engineering team I have talked to in the last six months has a version of this problem. The microservice split was right. The client experience is wrong. The fix is not another service mesh feature. It is a capability layer that presents business-shaped tools over the top of the service-shaped mesh.

The conversation with the client team changes. It is no longer “call these twenty services in this order with these auth patterns and hope you got the payload translation right.” It is “here is the capability. Here is its contract. Here is how you consume it.”

That is what rightsizing looks like at the microservice layer.

Next

Next post in the series is Rightsize a monolith API — the other end of the same spectrum. The monolith is one big surface, the microservices are twenty small ones, and the capability pattern applies to both for the same reason.

Walking the list.