Stage 7 · Master
The API Gateway & Service Boundaries
The API Gateway Pattern
Expose one public edge that speaks auth, rate limits, and routing so internal services can stay narrow.
Why Fieldwork Does Not Expose Every Service
One of the easiest ways to make a microservice architecture look real is to expose a dozen ports and call that flexibility. I did not want that for Fieldwork. The product is a multi-tenant workspace and task-tracking API, not a networking demo. Public clients should have one stable entrypoint, one authentication contract, one error envelope, and one place where cross-cutting concerns are enforced before traffic fans out across internal services.
The alternative was direct service exposure: let tasks, projects, workspaces, and future services each publish their own public API and let the frontend talk to them independently. The concrete cost of that approach is coordination overhead at the worst possible layer. Every service would need its own auth validation, rate limiting, CORS posture, observability hooks, and client-facing versioning story. That is not service autonomy. That is duplicated edge code.
| Public API shape | Immediate appeal | Why Fieldwork rejected it |
|---|---|---|
| Expose each service directly | Services look independent | Cross-cutting edge logic gets reimplemented everywhere |
| Backend-for-frontend only | Frontend-specific shaping is easy | Too narrow once other clients and internal policy concerns arrive |
| API gateway in front of services | One edge contract, private internal topology | Gateway must be kept focused and not become business logic soup |
What the Gateway Owns at the Edge
In Fieldwork, the gateway owns the responsibilities that are truly edge concerns: validating JWTs, establishing tenant context, applying request IDs and deadlines, enforcing rate limits, checking route-level RBAC, and forwarding the request to the correct internal service. Those are concerns that should behave the same way whether the caller is loading a task list or updating a workspace membership.
What it does not own is business orchestration that belongs to a domain. If deleting a project should cascade task archival under specific rules, that logic stays in the projects service. The gateway may know where to send DELETE /projects/:id, but it should not know how project deletion works. That boundary is the only thing that keeps a gateway from turning into a disguised monolith.
Reverse Proxying Without Hiding Service Boundaries
A gateway can accidentally erase useful service boundaries if it starts path-rewriting, response-shaping, and fallback behavior so aggressively that downstream services no longer own their own contracts. I avoided that by keeping Fieldwork's public paths aligned with service domains. Tasks traffic still goes to the tasks service. Projects traffic still goes to the projects service. The gateway is a controlled entrance, not a second application layer with a secret routing language.
That alignment also keeps observability cleaner. When latency spikes on task creation, traces and logs still point to the tasks service as the owner, even though traffic entered through the gateway. If the gateway had started reshaping domain boundaries for convenience, we would have paid later in debugging confusion and deployment coupling.
If a new feature requires branching business rules, validation, or data access inside the gateway, that is usually a sign the feature belongs in a service instead.
Where the Pattern Can Go Wrong
The failure mode I wanted to guard against was the gateway becoming the smartest service in the system. Once teams realize it already sees every request, they are tempted to stuff in ad hoc orchestration, response aggregation, and emergency exceptions. Sometimes that is necessary, but it should feel expensive. The gateway is on every request path, so complexity there magnifies quickly.
There is also an organizational trap: people call something a gateway when it is really just a reverse proxy plus fear. If the gateway owns auth, rate limits, tracing, and authorization consistently, it is earning its keep. If it is only there because the architecture diagram wanted one box in front, then it is just another failure point. Fieldwork uses the pattern because it simplifies the client contract and concentrates truly shared responsibilities, not because microservices are supposed to have one.
- Keep public clients on one stable origin and one auth contract.
- Centralize only cross-cutting edge concerns in the gateway; keep domain behavior in services.
- Preserve service-aligned routing so traces, ownership, and failure analysis stay honest.
The Boundary Decision for Fieldwork
Fieldwork exposes a single public API gateway and keeps tasks, projects, workspaces, and future services private behind it. The gateway owns authentication, tenant context, route-level authorization, request shaping at the protocol edge, and forwarding. Services own domain logic and persistence.
We rejected direct public exposure of each service because it would have multiplied edge policy in exactly the place we need consistency most. The gateway pattern is not free; it introduces a choke point that must stay disciplined. But for this system, that cost is lower than teaching every service how to be a perfect public edge on its own.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.