Architecture · 7 MIN READ
Choose a Go architecture by the change it makes easier
A practical path from one executable to explicit modules, ports and independent services. Compare guarantees, not diagrams.
Start with the constraint
An architecture decision should name the change it makes cheaper. A small command-line tool needs straightforward control flow and a reliable exit status. A domain-rich application needs clear invariants and ownership. Independently deployed services need stable remote contracts and a plan for partial failure. These are different goals; one universal ranking hides them.
Write down the workload, correctness requirement, team ownership and current pain before choosing a style. If a single team deploys everything together, splitting the system into many remote services adds operational boundaries before it creates organizational independence.
One deployment can have strong boundaries
A modular monolith lets billing and shipping expose small capability APIs while keeping local calls and a coordinated release. It can use ports and adapters internally, and it can separate command and query models. Those ideas do not require separate databases or a message broker.
The useful test is whether shipping can access billing only through the agreed contract. A shared models package with mutable records can undo the boundary even when the folder tree looks perfect. Go’s internal directory restricts importers, but sibling packages still need explicit ownership and a small exported surface.
A remote boundary creates new failure modes
A local function either returns or fails within one process. A remote request can time out after the operation succeeds. Retrying it safely requires an operation identifier, idempotency and reconciliation. Database transactions no longer make a remote payment and a local write atomic.
Before extracting a service, decide who owns its data, how old clients remain compatible, what the deadline means and how an operator diagnoses failure. Independent scaling is valuable when it solves a measured bottleneck. It is a poor reason to distribute a tiny application whose main bottleneck is one missing database index.
Make the decision reversible
Capture an architecture decision in a short record: context, alternatives, chosen tradeoff, consequences and evidence that would reopen it. Keep a seam around the capability you might extract. Move reads behind that contract before moving data ownership. For writes, keep one authoritative path so a comparison rollout cannot charge or reserve twice.
Use the architecture comparison in the course to explore three scenarios. Its scores are editorial suitability guidance, not benchmarks. Read the costs and failure sections beside the score; styles such as DDD, CQRS and deployment topology can be combined.
A review you can do today
Trace one request from input to durable effect. Identify validation, authorization, transaction ownership, timeout propagation and final error reporting. Then ask which change would currently touch the most unrelated packages. Improve that seam first.
A useful architecture makes a business rule testable without HTTP and makes a database adapter testable without inventing another business system. If the current approach already does that at a reasonable cost, keeping it is a valid design decision.