Data Structures & Application Development

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:

  1. Where does user input enter the system?
  2. Where are business rules applied?
  3. Where is data stored or retrieved?
  4. 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:

LayerMain responsibilityTypical examples
PresentationCommunicates with users or clientsCLI menu, HTML endpoint, JSON response
ApplicationCoordinates a use casePlaceOrderService, RegisterUserService
DomainRepresents business concepts and rulesOrder, Product, validation rules
Data accessStores and retrieves informationRepository, 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 PresentationCLI / Web / API ApplicationUse-case service DomainRules and entities DataFiles / database Results and errors return through the application boundary Dependencies should point toward stable business concepts.
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:

  1. receive validated input;
  2. obtain required domain objects or data;
  3. invoke domain rules;
  4. save changed state;
  5. 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.

A suitable module arrangement could be:

library/
  presentation/
    cli.py
    web_controller.py
  application/
    borrow_book.py
  domain/
    book.py
    member.py
    loan.py
  data/
    book_repository.py
    member_repository.py
    loan_repository.py

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:

try:
    member_id = input("Member ID: ")
    book_id = input("Book ID: ")
    loan = service.execute(member_id, book_id)
    print("Loan created:", loan.book_id)
except ValueError as error:
    print("Cannot borrow book:", error)

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:

  1. display an understandable prompt;
  2. parse and validate input;
  3. invoke an application operation;
  4. 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:

MethodTypical meaning
GETRetrieve a representation
POSTCreate or submit an operation
PUTReplace a resource representation
PATCHPartially modify a resource
DELETERemove 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

  1. What is the difference between cohesion and coupling?
  2. What is the primary responsibility of the application layer?
  3. Why should business rules not be placed only in a CLI controller?
  4. What problem does Dependency Injection help solve?
  5. Distinguish authentication from authorization.

Long Answer Questions

  1. Explain the responsibilities of the presentation, application, domain, and data-access layers.
  2. Compare the Repository pattern with direct database access from a controller.
  3. Explain how MVC can be used in a web application.
  4. Discuss how layered architecture supports testing and maintenance.
  5. Explain why a modular monolith may be preferable to immediate adoption of microservices.

Analytical / Application Questions

  1. Design the layers and main modules for a command-line student-enrollment application. Identify one business rule that belongs in the domain layer.
  2. A web controller directly writes SQL, formats HTML, and calculates discounts. Analyze the design problems and propose a refactoring.
  3. 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