Chapter 12
Application Architecture and User-Facing Development
Certificate in Java-Python-Programming Languages
Data Structures & Application Development
•
Certificate
Application Architecture and User-Facing Development
Introduction
A program that works is not necessarily an application that can be maintained, extended, tested, and safely used. As software grows, placing all input handling, business rules, data access, and error management in one file creates strong dependencies and makes change expensive. Application architecture provides a structure for organizing these responsibilities.
This chapter introduces practical architectural principles for certificate-level Java and Python development. It focuses on modular layers, separation of concerns, simple command-line interfaces, web applications, and service-based solutions. The emphasis is not on mastering a particular framework, but on understanding how responsibilities are divided and how established patterns guide implementation.
A useful architecture allows developers to answer four questions clearly:
Where does user input enter the system?
Where are business rules applied?
Where is data stored or retrieved?
How are results returned to users or other software?
These questions are relevant whether the application is a command-line inventory tool, a Python web service, or a Java application connected to a database.
Learning Objectives
By the end of this chapter, you should be able to:
explain architecture, modularity, cohesion, coupling, and separation of concerns;
distinguish presentation, application, domain, and data-access responsibilities;
design a small layered application in Java or Python;
describe the roles of common patterns such as MVC, Repository, Service Layer, and Dependency Injection;
develop a simple command-line or service-oriented application structure;
identify validation, error-handling, security, and testing concerns;
evaluate architectural trade-offs for small applications.
1. Architecture as a Structure for Change
Application architecture is the organization of a software system into components and the rules governing their relationships. It includes both the static structure of modules and the dynamic flow of requests through those modules.
Architecture is not the same as a programming language or framework. Java, Python, Spring, Django, Flask, and similar technologies are implementation tools. Architecture is the reasoning that determines which component should perform a responsibility and how components communicate.
1.1 Modularity
A module is a relatively independent unit of software with a defined responsibility and interface. A module may be a function, class, package, or service. Good modules hide internal details and expose only what other components need.
Two qualities are especially important:
Cohesion is the degree to which the responsibilities within a module belong together. A ReceiptPrinter that formats receipts has high cohesion. A class that prints receipts, connects to a database, validates passwords, and sends email has low cohesion.
Coupling is the degree of dependency between modules. If a user-interface class directly creates database connections and depends on database-specific commands, coupling is high.
A desirable design generally aims for high cohesion and low coupling. This is not an absolute law: some coupling is unavoidable. The goal is to make dependencies explicit, limited, and stable.
1.2 Separation of Concerns
Separation of concerns means dividing a system according to distinct reasons for change. User-interface formatting should change independently of a database schema. A discount rule should be testable without starting a web server.
A common small-application structure contains four layers:
Layer
Main responsibility
Typical examples
Presentation
Communicates with users or clients
CLI menu, HTML endpoint, JSON response
Application
Coordinates a use case
PlaceOrderService, RegisterUserService
Domain
Represents business concepts and rules
Order, Product, validation rules
Data access
Stores and retrieves information
Repository, file adapter, SQL gateway
The layers are logical responsibilities, not necessarily separate deployment machines. A small program may implement each layer with one class or module.
A typical request flows as follows:
Layered application request flow
Figure: Layered application request flow
Figure: A request moves through separated layers, while results return through controlled interfaces.
2. A Practical Layered Model
2.1 Presentation Layer
The presentation layer receives input and presents output. In a command-line program it reads text from the console and prints messages. In a web application it interprets HTTP requests and produces HTML or JSON.
Presentation code should not contain important business rules. For example, a command-line menu may collect a product identifier, but the rule that a product cannot be sold when its quantity is zero belongs in the application or domain layer. Otherwise, a web client and a CLI client might implement different rules.
2.2 Application Layer
The application layer coordinates a complete use case. A use case is a meaningful task from the user or client perspective, such as “borrow a book,” “create an account,” or “record a sale.”
An application service usually performs the following sequence:
receive validated input;
obtain required domain objects or data;
invoke domain rules;
save changed state;
return a result or controlled error.
The application layer should coordinate rather than become a second database or a collection of unrelated utility functions.
2.3 Domain Layer
The domain layer expresses the concepts and rules of the problem. An entity has identity and may change over time, such as a Customer or Book. A value object is defined by its value, such as an email address or money amount. A domain service may contain a rule that does not naturally belong to one entity.
For example, an InventoryItem might enforce that quantity cannot be negative. This rule should not exist only in a web controller because inventory could also be changed through a scheduled job or another service.
2.4 Data-Access Layer
The data-access layer translates between program objects and storage mechanisms. Storage may be a text file, CSV document, relational database, or remote API. The rest of the application should depend on an abstraction such as ProductRepository, not on SQL statements scattered throughout user-interface code.
A repository commonly offers operations such as:
findById(id);
findAll();
save(item);
delete(id).
The exact methods should reflect application needs. A repository is not automatically a complete replacement for a database; complex reporting and transaction requirements may need specialized queries.
3. Established Design Patterns
A design pattern is a reusable design idea, not a copy-and-paste class. Patterns are useful when they make a recurring structure clearer. They become harmful when applied mechanically.
Model–View–Controller (MVC)
MVC separates:
Model: application data and rules;
View: visible output;
Controller: interprets input and coordinates a response.
In a web application, a controller may receive an HTTP request, call an application service, and select a response view. The model should not be confused with a database table: it represents concepts used by the application.
Repository Pattern
The Repository pattern provides a collection-like interface to domain objects while hiding persistence details. A FileProductRepository and a SqlProductRepository can implement the same interface. This supports testing and future replacement, provided the interface expresses stable needs rather than storage-specific features.
Service Layer
A Service Layer defines application operations. For example, CheckoutService.checkout(cartId, payment) represents a use case. It can coordinate repositories and domain objects without forcing a controller to understand transaction or workflow details.
Dependency Injection
Dependency Injection means that an object receives its dependencies rather than constructing them internally. In Python, a constructor parameter can receive a repository. In Java, a constructor can receive an interface implementation.
Without injection, a service may contain new SqlProductRepository(), making tests dependent on a database. With injection, a test can provide an in-memory repository. Injection can be manual; a framework is not required.
Data Transfer Objects
A Data Transfer Object (DTO) carries data across a boundary, such as from an HTTP request to an application service. DTOs can prevent external input formats from becoming tightly coupled to internal domain objects. They should not automatically become an alternative name for every class in the system.
4. Designing a Small Application
Consider a simple library application with a use case: a member borrows a book.
The requirements are:
a known member may borrow a book;
a book may be borrowed only when available;
the system records the borrowing;
the user receives a success or explanatory failure message.
The names are illustrative. A Java project might use packages such as presentation, application, domain, and data.
A Python domain object might be represented conceptually as:
class Book:
def __init__(self, book_id, title, available=True):
self.book_id = book_id
self.title = title
self.available = available
def borrow(self):
if not self.available:
raise ValueError("Book is already borrowed")
self.available = False
The important point is not the syntax. The Book object protects its own invariant: after a successful borrow, it is unavailable; an unavailable book cannot be borrowed again.
An application service could be structured as:
class BorrowBookService:
def __init__(self, books, members, loans):
self.books = books
self.members = members
self.loans = loans
def execute(self, member_id, book_id):
member = self.members.find(member_id)
if member is None:
raise ValueError("Member not found")
book = self.books.find(book_id)
if book is None:
raise ValueError("Book not found")
book.borrow()
loan = Loan(member_id, book_id)
self.books.save(book)
self.loans.save(loan)
return loan
In production code, persistence failure and transaction boundaries would require additional handling. The example demonstrates separation: the service coordinates, the domain object applies a rule, and repositories manage storage.
A CLI controller should mainly translate between text and the service:
The CLI does not decide whether a book is available. Therefore, a later web controller can reuse the same service.
5. User-Facing Development
5.1 Command-Line Applications
A robust CLI generally has four stages:
display an understandable prompt;
parse and validate input;
invoke an application operation;
display a useful result or error.
Input validation should distinguish format errors from business errors. “Quantity must be an integer” is a format problem. “Quantity exceeds available stock” is a business problem. Both should be explained without exposing a stack trace to the user.
A loop-based CLI should also provide a clear exit command and avoid losing data when an operation fails. Repeated prompts should be implemented through small functions rather than one large procedure.
5.2 Web Applications and Services
A web application communicates through HTTP. A client sends a request containing a method, URL, headers, and possibly a body. The server returns a status code, headers, and a response body.
Common meanings include:
Method
Typical meaning
GET
Retrieve a representation
POST
Create or submit an operation
PUT
Replace a resource representation
PATCH
Partially modify a resource
DELETE
Remove a resource
A service endpoint should have a stable contract. For example, POST /loans might accept member and book identifiers and return a created loan. The controller translates HTTP data into an application request and translates the result into an HTTP response.
Status codes should communicate outcome appropriately. A successful creation commonly uses 201, an invalid request may use 400, an unauthenticated request 401, a forbidden operation 403, a missing resource 404, and an unexpected server failure 500. Exact choices depend on the API contract.
The service should not expose internal database errors, file paths, passwords, or stack traces. Logs may contain technical details, but client responses should be safe and understandable.
6. Validation, Errors, and Security
Validation occurs at more than one boundary. External input should be checked for type, length, allowed values, and required fields. Domain rules should also be enforced internally because not every caller necessarily passes through the same user interface.
Error handling should be deliberate:
catch errors where meaningful recovery or translation is possible;
use domain-specific exceptions or result types where appropriate;
log unexpected failures with a correlation or request identifier;
avoid revealing sensitive implementation details.
Security is an architectural concern, not merely a login feature. Important practices include:
authenticate users and authorize operations separately;
apply least privilege to users, services, and database accounts;
hash passwords using an established password-hashing algorithm rather than storing plaintext;
use parameterized database queries to reduce injection risk;
protect secrets through environment or secret-management facilities;
validate uploaded or externally supplied data;
use encrypted transport such as HTTPS for network communication;
avoid logging passwords, access tokens, and unnecessary personal data.
Privacy principles also matter. Collect only data needed for the stated purpose, restrict access, define retention rules, and provide appropriate deletion or correction processes where required by applicable law and institutional policy.
7. Testing and Maintainability
Layered architecture supports different testing levels. A domain test can test Book.borrow() without a database. An application-service test can use in-memory repositories. An integration test can verify the real database adapter. An end-to-end test can exercise the CLI or HTTP interface.
A useful test should be repeatable and independent. Test names should describe behavior, such as borrow_fails_when_book_is_unavailable. Tests should include both successful paths and meaningful failure paths.
Maintainability is improved by:
clear names and small functions;
interfaces at genuine boundaries;
centralized configuration;
consistent error conventions;
automated formatting and static analysis;
version control with focused commits;
documentation of public APIs and important decisions.
Architecture should remain proportional to the problem. A four-layer design does not require dozens of classes. For a small script, introducing multiple services and interfaces may create more complexity than it removes. A useful rule is to introduce a boundary when it protects a change, enables testing, or clarifies a responsibility.
8. Applied Case: Community Clinic Appointment System
The following is an illustrative case.
Background
A community clinic initially records appointments in a spreadsheet. Reception staff need a simple CLI tool, while a future patient portal may require a web service. The clinic must prevent double booking and restrict access to personal information.
Problem
The first prototype puts menu display, spreadsheet access, appointment rules, and messages in one Python file. A change to appointment duration risks breaking the user interface. Testing requires opening the spreadsheet, and errors expose file details.
Analysis
The central domain rule is that two appointments cannot occupy the same clinician and time interval. The rule must be independent of whether an appointment is created through a CLI or web portal. Storage is also likely to change from a spreadsheet to a database.
Approach and Solution
The redesigned system contains:
a CLI controller for reception staff;
an appointment application service;
domain objects for Patient, Clinician, and Appointment;
an appointment repository interface;
a spreadsheet adapter for the initial release;
an authentication and authorization boundary.
The service obtains existing appointments for the clinician and date, asks the domain policy whether the requested interval conflicts, then saves the appointment. The CLI converts validation failures into clear messages. A future HTTP controller can call the same service.
The repository interface prevents the service from depending on spreadsheet cell coordinates. Tests use an in-memory repository and verify that overlapping appointments are rejected.
Outcome
The clinic can replace spreadsheet storage without rewriting booking rules. The same business operation can support multiple interfaces. Security review can focus on authentication, authorization, and data exposure at defined boundaries rather than searching through one large script.
Learning Points
The case demonstrates that architecture is valuable because it isolates likely changes. It also shows that separation of concerns does not mean separation without communication: the layers collaborate through deliberate interfaces and use-case operations.
9. Contemporary Development Considerations
Modern applications increasingly use APIs, cloud-hosted databases, containers, automated deployment, and AI-assisted coding tools. These technologies do not remove architectural responsibility. Generated code still requires review for correctness, security, licensing, privacy, and consistency with application rules.
A small application may begin as a modular monolith: one deployable program with clear internal layers. This is often easier to develop and operate than immediately creating microservices. A service should be separated into an independently deployable component only when there is a justified need, such as independent scaling, ownership, deployment, or fault isolation.
Observability is also important in service-based applications. Logs describe events, metrics summarize behavior, and traces connect a request across components. Observability data should be designed to avoid unnecessary personal information.
10. Best-Practice Design Checklist
Before implementing a small application, ask:
What are the main user or client use cases?
Which rules are domain rules and must be true for every interface?
Which component owns each responsibility?
Can the domain and application logic be tested without external storage?
Are dependencies passed through clear interfaces?
Are invalid input and unexpected failures handled differently?
Are authentication, authorization, secrets, and personal data considered?
Is the architecture simple enough for the current problem?
Can a likely change be made without modifying unrelated layers?
Architecture is successful when it makes the system easier to understand and change, not when it contains the greatest number of patterns.
Chapter Summary
Application architecture organizes software so that responsibilities and dependencies are understandable. Modularity, high cohesion, low coupling, and separation of concerns provide the foundation for maintainable systems. A practical layered model separates presentation, application coordination, domain rules, and data access.
MVC, Repository, Service Layer, Dependency Injection, and DTOs are useful patterns when applied to real design needs. Command-line programs and web services can reuse the same application and domain logic when interfaces remain separate from business rules. Validation, error handling, security, testing, privacy, and observability must be considered as architectural concerns. For certificate-level projects, a modular monolith is often an effective starting point, while unnecessary complexity should be avoided.
Key Terms
Architecture: The structure of components and their relationships in a software system.
Module: A software unit with a defined responsibility and interface.
Cohesion: The degree to which a module’s responsibilities belong together.
Coupling: The degree of dependency between modules.
Separation of concerns: Dividing software according to distinct responsibilities or reasons for change.
Presentation layer: The part that communicates with users or external clients.
Application layer: The part that coordinates a complete use case.
Domain layer: The part containing business concepts and rules.
Data-access layer: The part that communicates with storage systems.
Entity: A domain object identified by a persistent identity.
Value object: An object defined by its value rather than identity.
MVC: Model–View–Controller, a pattern separating data, presentation, and input coordination.
Repository: An abstraction for storing and retrieving domain objects.
Service Layer: A set of operations representing application use cases.
Dependency Injection: Supplying an object’s dependencies from outside rather than constructing them internally.
DTO: Data Transfer Object used to carry data across a boundary.
Validation: Checking whether input meets structural and business requirements.
Authentication: Verifying identity.
Authorization: Determining permitted actions.
Modular monolith: One deployable application organized into separated internal modules.
Review Questions
Short Answer Questions
What is the difference between cohesion and coupling?
What is the primary responsibility of the application layer?
Why should business rules not be placed only in a CLI controller?
What problem does Dependency Injection help solve?
Distinguish authentication from authorization.
Long Answer Questions
Explain the responsibilities of the presentation, application, domain, and data-access layers.
Compare the Repository pattern with direct database access from a controller.
Explain how MVC can be used in a web application.
Discuss how layered architecture supports testing and maintenance.
Explain why a modular monolith may be preferable to immediate adoption of microservices.
Analytical / Application Questions
Design the layers and main modules for a command-line student-enrollment application. Identify one business rule that belongs in the domain layer.
A web controller directly writes SQL, formats HTML, and calculates discounts. Analyze the design problems and propose a refactoring.
Design a testing strategy for the clinic appointment case, identifying unit, integration, and end-to-end tests.
Multiple Choice Questions with Answers
Question 1
Which quality describes how closely the responsibilities within one module belong together?
A. Cohesion
B. Coupling
C. Authentication
D. Serialization
Correct Answer: A. Cohesion
Question 2
Which layer should normally coordinate a complete use case such as placing an order?
A. Presentation layer
B. Application layer
C. Data-access layer
D. Operating-system layer
Correct Answer: B. Application layer
Question 3
What is the main purpose of the Repository pattern?
A. To format HTML pages
B. To authenticate users
C. To hide storage details behind an interface
D. To replace all domain objects
Correct Answer: C. To hide storage details behind an interface
Question 4
Which practice is an example of Dependency Injection?
A. A service creates its database inside every method
B. A controller stores passwords in source code
C. A repository constructs the user interface
D. A service receives a repository through its constructor
Correct Answer: D. A service receives a repository through its constructor
Question 5
Which HTTP method is commonly used to create a new resource?
A. GET
B. POST
C. DELETE
D. PATCH
Correct Answer: B. POST
Question 6
The rule that an unavailable book cannot be borrowed again belongs primarily in which area?
A. Domain logic
B. Screen formatting
C. Operating-system configuration
D. Network encryption
Correct Answer: A. Domain logic
Question 7
Which response status commonly indicates that a requested resource was not found?
A. 201
B. 400
C. 404
D. 500
Correct Answer: C. 404
Question 8
Why should passwords not be stored as plaintext?
A. Plaintext prevents HTTP requests
B. A storage compromise would directly reveal passwords
C. Plaintext makes databases relational
D. Plaintext disables dependency injection
Correct Answer: B. A storage compromise would directly reveal passwords
Question 9
What is a suitable reason to introduce an architectural boundary?
A. It makes every project longer
B. It guarantees zero defects
C. It protects a likely change or enables isolated testing
D. It eliminates the need for requirements
Correct Answer: C. It protects a likely change or enables isolated testing
Question 10
Which statement best describes a modular monolith?
A. A single deployable application with clear internal modules
B. A system containing only command-line code
C. A database without application logic
D. A collection of unrelated scripts with shared global variables
Correct Answer: A. A single deployable application with clear internal modules
Data Structures & Application Development
Chapter 12
of 15