Chapter 9. Foundations

Background before the individual styles: the fundamental patterns architecture evolved through, and the hard realities (fallacies and extra challenges) that distributed architectures introduce over monolithic ones.

Fundamental patterns

  • Big Ball of Mud — the absence of any discernible structure, named after the 1997 Foote & Yoder anti-pattern paper. A haphazard, sprawling, spaghetti-code system with information shared promiscuously and structure eroded beyond recognition. It makes change increasingly hard and suffers in deployment, testability, scalability, and performance. Few architects intend to create one; many projects drift into it through lack of governance around code quality and structure.
  • Unitary architecture — the original state where software and hardware were a single entity (early mainframes), gradually splitting as needs grew (e.g. separating data into its own system).
  • Client/server (two-tier) — separates technical function between frontend and backend. Variants by era:
    • Desktop + database server — rich desktop UI with data on a standalone DB server reachable over standard protocols; presentation on the desktop, heavy computation on the DB.
    • Browser + web server — even thinner clients (browsers) over web server → DB server; wider distribution inside and outside firewalls.
    • Three-tier — popular in the late 1990s: industrial DB tier, application-server tier, and a generated-HTML/JavaScript frontend.

Monolithic vs distributed: the fallacies of distributed computing

Eight assumptions that quietly break distributed architectures because they all rely on the network:

  1. The network is reliable — it isn’t. Service B can be healthy while Service A can’t reach it, or a request gets no response. This is why timeouts and circuit breakers exist; the more a system leans on the network, the less reliable it potentially becomes.
  2. Latency is zero — a local method call is nanoseconds/microseconds; a remote call (REST, messaging, RPC) is milliseconds. You must know your production average round-trip latency, and especially the 95th–99th percentile — the “long tail” is what kills performance. At ~100 ms/request, chaining 10 service calls adds ~1,000 ms; an average of 60 ms can hide a 95th percentile of 400 ms.
  3. Bandwidth is infinite — irrelevant in a monolith, but breaking a system into services means lots of inter-service chatter. Stamp coupling — a service returning 45 attributes (500 KB) when the caller needs only the name (200 bytes) — at 2,000 requests/second is ~1 Gb of bandwidth for one call out of hundreds. Fixes: private endpoints, field selectors, GraphQL, consumer-driven contracts (CDCs), internal messaging. The principle: pass the minimal data needed.
  4. The network is secure — VPNs and firewalls lull you into forgetting that every endpoint of every deployment unit must be secured. The attack surface grows by magnitudes versus a monolith, and securing every endpoint (even for inter-service calls) is another reason synchronous distributed architectures run slower.
  5. The topology never changes — routers, switches, firewalls, and appliances change constantly. A “minor” 2 a.m. network upgrade can invalidate all your latency assumptions and trigger timeouts/circuit breakers. Stay in constant contact with ops and network admins.
  6. There is only one administrator — large companies have dozens of network admins. Knowing who to talk to about latency or topology changes reflects the coordination overhead distributed architectures demand and monoliths don’t.
  7. Transport cost is zero — not latency, but actual money. Distributed architectures cost significantly more (extra hardware, servers, gateways, firewalls, subnets, proxies). Analyse current capacity, bandwidth, latency, and security zones before committing.
  8. The network is homogeneous — most companies run multiple network hardware vendors, which don’t always integrate seamlessly. Packets occasionally get lost, looping back into fallacies #1, #2, and #3.

Other distributed considerations

  • Distributed logging — a monolith has one log; distributed systems have dozens to hundreds in different places and formats, making root-cause analysis hard. Consolidation tools like Splunk help but only scratch the surface.
  • Distributed transactions — monoliths get ACID commits/rollbacks for free. Distributed systems rely on eventual consistency — the trade-off being high scalability/performance/availability at the cost of data consistency and integrity. Managed via transactional sagas (event sourcing for compensation, or finite state machines) and BASE transactions: Basic availability, Soft state (data in transit / temporarily inconsistent), Eventual consistency. BASE is a technique, not software.
  • Contract maintenance and versioning — a contract is the behaviour and data agreed between client and service. Maintenance is hard because services are decoupled and owned by different teams; version deprecation and its communication models are especially complex.

Source

Mark Richards & Neal Ford, Fundamentals of Software Architecture, Chapter 9.