Backend7 min read

Software Architecture Fundamentals

Design scalable systems - layered architecture, hexagonal design, CAP theorem, consistency patterns, and architectural decision records.

Software ArchitectureSystem DesignPatternsClean CodeScalability

skill: Software Architecture Fundamentals category: Backend tags: [Software Architecture, System Design, Patterns, Clean Code, Scalability] readingTime: 7

Good architecture reduces the cost of change. Every architectural decision trades something - consistency for availability, simplicity for flexibility, coupling for cohesion. Name your trade-offs explicitly (in an ADR), choose deliberately, and resist the urge to add complexity before it's proven necessary.

Workflow

  1. Apply hexagonal architecture (ports and adapters)Hexagonal architecture isolates business logic from infrastructure - your domain code doesn't know whether it's talking to a database, an API, or a test double.
  2. Understand CAP theorem trade-offsDistributed systems can guarantee only two of three properties: Consistency, Availability, Partition Tolerance. Choosing a database means choosing which trade-off to accept.
  3. Design for the right level of couplingTight coupling makes systems hard to change. Loose coupling enables independent deployment, testing, and evolution. Know the difference between good and bad coupling.
  4. Write Architecture Decision RecordsADRs document architectural decisions with context and consequences - future team members understand why the system is the way it is, not just what it is.
  5. Apply scalability patternsUnderstand how to scale before you need to - adding scalability after the fact is expensive. Know which bottleneck you're solving before applying a pattern.

Output Format

Architecture: hexagonal ports/adapters, CAP theorem trade-off selection, interface-based loose coupling, ADR documentation, and scalability patterns (caching, CQRS).

Domain: pure logic, no imports of infrastructure
Ports: interfaces (OrderRepository, PaymentGateway)
Adapters: PostgresOrderRepository implements OrderRepository
ADR: context + decision + positive/negative consequences
Scaling: identify bottleneck → apply caching/CQRS/sharding

Examples

Input

Output

Edge Cases & Gotchas

  • Over-engineering small systems - Hexagonal architecture, CQRS, and event sourcing add complexity. A startup's initial product doesn't need all three - start with a monolith and extract when pain is felt, not anticipated.
  • CAP in practice - Most databases offer tunable consistency - PostgreSQL with synchronous replicas is strongly consistent; with async replicas, it's eventually consistent. Read your database's documentation on consistency guarantees.
  • ADR archaeology - Unmaintained ADRs mislead more than help. Mark outdated ADRs as "Superseded by ADR 042" rather than deleting them - history matters.
  • Distributed monolith anti-pattern - Microservices with synchronous HTTP calls between every service create a distributed monolith: all the operational complexity of microservices without the independence. Use async events between services for true decoupling.

When to Break These Rules

  • Use a simple layered architecture (controller → service → repository) for CRUD applications - hexagonal architecture adds indirection without proportional benefit for simple domains.
  • Use event sourcing only when audit history is a core requirement - the complexity of replaying events is not justified for typical applications that just need current state.

Reference Files

  • https://herbertograca.com/2017/09/14/ports-adapters-architecture/
  • https://adr.github.io/

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.