Architecture and Professional Software EngineeringIntroduction and ContextObject-oriented programming (OOP) provides mechanisms for encapsulation, abstraction, inheritance, and polymorphism. These mechanisms help developers model domain concepts and manage complexity within individual components. However, a large software system cannot be designed successfully through classes alone. It also requires decisions about how components are organized, how responsibilities are distributed, how data and control flow across boundaries, and how teams develop, test, release, and maintain the system. Software architecture is the set of significant structures and decisions that shape a system. It includes modules, components, interfaces, data stores, deployment environments, and the relationships among them. Professional software engineering extends architecture into a disciplined development process involving version control, documentation, package management, code review, automation, security, and technical-debt management. For AI and machine-learning systems, these concerns are especially important. A model may be accurate in an experiment but unreliable in production if data pipelines, APIs, monitoring, reproducibility, privacy, and deployment processes are poorly designed. Architecture therefore connects programming knowledge with organizational and operational success. Learning ObjectivesBy the end of this chapter, students should be able to: 1. Explain the role of software architecture and distinguish architecture from detailed design. 2. Apply layered, MVC, component-based, service-oriented, and hexagonal architectures. 3. Design basic APIs with clear contracts, validation, error handling, and security considerations. 4. Use version control, documentation, package management, code review, and CI/CD effectively. 5. Evaluate reuse, coupling, cohesion, scalability, maintainability, and technical debt. 6. Make architecture decisions using explicit requirements, trade-offs, and evidence. 7. Explain professional, ethical, governance, and team implications of architectural choices. Architecture as a Set of DecisionsArchitecture is not merely a diagram or a particular technology. It is a set of decisions about structure and behavior that are difficult or expensive to change later. Examples include selecting a relational or document database, deciding whether a system is deployed as one application or multiple services, defining public API boundaries, and determining where authentication and authorization occur. A useful distinction is: - Architecture concerns major structures, boundaries, externally visible qualities, and decisions with broad consequences.
- Detailed design concerns the internal implementation of components, such as class relationships, algorithms, and local data structures.
- Implementation is the actual code, configuration, tests, and infrastructure that realize the design.
The distinction is not absolute. A class hierarchy may be a local design decision in one system but an architectural boundary in another. Architecture should therefore be understood in context. Functional and Quality RequirementsFunctional requirements state what a system does: registering users, processing payments, or returning a prediction. Quality requirements describe how well it must operate. Common quality attributes include: - Maintainability: ease of changing and understanding the system.
- Reliability: ability to perform correctly over time.
- Availability: proportion of required time the system is operational.
- Performance: response time, throughput, and resource consumption.
- Scalability: ability to handle increased load by adding or improving resources.
- Security: protection of confidentiality, integrity, and availability.
- Testability: ease of verifying behavior.
- Deployability: ease and safety of releasing changes.
Architectural decisions involve trade-offs. Strong consistency may simplify correctness but reduce availability or scalability. Many small services may permit independent deployment but increase operational complexity. A responsible architect records the requirement, alternatives, decision, evidence, and consequences rather than claiming that one architecture is universally best. Common Architectural StylesLayered ArchitectureLayered architecture organizes a system into horizontal layers, each with a defined responsibility. A common arrangement is: 1. Presentation layer: user interfaces, controllers, and request handling. 2. Application or service layer: use cases, workflows, and transaction coordination. 3. Domain layer: business concepts and rules. 4. Infrastructure or data-access layer: databases, files, messaging, and external systems. A request normally moves from the presentation layer toward the domain and infrastructure layers, while results move back. The principal benefit is separation of concerns. Database code need not be mixed with user-interface code, and business rules can be tested independently. A strict layered design permits a layer to depend only on the layer directly below it. A relaxed design may permit upper layers to access lower layers directly. Strict layering improves clarity but can create repetitive forwarding code. Layers should not become empty wrappers that add no meaningful responsibility. Model–View–ControllerMVC separates an interactive application into: - Model: domain state and business behavior.
- View: representation presented to a user or client.
- Controller: interprets input, invokes application behavior, and selects a response.
For example, a web controller may receive a request to list courses, call an application service, and pass the result to a view or serialize it as JSON. MVC reduces the risk that presentation code will contain all business logic. In practice, frameworks often implement variants such as Model–View–ViewModel, and the boundaries must be defined carefully. A common failure is the “fat controller,” in which controllers perform validation, business rules, database queries, and formatting. Controllers should coordinate rather than become the central location for every responsibility. Component-Based ArchitectureA component is a replaceable unit with a defined interface and an encapsulated implementation. Components may be libraries, modules, plug-ins, or deployable units. Component-based architecture emphasizes: - explicit interfaces;
- information hiding;
- substitutability;
- independent testing;
- controlled dependencies; and
- reuse where it is genuinely beneficial.
A component should expose stable behavior rather than its internal data structures. Dependency injection can provide collaborators from outside, making components easier to test and replace. Reuse is not automatically good: a highly generic component may be harder to understand than a small, focused implementation. Service-Oriented ArchitectureService-oriented architecture (SOA) structures functionality as services that communicate through contracts. A service owns a business capability and provides operations through a protocol such as HTTP, messaging, or another network interface. Services may be deployed independently, but SOA does not require every service to be a microservice. Important service principles include autonomy, explicit contracts, loose coupling, discoverability, and meaningful business capability. Network calls introduce latency, partial failure, serialization costs, versioning concerns, and security requirements. Consequently, splitting a monolith into services without a business or scaling rationale can make a system worse. A service boundary should reflect ownership and change patterns. If two functions always change together and require tightly consistent transactions, separating them may create unnecessary complexity. If a capability has independent scaling, ownership, or release needs, a service boundary may be justified. Hexagonal ArchitectureHexagonal architecture, also called ports-and-adapters architecture, places the application core at the center. The core contains domain and application logic. It communicates with the outside world through ports, which are interfaces expressing what the core needs or offers. Adapters implement those ports for particular technologies. For example, a prediction application might define a `ModelRepository` port. A file-system adapter, database adapter, or cloud-model adapter can implement that interface. The domain does not need to depend directly on a vendor SDK. This is an application of dependency inversion: high-level policy should not depend on low-level mechanisms; both should depend on abstractions. Hexagonal architecture supports automated testing and technology substitution. Its cost is additional interfaces and indirection. It is most valuable when external dependencies are volatile, expensive to invoke, difficult to test, or strategically important. Comparing Architectural StylesThese styles are not mutually exclusive. A system may use layers internally, MVC at its user-facing boundary, components within the domain, services across organizational boundaries, and hexagonal principles to isolate infrastructure. Architecture selection should be based on evidence. A small academic scheduling application with one team may benefit from a modular monolith using layers and ports. A large platform with independently released business capabilities may require services. Relevant evidence includes expected load, team structure, data ownership, deployment frequency, regulatory constraints, failure tolerance, and anticipated change. Useful design heuristics include: - Prefer high cohesion: keep closely related responsibilities together.
- Minimize unnecessary coupling: reduce knowledge that one module has about another.
- Define boundaries around stable concepts and ownership.
- Keep domain rules independent of frameworks when feasible.
- Make dependencies explicit.
- Optimize for the changes the system is likely to experience, not hypothetical change.
- Use the simplest architecture that satisfies current and credible future requirements.
API Design and ContractsAn application programming interface (API) is a contract through which software components communicate. A web API commonly uses HTTP methods such as: - `GET` to retrieve a resource;
- `POST` to create or initiate an operation;
- `PUT` to replace a resource;
- `PATCH` to partially modify a resource; and
- `DELETE` to remove a resource.
An API contract should specify endpoints, request and response representations, authentication, authorization, validation rules, status codes, error formats, versioning policy, rate limits, and idempotency expectations. An operation is idempotent when repeating the same request has the same intended effect as performing it once. `GET` should be safe and idempotent; a carefully designed `PUT` is generally idempotent, while `POST` is not automatically so. For example, a prediction API might define `POST /predictions` with a JSON request containing validated features and a response containing a prediction identifier, result, model version, and timestamp. It should reject malformed or out-of-range values, avoid exposing internal stack traces, and return a stable error structure. A client should not need to understand database tables or internal class names. API Security and ReliabilityAPI security includes authentication, authorization, input validation, transport encryption, secret management, logging, and protection against abuse. Authentication answers “Who are you?” Authorization answers “What may you do?” They must not be conflated. Reliable APIs handle timeouts, retries, duplicate requests, dependency failures, and partial results. Retries should be bounded and used carefully: retrying a non-idempotent operation can create duplicates. Correlation identifiers, structured logs, metrics, and traces support diagnosis. Sensitive personal or health data should be minimized, protected, and retained only as long as justified. Professional Engineering PracticesVersion ControlVersion control records changes to source code and related artifacts. A repository should contain source, tests, configuration templates, documentation, and reproducible build definitions, while excluding secrets, generated binaries, and local environment files. A practical workflow uses small, focused commits with meaningful messages. Branches or trunk-based development may be used, but the chosen approach should support frequent integration. Tags identify releases, and protected branches can require review and automated checks. Merge conflicts are reduced when changes are cohesive and frequently synchronized. Version control is also an accountability and learning mechanism. It helps teams identify when behavior changed, review the rationale, revert faulty releases, and audit important modifications. DocumentationDocumentation should serve users, developers, operators, and maintainers. Important forms include: - a README explaining purpose, setup, and basic use;
- API documentation generated from an explicit contract;
- architecture decision records (ADRs) documenting context, decision, alternatives, and consequences;
- deployment and operational runbooks;
- contribution and coding guidelines; and
- data, model, and security documentation where relevant.
Documentation becomes unreliable when it is separated from development. Keeping documentation near code, generating reference material from specifications, and reviewing it with changes improves accuracy. Package and Dependency ManagementPackages provide reusable libraries and tools. A professional project declares direct dependencies explicitly, records exact or bounded versions, uses a lock file where appropriate, and separates development dependencies from production dependencies. Dependency updates should be tested rather than applied blindly. Risks include vulnerable transitive dependencies, license incompatibility, abandoned packages, supply-chain attacks, and accidental reliance on undocumented behavior. Teams should scan dependencies, monitor advisories, verify package sources, and remove unused libraries. “Not invented here” is not a sufficient reason to rewrite mature functionality, but importing a package also creates maintenance responsibility. Code ReviewCode review is a structured examination of a change before integration. Reviewers should examine correctness, requirements, security, tests, readability, maintainability, performance where relevant, and compatibility. Review comments should focus on the code and provide reasons or alternatives rather than personal criticism. Small changes are easier to review. Automated formatting, static analysis, unit tests, and security checks should handle mechanical issues so that humans can focus on design and judgment. Review is not a substitute for testing, and approval should not become a ceremonial step. Continuous Integration and DeliveryContinuous integration (CI) automatically builds and tests changes as they are integrated. A typical pipeline may perform dependency installation, formatting checks, static analysis, unit tests, integration tests, security scanning, packaging, and artifact publication. Continuous delivery means that the system is kept in a releasable state, usually with automated deployment subject to appropriate approval. Continuous deployment goes further by automatically releasing changes that pass defined controls. These practices require reliable tests, environment configuration, rollback procedures, observability, and secure handling of credentials. A pipeline should fail for meaningful reasons and provide rapid feedback. Tests that are flaky, excessively slow, or unrelated to the change reduce trust in automation. Deployment strategies such as canary releases, blue-green deployment, and feature flags can reduce exposure, but they also add operational complexity. Making Evidence-Based Architecture DecisionsAn architecture decision should begin with a problem, not a fashionable technology. A lightweight decision process is: 1. State the functional and quality requirements. 2. Identify constraints, assumptions, and stakeholders. 3. Define candidate architectures. 4. Compare candidates against explicit criteria. 5. Test important assumptions with prototypes, benchmarks, or experiments. 6. Record the decision and its consequences. 7. Revisit it when evidence or requirements change. A decision matrix can support discussion, but numerical scores are not objective facts. For instance, a team might score alternatives for deployment simplicity, scalability, security, and maintainability. The scores should be justified with evidence and sensitivity analysis. If changing an uncertain assumption reverses the result, the team should investigate that assumption rather than presenting a precise but misleading total. Architecture fitness functions are automated checks that monitor architectural properties. Examples include detecting forbidden dependencies between layers, verifying API compatibility, checking response-time thresholds, or ensuring that services emit required audit events. Such checks turn architectural intentions into observable constraints. Reuse, Coupling, and Technical DebtReuse can reduce development time and defects, but it may introduce dependency risk, inappropriate abstraction, licensing obligations, and upgrade cost. Reuse should be evaluated by comparing total lifecycle costs, not just initial coding effort. Technical debt is a design or implementation compromise that provides a short-term benefit while creating future cost or risk. It can be deliberate, such as releasing a limited prototype to test demand, or accidental, such as duplicated logic and undocumented workarounds. Not all imperfect code is debt; debt implies a future obligation or consequence. A useful management practice is to record the debt, its impact, evidence, owner, and repayment condition. Debt should be prioritized by risk and cost, including security exposure, delivery friction, reliability problems, and difficulty of future change. Refactoring should be connected to a business or engineering outcome rather than treated as cosmetic code rearrangement. Illustrative Case: A University Predictive-Advising PlatformBackgroundThis illustrative case concerns a university building a platform that predicts whether a student may need academic support. Advisors access a web interface, while other university systems submit student information through an API. The model is developed by a data-science team and must be updated periodically. ProblemAn initial prototype placed data loading, feature calculation, model invocation, database access, and HTTP response formatting in one controller. It worked in a demonstration but was difficult to test. The model file was loaded differently in development and production, errors exposed internal details, and no record identified which model version produced a prediction. Student data also required careful access control and retention policies. AnalysisThe most important requirements were traceability, privacy, testability, and safe deployment rather than extreme request volume. A large microservice platform would add complexity without evidence of need. The team selected a modular monolith using layered and hexagonal principles. Approach and SolutionThe presentation layer exposed authenticated endpoints. An application service coordinated the “create prediction” use case. Domain objects represented eligibility rules and prediction metadata. A port named `PredictionModel` isolated model inference, while an adapter implemented it using the chosen machine-learning library. A repository port isolated persistence. The API contract validated input and returned a stable error format. Authorization restricted advisor access to permitted students. The response included a model identifier and timestamp, enabling audit and reproducibility. Tests replaced the model and repository adapters with fakes for fast unit testing; a smaller set of integration tests verified real persistence and inference. The team used a repository with protected branches, required reviews, dependency scanning, and a CI pipeline. A staging deployment preceded production. Model updates were treated as versioned releases, and monitoring tracked latency, error rates, input-quality warnings, and changes in prediction distributions. Human advisors remained responsible for decisions; the prediction was presented as decision support rather than an unquestionable classification. OutcomeThe modular structure reduced test setup and permitted model adapters to change without rewriting application logic. Deployment became repeatable, and audit records improved accountability. The system still required ongoing work: model performance could drift, access permissions needed review, and monitoring could not by itself prove that predictions were fair or educationally appropriate. Learning PointsThe case demonstrates that architecture should follow requirements and risks. Layering clarified responsibilities, hexagonal boundaries isolated volatile technologies, API contracts supported independent clients, and CI/CD reduced release uncertainty. Governance and human oversight were necessary because technical correctness alone did not resolve privacy, fairness, or educational consequences. Challenges, Limitations, and GovernanceNo architecture eliminates complexity; it relocates and manages it. Layers can become rigid, services can create distributed-system failures, and abstractions can obscure simple behavior. Documentation can become obsolete, automation can encode incorrect assumptions, and metrics can encourage teams to optimize what is easy to measure rather than what matters. AI-enabled applications introduce additional concerns. Training data may contain personal or biased information. Models may be difficult to explain, may drift after deployment, or may perform differently across groups. Teams should document data provenance, intended use, limitations, evaluation conditions, model version, and human-override procedures. They should apply privacy-by-design principles, least-privilege access, retention controls, and relevant institutional and legal requirements. Professional responsibility includes accessibility, inclusive design, secure defaults, truthful communication of system limitations, and timely disclosure of defects. Governance should define who owns data, models, incidents, approvals, and retirement decisions. Architecture review should include technical and nontechnical stakeholders when systems affect people or institutional decisions. Contemporary Developments and Best PracticesModern engineering increasingly emphasizes platform automation, infrastructure as code, observability, containerized deployment, event-driven integration, and internal developer platforms. These techniques can improve repeatability and autonomy, but they should not be adopted as ends in themselves. Cloud services also create concerns about cost visibility, vendor dependence, regional data residency, and operational skill requirements. Current best practices include: - begin with requirements, risks, and ownership;
- prefer modularity before distribution;
- define contracts and validate them automatically;
- keep business rules independent from volatile infrastructure where useful;
- integrate frequently and keep changes small;
- automate tests, security checks, builds, and deployment controls;
- observe systems in production without collecting unnecessary sensitive data;
- document decisions and revisit them using evidence;
- treat dependencies and technical debt as managed risks; and
- design AI systems for traceability, privacy, fairness assessment, and human accountability.
Chapter SummarySoftware architecture organizes a system’s major structures, boundaries, dependencies, and quality attributes. Layered architecture separates responsibilities; MVC separates interaction concerns; component-based architecture emphasizes replaceable units and interfaces; service-oriented architecture organizes independently communicating capabilities; and hexagonal architecture protects application logic from infrastructure through ports and adapters. These styles can be combined, and their suitability depends on requirements, constraints, evidence, and team context. Professional software engineering extends architecture through API contracts, version control, documentation, package management, code review, CI/CD, security, observability, and governance. Good decisions make trade-offs explicit and test uncertain assumptions. Reuse must be evaluated across its lifecycle, while technical debt should be recorded and prioritized according to risk and future cost. For AI systems, reproducibility, model versioning, privacy, fairness, monitoring, and human oversight are essential architectural concerns. Key TermsArchitecture: Major structures and decisions that shape a software system. Quality attribute: A nonfunctional property such as security, reliability, or maintainability. Layered architecture: An organization of software into responsibility-based horizontal layers. MVC: Model–View–Controller separation of state, presentation, and input coordination. Component: A replaceable unit with an explicit interface and encapsulated implementation. Service-oriented architecture: An architecture in which business capabilities communicate through service contracts. Hexagonal architecture: An architecture that isolates application logic through ports and adapters. Port: An interface expressing an application capability or required external interaction. Adapter: An implementation that connects a port to a particular technology or external system. API: A contract through which software components communicate. Idempotency: The property that repeating an operation has the same intended effect as performing it once. Coupling: The degree of dependency between modules or components. Cohesion: The degree to which the responsibilities of a module belong together. Version control: A system for recording, comparing, and managing changes to artifacts. Code review: Structured human examination of a proposed software change. Continuous integration: Automated building and testing of frequently integrated changes. Technical debt: A compromise that creates future cost, risk, or obligation. Architecture decision record: A document capturing decision context, alternatives, and consequences. Observability: The ability to understand internal system behavior through outputs such as logs, metrics, and traces. Review QuestionsShort Answer Questions1. Distinguish software architecture from detailed design. 2. What are the responsibilities of the application layer in a layered architecture? 3. Define a port and an adapter in hexagonal architecture. 4. Why should API contracts specify error behavior as well as successful responses? 5. What is the difference between continuous delivery and continuous deployment? Long Answer Questions1. Compare layered, MVC, component-based, service-oriented, and hexagonal architectures. Explain how they may be combined in one system. 2. Describe a professional workflow from version-controlled change through code review, CI, staging, and production release. 3. Explain how quality attributes influence architecture selection, using performance, security, maintainability, and scalability as examples. 4. Discuss the benefits and risks of package reuse and propose a dependency-management policy for a student project. 5. Explain how technical debt should be identified, documented, prioritized, and repaid. Analytical / Application Questions1. A small team is considering splitting a modular monolith into eight services because “microservices are more scalable.” What evidence should the team collect, and what risks should it analyze before deciding? 2. Design an API and architecture for a university prediction service. Identify its layers or ports, authentication and authorization rules, error strategy, testing approach, and monitoring requirements. 3. An API intermittently creates duplicate payment records after clients retry timed-out requests. Analyze the architectural cause and propose contract, implementation, and operational changes. Multiple Choice Questions with AnswersQuestion 1Which architectural style most directly isolates application logic from infrastructure through ports and adapters? A. Layered architecture B. Hexagonal architecture C. Model–View–Controller D. Peer-to-peer architecture Correct Answer: B. Hexagonal architecture Question 2Which responsibility is most appropriate for a web controller? A. Coordinating input and selecting a response B. Implementing every business rule C. Managing database indexes D. Training a machine-learning model Correct Answer: A. Coordinating input and selecting a response Question 3Which HTTP method is generally intended to be idempotent when replacing a resource? A. POST B. PATCH C. PUT D. CONNECT Correct Answer: C. PUT Question 4What is the primary purpose of an architecture decision record? A. To replace all source-code comments B. To record context, alternatives, decisions, and consequences C. To measure processor speed D. To store user passwords Correct Answer: B. To record context, alternatives, decisions, and consequences Question 5Which practice helps ensure that business logic can be tested without a real database? A. Hard-coding database calls in domain classes B. Using ports and replaceable adapters C. Removing all interfaces D. Storing credentials in source control Correct Answer: B. Using ports and replaceable adapters Question 6What is a major risk of excessive service decomposition? A. Elimination of all network latency B. Distributed-system and operational complexity C. Guaranteed stronger consistency D. Inability to use version control Correct Answer: B. Distributed-system and operational complexity Question 7Which item should normally be excluded from a source repository? A. Unit tests B. Architecture documentation C. Production secrets D. Build configuration Correct Answer: C. Production secrets Question 8What does continuous integration primarily automate? A. Frequent building and testing of integrated changes B. Replacement of all human code review C. Permanent deletion of old releases D. Manual assignment of user permissions Correct Answer: A. Frequent building and testing of integrated changes Question 9Which statement best describes technical debt? A. Any code written by a junior developer B. A compromise that creates future cost or risk C. A required feature in every application D. A type of database index Correct Answer: B. A compromise that creates future cost or risk Question 10Why should an AI prediction response include a model version? A. To make the response larger B. To support traceability and reproducibility C. To eliminate the need for authorization D. To guarantee that predictions are fair Correct Answer: B. To support traceability and reproducibility
|