Software Architecture Patterns

Software architecture defines the structural boundaries and communication paths of a system. Choosing a pattern is a trade-off between developer velocity, system maintainability, and operational complexity.

1. Layered (N-Tier) Architecture

The most common traditional pattern, where components are organized into horizontal layers. Each layer has a specific responsibility and typically only communicates with the layer immediately below it.

I/O Flow Diagram (Prose)

  1. Request Input: User sends an HTTP request to the Presentation Layer (Controller).
  2. Downward Call: The Controller translates the request into a method call on the Business Layer (Service).
  3. Transitive Dependency: The Service performs business logic and calls the Data Access Layer (Repository).
  4. Physical I/O: The Repository executes a SQL query against the Database.
  5. Upward Response: Data flows back up the stack (DB → Repo → Service → Controller → User).

Key Characteristic: High coupling. The business logic depends directly on the database implementation. If you change the database, the Service layer often requires modification.

2. Hexagonal (Ports & Adapters)

This pattern inverts the dependencies of the Layered architecture. The "Core" (Business Logic) sits at the center and defines Ports (interfaces) for everything external.

I/O Flow Diagram (Prose)

  1. Primary Actor: A user (or another system) interacts with a Driving Adapter (e.g., a REST API or CLI).
  2. Inward Translation: The Adapter converts the external signal into a call to a Driving Port (Interface implemented by the Core).
  3. Core Execution: The Core logic executes using only internal entities.
  4. Outward Port Call: When the Core needs data, it calls a Driven Port (an interface it defines, e.g., UserRepository).
  5. Secondary Adapter: A Driven Adapter (e.g., PostgreSQLAdapter) implements that port, performing the actual physical I/O.

Key Characteristic: Dependency Inversion. The Core depends on nothing external. The Database depends on the Core's interface.

3. Event-Driven Architecture (EDA)

EDA moves away from synchronous method calls toward asynchronous message passing. Components communicate by emitting and consuming events.

I/O Flow Diagram (Prose)

  1. Event Source: Service A performs a local change and emits a UserRegistered event to an Event Broker (e.g., Kafka).
  2. Decoupled Buffer: The Broker stores the event in an append-only log. Service A is now finished; it does not wait for a response.
  3. Asynchronous Consumption: Service B (Emailer) and Service C (Analytics) independently poll the Broker.
  4. Independent Action: Service B sees the event and sends an email. Service C sees the event and updates a dashboard.
  5. State Consistency: The system reaches EventualConsistency as all consumers process the event log.

Key Characteristic: Temporal and Spatial Decoupling. Producers and consumers do not need to exist at the same time or know each other's location.

Comparative Summary

PatternDependency DirectionPrimary BenefitMain Drawback
LayeredTop → DownSimple to understandHard to test without DB
HexagonalOutside → InsideTestable, Tech-agnosticHigh boilerplate
Event-DrivenTo/From BrokerMassive ScalabilityDebugging/Tracing complexity

Further Reading