Chapter 3
Software Architecture and Architectural Decision-Making
Bachelors Program in Computer Science Engineering
Advanced Software Development
•
Bachelor
Software Architecture and Architectural Decision-Making
Introduction
Software architecture is the set of significant structures of a software system, the relationships among those structures, and the principles that guide their design and evolution. It is concerned not only with components and interfaces, but also with deployment, data ownership, communication, security, operational behavior, and the decisions that constrain future change.
Architecture matters because software systems rarely fail only because of incorrect algorithms. They fail because they cannot scale, are too difficult to modify, expose sensitive data, depend on unreliable services, or become impossible to operate. Architectural design therefore connects technical structure with business goals and quality attributes such as performance, availability, security, maintainability, and usability.
This chapter develops practical methods for designing and evaluating layered, hexagonal, event-driven, service-oriented, and microservice architectures. It also explains quality-attribute scenarios, trade-off analysis, architecture views, architecture decision records, and fitness functions.
Learning Objectives
By the end of this chapter, you should be able to:
Explain the role and scope of software architecture.
Compare layered, hexagonal, event-driven, service-oriented, and microservice architectures.
Convert vague quality requirements into measurable quality-attribute scenarios.
Analyze architectural trade-offs using structured reasoning.
Select appropriate architecture views for different stakeholders.
Write and evaluate architecture decision records.
Design fitness functions that continuously test architectural properties.
Recommend an architecture for a realistic software system and justify the recommendation.
1. Architecture as a Set of Decisions
Architecture is often confused with a diagram. A diagram is a representation of architecture; it is not the architecture itself. Architecture includes decisions about:
decomposition into modules, components, and services;
responsibilities and boundaries;
communication protocols and interaction styles;
data ownership and consistency;
deployment topology;
security controls and trust boundaries;
availability, scaling, and failure handling;
technology choices that significantly constrain future development.
An architectural decision is significant when reversing it would be expensive, risky, or disruptive. Choosing a programming language for a small internal utility may not be architecturally significant. Choosing a shared database for ten independently deployed services usually is.
Architecture is also a socio-technical activity. Teams, governance, skills, budgets, regulatory obligations, and operational maturity influence which designs are feasible. A theoretically elegant architecture may be inappropriate if an organization lacks the skills to monitor, deploy, and secure it.
Architecture and design
The boundary between architecture and detailed design is gradual rather than absolute. Architecture establishes major structures and constraints; design elaborates their internal behavior. For example, deciding that payment processing is an independently deployable service is architectural. Choosing the class names inside that service is normally detailed design. The distinction depends on system scale and consequences.
Architecture principles
Architecture principles are durable guidelines that shape decisions. Examples include:
business rules must not depend on user-interface frameworks;
every service owns its persistent data;
sensitive information must be encrypted in transit and at rest;
ability to infer internal state from external evidence
Deployability
speed and safety of releasing changes
Terms such as “high performance” or “secure” are too vague to guide architecture. A quality-attribute scenario makes a requirement testable by specifying stimulus, context, response, and response measure.
A useful scenario contains:
Source of stimulus: who or what initiates the event.
Stimulus: the event or condition.
Environment: the operating context.
Artifact: the system element affected.
Response: what the architecture should do.
Response measure: how success is evaluated.
For example:
During normal operation, when 2,000 users submit search requests per second, the search subsystem shall return results for at least 99% of requests within 300 milliseconds, measured at the API boundary.
This scenario is useful because it identifies demand, context, responsibility, and a measurable target. A security scenario might state:
When an authenticated user attempts to access another user’s invoice, the authorization component shall deny access, record the event, and expose no invoice data within 100 milliseconds.
Quality attributes interact. Replication may improve availability but increase consistency complexity. Caching may improve performance but create stale-data risks. Independent deployment may improve modifiability but increase operational complexity. Architecture is therefore an exercise in balancing competing objectives.
3. Architectural Styles and Patterns
An architectural style provides recurring structural constraints. A pattern is a reusable solution to a recurring design problem within a context. Styles are not complete solutions; they must be adapted to requirements.
3.1 Layered architecture
Layered architecture organizes responsibilities into horizontal layers. A common arrangement is:
presentation or API layer;
application or use-case layer;
domain layer;
infrastructure and persistence layer.
A strict layered design allows a layer to call only the layer immediately below it. A relaxed design permits calls across several lower layers, although this can weaken boundaries.
Layered architecture with presentation, application, domain, and infrastructure layers
Figure: Layered architecture with presentation, application, domain, and infrastructure layers
Figure: A conventional layered architecture and its principal dependency direction.
Strengths include conceptual simplicity, clear responsibility separation, and suitability for conventional business applications. Weaknesses include accidental coupling, performance overhead across layers, and the tendency for business logic to become concentrated in application or database code.
Layering is appropriate when the system has stable business boundaries, a relatively unified deployment model, and a team that benefits from straightforward structure. It is less suitable when independent scaling, highly variable workloads, or asynchronous workflows dominate.
3.2 Hexagonal architecture
Hexagonal architecture, also called ports-and-adapters architecture, places the application core at the center. The core contains domain rules and use cases. It communicates through ports, which are interfaces representing required or provided capabilities. Adapters connect those ports to databases, HTTP clients, message brokers, command-line interfaces, or user interfaces.
The key dependency rule is that dependencies point inward toward the core. A database adapter depends on a persistence port defined by the application, rather than the domain depending directly on a database library.
This architecture supports testability and replaceability. For example, a payment gateway can be replaced by a test double without changing the payment rules. However, hexagonal architecture does not automatically solve distributed-systems problems. It can still be implemented as a monolith, and it may introduce additional interfaces and mapping code.
3.3 Event-driven architecture
In an event-driven architecture, components communicate by producing and consuming events. An event records that something has happened, such as OrderPlaced or ShipmentDispatched. Producers do not necessarily know which consumers exist.
Important concepts include:
event producer: publishes an event;
event consumer: processes an event;
broker: transports and sometimes stores events;
consumer group: coordinates parallel consumers;
event schema: defines the event structure and compatibility rules;
replay: reprocesses retained events;
dead-letter channel: stores messages that repeatedly fail.
Event-driven systems support loose temporal coupling, independent scaling, and auditability. They also create challenges: eventual consistency, duplicate delivery, ordering, schema evolution, debugging, and more complex failure handling.
Consumers should generally be idempotent: processing the same event more than once should not produce an incorrect additional effect. This can be achieved with unique event identifiers, deduplication records, or transactional state changes.
3.4 Service-oriented architecture
Service-oriented architecture (SOA) structures functionality as network-accessible services with explicit contracts. Services may be relatively coarse-grained and can be governed through enterprise standards, shared identity, registries, and integration infrastructure.
SOA is broader than a particular protocol. SOAP and enterprise service buses are historical technologies associated with some SOA implementations, but REST, messaging, and other protocols can also support service orientation.
SOA is useful where multiple systems must integrate across organizational or technological boundaries. Its risks include centralized governance, excessive middleware, large shared services, and contracts that evolve slowly.
3.5 Microservice architecture
Microservices are a specific approach in which a system is composed of small, independently deployable services aligned with business capabilities. A genuine microservice architecture usually includes:
autonomous ownership by a team;
independent deployment;
explicit service contracts;
bounded data ownership;
decentralized implementation choices within governance limits;
operational capability for monitoring, deployment, and recovery.
Microservices can improve team autonomy, scaling independence, and fault isolation. They also introduce network latency, distributed transactions, operational overhead, contract management, and difficult testing. A distributed monolith results when services must always be deployed together, share databases, or make long synchronous call chains.
Microservices are not an automatic upgrade from a modular monolith. A well-structured monolith may be the safer starting point, especially when domain boundaries are uncertain or the organization has limited operational maturity.
4. Comparing Architectural Choices
Style
Main strength
Main risk
Suitable context
Layered
simplicity and familiarity
coupling across layers
conventional business systems
Hexagonal
testability and dependency inversion
extra abstractions
domain-rich applications
Event-driven
decoupling and asynchronous scale
eventual consistency and debugging difficulty
workflows and integration
SOA
enterprise integration
governance and middleware complexity
heterogeneous organizations
Microservices
team autonomy and independent deployment
distributed-systems complexity
mature product organizations with strong boundaries
These styles can be combined. A microservice may use hexagonal internals; a layered monolith may publish domain events; an SOA environment may include event-driven integration. Architectural labels are less important than the constraints actually enforced.
5. Trade-Off Analysis
Architectural trade-off analysis begins with quality priorities rather than fashionable technologies. A practical process is:
Identify business drivers and constraints.
Define important quality-attribute scenarios.
Propose two or more architectural options.
Identify risks, benefits, and sensitivities.
Evaluate options with scenarios, prototypes, and evidence.
Record the decision and its assumptions.
Revisit the decision when evidence changes.
A simple weighted scoring model can structure discussion. Let Si be the score of option i, wj the importance weight of criterion j, and rij the rating of option i on that criterion. If weights and ratings are normalized, the score is:
Si = ∑j=1n wj rij
The weights satisfy wj ≥ 0 and ∑wj = 1. Ratings must use the same scale for all options. The result is a decision aid, not an objective truth: scores depend on assumptions and can hide unacceptable weaknesses. A security failure, for example, may be a veto rather than a low weighted score.
Worked comparison
Suppose a team compares a modular monolith with microservices using four criteria. The weights are performance 0.20, modifiability 0.30, operational simplicity 0.25, and team autonomy 0.25. Ratings use a scale from 1 (poor) to 5 (excellent).
Under these assumptions, the modular monolith scores higher. The calculation does not prove that it is universally better. If independent scaling becomes dominant or multiple autonomous teams are added, the weights and ratings may change.
6. Architecture Views and Documentation
No single diagram can communicate architecture to every stakeholder. A view is a representation of a particular set of concerns. A viewpoint defines the conventions used to create such views.
Useful views include:
context view: system, users, external systems, and major relationships;
logical or module view: responsibilities and dependencies among code structures;
component-and-connector view: runtime components and communication paths;
deployment view: processes, containers, nodes, networks, and regions;
data view: ownership, flows, schemas, and consistency boundaries;
security view: trust zones, identities, permissions, and controls;
operational view: monitoring, alerting, recovery, and scaling mechanisms.
The 4+1 view model is a well-known organizing approach: logical, process, development, and physical views are connected by scenarios. Other approaches, including the “C4” model, use context, container, component, and code diagrams. These are complementary documentation techniques rather than competing laws.
Good architecture documentation states scope, assumptions, decisions, interfaces, quality targets, and known risks. It should be version-controlled and updated as part of engineering work. A diagram without a legend, ownership, or explanation of important constraints quickly becomes misleading.
7. Architecture Decision Records
An Architecture Decision Record (ADR) captures a significant decision in a short, durable format. A useful ADR contains:
title and identifier;
status, such as proposed, accepted, superseded, or rejected;
date and decision owners;
context and problem;
decision drivers;
considered options;
selected decision;
consequences, including disadvantages;
validation plan and review conditions.
Illustrative ADR
Title: Use asynchronous events for shipment-status notifications
Status: Accepted
Context: The order system must notify customers and analytics services when shipment status changes. Notification consumers have different availability and scaling characteristics.
Decision drivers: loose coupling, reliable delivery, independent scaling, and auditability.
Options considered: direct synchronous calls, a shared database polling process, and a durable event broker.
Decision: publish versioned shipment-status events through a durable broker. Consumers acknowledge processing and use event identifiers for deduplication.
Consequences: consumers can scale independently and temporary outages can be absorbed. The system must handle eventual consistency, duplicate delivery, schema compatibility, monitoring, and replay.
Review condition: reconsider if notification latency must be lower than the broker and consumer path can guarantee, or if operational support for the broker becomes inadequate.
The ADR records reasoning, not merely the chosen technology. This prevents “decision amnesia,” in which future developers see a constraint but not the assumptions behind it.
8. Fitness Functions
A fitness function is an automated evaluation that indicates whether an architecture remains within acceptable boundaries. The term is associated with evolutionary architecture, where systems are allowed to evolve while automated checks protect important properties.
Examples include:
an architecture test that prevents the domain package from importing infrastructure code;
a dependency check that rejects forbidden service-to-service calls;
a performance test requiring the 95th-percentile response time to remain below a target;
a deployment test verifying that a service can be released independently;
a security scan that blocks critical vulnerabilities;
a resilience test that verifies recovery after a dependency failure.
For percentile latency, the 95th percentile is the value below which 95% of measured requests fall. It should not be confused with an average. A fitness function must specify measurement method, workload, environment, threshold, and response when it fails.
A useful fitness function has these properties:
Relevant: connected to an important quality scenario.
Automated: repeatable in a pipeline or operational system.
Actionable: failure identifies a clear corrective response.
Proportionate: its maintenance cost is justified by its value.
Fitness functions can be static, such as dependency rules, or dynamic, such as load and chaos tests. They do not replace human judgment. A system may pass all automated checks while failing to meet an unmeasured business or ethical requirement.
9. Applied Case: A University Course-Registration Platform
The following is an illustrative case.
Background
A university operates a course-registration platform. Students search courses, enroll, drop courses, and view schedules. Registration opens at a published time, creating a sharp demand spike. The platform integrates with identity management, tuition billing, room scheduling, and notification systems.
Problem
The existing application is a tightly coupled monolith with a shared database. During registration openings, search traffic overwhelms enrollment transactions. Changes to billing require full-system deployments. A failure in notifications sometimes prevents enrollment because the enrollment transaction performs a synchronous notification call.
Important quality scenarios are:
At registration opening, 10,000 concurrent students may search courses; 99% of searches should complete within 500 milliseconds.
When an enrollment request is accepted, the student’s seat allocation must be durable and must not exceed course capacity.
If notification is unavailable, enrollment must still complete and the notification must be retried.
A billing change should be deployable without redeploying course search.
Unauthorized users must not view another student’s schedule.
Analysis
The system has at least three distinct concerns: read-heavy course search, consistency-sensitive enrollment, and asynchronous notification. A complete microservice decomposition would increase operational risk if introduced before boundaries and observability are understood. A modular monolith with explicit modules and asynchronous integration is a safer transitional architecture.
The proposed modules are Course Catalog, Enrollment, Billing Integration, Identity, and Notification. The Enrollment module owns enrollment rules and seat allocation. Course search may use a read-optimized index derived from catalog data. Enrollment emits an EnrollmentAccepted event after the durable enrollment transaction completes. Notification consumes the event and retries failures.
Approach and solution
The architecture uses:
a layered internal structure for each module;
hexagonal boundaries around domain logic and external adapters;
a transactional database boundary for enrollment decisions;
an event broker for notifications and analytics;
centralized authentication with module-level authorization;
cache or search indexing for course discovery;
distributed tracing and correlation identifiers;
ADRs documenting the choice to delay independent deployment.
The enrollment operation must protect the invariant that allocated seats do not exceed capacity. A transaction can lock or otherwise coordinate the relevant course record, verify capacity, create the enrollment, decrement availability, and commit atomically. The exact mechanism depends on the database and isolation model. The notification event should be published reliably, commonly through an outbox pattern: the enrollment transaction writes both business state and an outbox record, and a separate publisher delivers the event.
Outcome
Search traffic can scale independently at the read layer, while enrollment remains within a carefully controlled consistency boundary. Notification outages no longer block enrollment. The team gains clear module boundaries and operational evidence before deciding whether particular modules should become independently deployed services.
Learning points
The strongest architecture is not necessarily the most distributed.
Quality scenarios expose which parts require strong consistency and which tolerate delay.
Events are valuable when they represent meaningful business facts, not merely every internal method call.
A modular monolith can be an intentional evolutionary stage.
Architecture must include operations, security, and recovery—not only code structure.
10. Governance, Ethics, and Contemporary Practice
Architectural governance should guide rather than paralyze teams. Lightweight review is appropriate for routine changes; deeper review is justified for decisions involving personal data, safety, regulatory exposure, major availability risks, or irreversible vendor dependence.
Privacy should be designed into architecture through data minimization, purpose limitation, retention controls, access logging, and appropriate deletion mechanisms. Service decomposition can increase the number of places where personal data is copied, so event payloads should contain only necessary information. Encryption, key management, least privilege, and secure software supply-chain practices are architectural concerns.
AI-enabled components introduce additional concerns. A system using a language model or prediction service should specify data-use boundaries, model failure handling, human oversight, auditability, and fallback behavior. AI should not be treated as a substitute for quality requirements or threat modeling. Model calls also create latency, cost, availability, and privacy dependencies.
Contemporary architecture increasingly emphasizes platform engineering, internal developer platforms, observability, cloud-native deployment, and sustainable computing. These trends are useful when they address real constraints. They should not turn architectural design into technology selection by fashion.
11. Common Failure Modes and Best Practices
Common failures include:
choosing microservices before identifying business boundaries;
treating a diagram as complete documentation;
using synchronous calls for every interaction;
sharing databases among supposedly autonomous services;
ignoring retries, timeouts, idempotency, and partial failure;
recording only decisions, not rejected alternatives or consequences;
measuring average performance while ignoring tail latency;
creating fitness functions that are easy to pass but unrelated to user value;
allowing architecture principles to become untestable bureaucracy.
Best practices are to begin with quality scenarios, prefer the simplest architecture that satisfies current needs, make boundaries explicit, automate architectural checks, document significant trade-offs, test failure behavior, and revisit assumptions using production evidence.
Chapter Summary
Software architecture is the set of significant structures, relationships, constraints, and decisions that shape a system’s behavior and evolution. Layered architecture emphasizes organized responsibility; hexagonal architecture protects a domain core through ports and adapters; event-driven architecture supports asynchronous collaboration; SOA supports enterprise integration; and microservices support independent deployment when boundaries and operational capability are mature.
Quality-attribute scenarios turn vague goals into measurable stimuli, responses, and response measures. Architectural choices should be compared through explicit drivers, trade-offs, evidence, and—where useful—weighted scoring. Architecture views communicate different concerns to different stakeholders. ADRs preserve decision context and consequences, while fitness functions continuously detect architectural erosion.
A responsible architect balances technical quality with organizational capability, security, privacy, regulatory obligations, operational cost, and future uncertainty. In many situations, an evolvable modular monolith is a better starting point than premature distribution.
Key Terms
Architecture: Significant structures, relationships, constraints, and decisions shaping a software system.
Architectural style: A recurring set of structural constraints for organizing a system.
Quality attribute: A property describing how well a system performs a function.
Quality-attribute scenario: A measurable statement of stimulus, context, response, and response measure.
Layered architecture: An organization of responsibilities into horizontal layers with controlled dependencies.
Hexagonal architecture: An architecture that isolates application logic behind ports and adapters.
Port: An interface through which an application provides or requires behavior.
Adapter: A component that connects an application port to an external technology or actor.
Event-driven architecture: An architecture in which components communicate by publishing and consuming events.
Eventual consistency: A condition in which replicas may temporarily differ but converge if updates cease.
Service-oriented architecture: An approach based on explicit, network-accessible services.
Microservice: An independently deployable service aligned with a business capability.
Bounded context: A boundary within which a domain model and terminology have specific meaning.
Architecture view: A representation of architecture focused on particular stakeholder concerns.
Architecture Decision Record: A concise record of a significant architectural decision and its rationale.
Fitness function: An automated check that evaluates whether an architectural property remains acceptable.
Idempotency: The property that repeated processing produces the same intended effect as one processing.
Observability: The ability to understand internal system behavior from emitted evidence.
Trade-off: A gain in one concern obtained at the cost of another concern.
Distributed monolith: A distributed system whose components remain tightly coupled in deployment or runtime behavior.
Review Questions
Short Answer Questions
What distinguishes an architectural decision from a routine implementation decision?
List the six elements of a quality-attribute scenario.
What is the dependency rule in hexagonal architecture?
Why must event consumers commonly be idempotent?
What information should an Architecture Decision Record preserve?
Long Answer Questions
Compare layered, hexagonal, and microservice architectures in terms of structure, strengths, and risks.
Explain how event-driven architecture changes consistency, failure handling, and observability.
Describe a systematic process for evaluating competing architectural options.
Explain the purpose of architecture views and compare context, deployment, and data views.
Discuss how fitness functions support evolutionary architecture and identify their limitations.
Analytical / Application Questions
A small company has one team, uncertain domain boundaries, and no production monitoring. Recommend an initial architecture and justify it using quality attributes and organizational constraints.
Design three quality-attribute scenarios for an online banking transfer system, including at least one security and one availability scenario.
For the university registration case, identify two architectural decisions, one likely trade-off for each, and one fitness function that could detect architectural erosion.
Multiple Choice Questions with Answers
Question 1
Which statement best describes a quality-attribute scenario?
A. A list of programming languages permitted in a project
B. A measurable description of stimulus, context, response, and response measure
C. A diagram showing only database tables
D. A record of individual developer tasks
Correct Answer: B. A measurable description of stimulus, context, response, and response measure
Question 2
What is the principal dependency direction in hexagonal architecture?
A. From domain logic toward database libraries
B. From infrastructure toward user-interface code
C. From external adapters toward the application core
D. From the database directly toward all consumers
Correct Answer: C. From external adapters toward the application core
Question 3
Which property is especially important for event consumers?
A. Idempotency
B. Tight coupling to every producer
C. Synchronous execution in all cases
D. Shared ownership of every database
Correct Answer: A. Idempotency
Question 4
Which is a common risk of microservice architecture?
A. It makes network communication unnecessary
B. It eliminates the need for monitoring
C. It guarantees strong consistency automatically
D. It introduces distributed-systems and operational complexity
Correct Answer: D. It introduces distributed-systems and operational complexity
Question 5
What is the main purpose of an Architecture Decision Record?
A. To replace all source-code documentation
B. To preserve the context, rationale, and consequences of a significant decision
C. To specify individual variable names
D. To guarantee that a decision can never be changed
Correct Answer: B. To preserve the context, rationale, and consequences of a significant decision
Question 6
A fitness function is best described as:
A. An automated evaluation of an architectural property
B. A manual employee performance review
C. A database backup procedure
D. A user-interface color standard
Correct Answer: A. An automated evaluation of an architectural property
Question 7
Using the weighted scoring formula, an option has ratings 4 and 2 with weights 0.6 and 0.4. What is its score?
A. 2.0
B. 2.4
C. 3.2
D. 4.6
Correct Answer: C. 3.2
Question 8
Which architecture view primarily shows nodes, processes, containers, networks, and deployment locations?
A. Deployment view
B. Context view
C. Domain glossary
D. Source-code view
Correct Answer: A. Deployment view
Question 9
In the illustrative registration case, why is an outbox pattern useful?
A. It removes the need for transactions
B. It reliably connects a committed business change with event publication
C. It forces all consumers to use synchronous calls
D. It guarantees that events can never be duplicated
Correct Answer: B. It reliably connects a committed business change with event publication
Question 10
Which situation most strongly suggests a distributed monolith?
A. Services have independent deployment and data ownership
B. A modular monolith has explicit internal boundaries
C. Services must be deployed together and share a database
D. A consumer processes retained events asynchronously
Correct Answer: C. Services must be deployed together and share a database