Process boundaries

The use case should live in one runtime

An in-process hop is another file. An RPC hop is another repository, contract, CI, clock, and failure mode. Align the process boundary with the change boundary. Applications you own that always change together should not be networked together.

Chatty façade

Orchestration across noun services

Client
  → BFF
    → get account
    → get balance
    → get limit
    → charge provider
    → debit ledger
    → insert row

The workflow lives in the façade; the rules live in the callees. Neither side is complete. Each extra call is another repository to open. Deployments must roll together anyway.

Capability API

One service, thin channel adapter

Client
  → optional auth adapter
    (same body, no remap)
  → capability API
    → in-process modules
    → its database

HTTP is the product API. Side effects that are not part of the decision go out of band after commit. Process boundary only where a second team, scale, or lifetime actually exists.

End-to-end

Public contract to persistence

Client
  → capability API
    → operation file
    → data context

No translation layer whose only job is to call the first. External systems you do not own stay behind one boring client. End-to-end means one use case, one runtime — not one company binary.

What each extra RPC costs an agent

The use case is no longer in one place

Change the check in one service and miss the façade call order: production bug. Skip the call for “performance”: the rule disappears. Add a status only on the RPC: the façade still returns 500.

Contracts duplicate the same facts

Façade request → proto → command → domain entity, each with a version number. The agent must know which consumer is on which contract.

Partial failure becomes something you have to design for

Each extra RPC multiplies the states the operation can end in. Sagas and outboxes are more applications. Keep that story for things that are actually remote.

Tests lie at the boundary

Mocked clients never see the query bug. Contract tests that only assert payload shape are not executable documentation of the operation.

Improvements that help even when you must split

Slice by use case

A capability that owns the verbs on a ticket beats Account + Ledger + Limit microservices that force a distributed transaction for every feature.

One public contract

Publish the user intention. Do not publish get/get/get for a screen and make every channel reassemble them. Composite reads are query operations on the owner.

System map

When a second deployable appears, a root map lists use case, repo, entry file, tables, and downstreams. If the happy path needs a sequence diagram, the topology is already too hard.

Monorepo for co-shipped code

Three clones and three CI definitions prevent atomic PRs. Do not “fix” duplication with a shared contracts package that rebuilds the world.

One error vocabulary

Pick the closed set and keep it stable. Map once at the owning edge. Downstream adapters must not invent a second enum.

Async after the decision

Commit, then publish. Notifications and analytics may lag. The money path must not.

Idempotency as a field

Retries will happen. Put the key on the request so a second call is visibly safe. Hidden “maybe retry in the façade” is global knowledge.

Gateways are pipes

Terminate TLS and route. The moment a gateway aggregates two capabilities, it becomes a façade written in YAML — worse, because the workflow is not in C#.

Frontends call operations

A GraphQL schema that stitches entity resolvers is chatty RPC with a type system on top. A mutation that resolves entirely in the owning subgraph is fine.

Decision table

Which topology to pick for five common situations, and why it is cheaper for an AI agent.
Situation Topology Why it is cheaper for AI
New field or rule on an existing operationEnd-to-end capabilityOne process, one test host, local reasoning
Channels need different authThin adapter + same HTTPAdapter has no business rules to duplicate
Real external providerOne client inside the ownerExternal I/O is explicit, not an internal mesh
Notifications, ETL, searchEvents after commitFailure there must not roll back what the user already saw
Two domains that rarely co-changeTwo apps, coarse APIsChange boundary matches service boundary

Across applications, end-to-end ownership costs an agent far less than one RPC behind a façade, which costs far less than a chatty mesh. Chatty RPC is the Layered hop chain moved onto the network, where the compiler can no longer save the agent.