{
  "$type": "site.standard.document",
  "canonicalUrl": "https://rednafi.com/go/gateway-pattern/",
  "description": "Separate business logic from external service calls using the Gateway pattern. Apply dependency inversion and interface segregation in Go.",
  "path": "/go/gateway-pattern/",
  "publishedAt": "2025-08-03T00:00:00.000Z",
  "site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
  "tags": [
    "Go",
    "Distributed Systems",
    "Design Patterns"
  ],
  "textContent": "No matter which language you're writing your service in, it's generally a good idea to\nseparate your external dependencies from your business-domain logic. Let's say your _order\nservice_ needs to make an RPC call to an external _payment service_ like Stripe when a\ncustomer places an order.\n\nUsually in Go, people make a package called external or http and stash the logic of\ncommunicating with external services there. Then the business logic depends on the\nexternal package to invoke the RPC call. This is already better than directly making RPC\ncalls inside your service functions, as that would make these two separate concerns\n(business logic and external-service wrangling) tightly coupled. Testing these concerns in\nisolation, therefore, would be a lot harder.\n\nWhile this is a fairly common practice, I was looking for a canonical name for this pattern\nto talk about it in a less hand-wavy way. Turns out [Martin Fowler wrote a blog post] on it\na few moons ago, and he calls it the _Gateway pattern_. He explores the philosophy in more\ndetail and gives some examples in JS. However, I thought that Gophers could benefit from a\nfew examples to showcase how it translates to Go. Plus, I wanted to reify the following\naxiom:\n\n> High-level modules should not depend on low-level modules. Both should depend on\n> abstractions. Abstractions should not depend on details. Details should depend on\n> abstractions.\n>\n> -- Dependency inversion principle (D in SOLID), Uncle Bob\n\nIn this scenario, our business logic in the order package is the _high-level module_ and\nexternal is the _low-level module_, as the latter concerns itself with transport details.\nInside external, we could communicate with the external dependencies via either HTTP or\ngRPC. But that's an implementation detail and shouldn't make any difference to the\nhigh-level order package.\n\norder will communicate with external via a common interface. This is how we satisfy the\n_\"both should depend on abstractions\"_ part of the ethos.\n\nOur app layout looks like this:\n\nLet's walk through the flow from the bottom up. Think about walking back from the edge to\nthe core, as in [Alistair Cockburn's Hexagonal Architecture] lingo where _edge_ represents\nthe transport logic and _core_ implies the business concerns.\n\nThe Stripe implementation lives in external/stripe/gateway.go. For simplicity's sake,\nwe're pretending to call the Stripe API over HTTP, but this could be a gRPC call to another\nservice.\n\nNotice that the stripe package handles the details of communicating with the Stripe\nendpoint, but it doesn't export any interface for the higher-level module to use. This is\nintentional.\n\nIn Go, the general advice is that the [consumer should define the interface they want, not\nthe provider].\n\n> Go interfaces generally belong in the package that uses values of the interface type, not\n> the package that implements those values.\n>\n> -- Go code review comments\n\nThat gives the consumer full control over what it wants to depend on, and nothing more. You\ndon't accidentally couple your code to a bloated interface just because the implementation\nprovided one. You define exactly the shape you need and mock that in your tests.\n\n> Clients should not be forced to depend on methods they do not use.\n>\n> -- Interface segregation principle (I in SOLID), Uncle Bob\n\nSo, in the order package, we define a tiny private interface that reflects the use case.\n\nThe order service doesn't know or care which implementation of the gateway it's using to\nperform some action. It just knows it can call Charge on the provided gateway type. It\ndoesn't need to care about the Refund method on the Stripe gateway implementation. Also,\nthe paymentGateway interface is bound to the order package, so we're not polluting the\nAPI surface with a bunch of tiny interfaces.\n\nNow, when testing the service logic, you just need to write a tiny mock implementation of\npaymentGateway and pass it to order.Service. You don't need to reach into the\nexternal/stripe package or wire up anything complicated. You can place the fake right next\nto your service test. Since interface implementations in Go are implicitly satisfied,\neverything just works without much fuss.\n\nThe test is focused only on what matters: Does the service call Charge with the correct\narguments? We're not testing Stripe here. That's its own concern.\n\nYou can still write tests for the Stripe client if you want. You'd do that in\nexternal/stripe/gateway_test.go.\n\nFinally, everything is wired together in cmd/main.go.\n\n---\n\nIt's also common to call gateways _\"client.\"_ Some people prefer that name. However, I think\n_client_ is way overloaded, which makes it hard to discuss the pattern clearly. There's the\nHTTP client, the gRPC client, and then your own client that wraps these. It gets confusing\nfast. I prefer _\"gateway,\"_ as Martin Fowler used in his original text.\n\nIn Go context, the core idea is that a service function uses a locally defined gateway\ninterface to communicate with external gateway providers. This way, the service and the\nexternal providers are unaware of each other's existence and can be tested independently.\n\n\n\n\n[martin fowler wrote a blog post]:\n    https://martinfowler.com/articles/gateway-pattern.html\n\n[alistair cockburn's hexagonal architecture]:\n    https://alistair.cockburn.us/hexagonal-architecture\n\n[consumer should define the interface they want, not the provider]:\n    https://go.dev/wiki/CodeReviewComments#interfaces",
  "title": "Gateway pattern for external service calls"
}