Contemporary Java Practice and Capstone ApplicationIntroductionA Java programmer is not prepared for professional work merely by knowing classes, inheritance, exceptions, and collections. Real software is developed in response to requirements, constrained by security and privacy obligations, tested against explicit quality criteria, deployed into operational environments, and maintained for years. Contemporary Java practice therefore combines programming knowledge with software engineering judgment. This chapter integrates the development lifecycle through an illustrative capstone application: a small community equipment-booking service. The application allows registered users to view equipment, reserve available items, and receive a confirmation. Its scope is intentionally modest, but the engineering decisions are representative of larger systems. The chapter also considers cloud-native Java, containers, AI-assisted development, licensing, governance, maintainability, digital transformation, and emerging Java trends. The purpose is not to promote a particular framework, cloud provider, or tool. Rather, it is to develop a disciplined method for making and evaluating technical choices. Learning ObjectivesBy the end of this chapter, you should be able to: - Translate a business need into requirements, acceptance criteria, and a manageable scope.
- Design a small Java application using suitable domain, application, and infrastructure responsibilities.
- Implement validation, persistence boundaries, error handling, and testable services.
- Explain how unit, integration, and acceptance tests provide different evidence.
- Package a Java application in a container and identify deployment concerns.
- Evaluate AI-assisted programming for productivity, correctness, security, and accountability.
- Apply principles of privacy, licensing, governance, accessibility, and maintainability.
- Assess contemporary Java practices in the context of digital transformation.
1. From Requirement to Definition of DoneA requirement describes a capability or constraint that stakeholders need. A user story expresses a requirement from a user’s perspective, but it is not a complete specification. A useful user story is supported by acceptance criteria, which state observable conditions for success. For the equipment-booking application, a user story is: As a member, I want to reserve an available item for a specified period so that I can use it when needed. Acceptance criteria might include: - The member must be authenticated.
- The item must exist and be bookable.
- The start date must precede the end date.
- A reservation must not overlap an existing reservation for the same item.
- The system must not store unnecessary personal information.
- A successful request must return a reservation identifier and a clear status.
A useful definition of done goes beyond “the code compiles.” It can require that the feature has reviewed code, automated tests, documented behavior, security checks, a migration or persistence decision, and a reproducible deployment procedure. Functional and non-functional requirementsFunctional requirements specify what the system does. Non-functional requirements specify qualities or constraints on how it does it. | Category | Example for the capstone application |
|---|
| Functional | Create a reservation for an available item | | Performance | Return ordinary availability requests within an agreed response target | | Security | Prevent users from viewing another user’s private data | | Reliability | Do not confirm two conflicting reservations | | Privacy | Collect only data necessary for booking and support | | Maintainability | Keep business rules independently testable | | Accessibility | Provide understandable messages and keyboard-operable interfaces |
Requirements should be testable. “The application should be user-friendly” is weak because it lacks an observable criterion. “A user can complete a booking using keyboard navigation and receives a meaningful validation message” is more testable. 2. Choosing an Appropriate ArchitectureA certificate-level application should be small enough to understand but structured enough to evolve. A modular monolith is often a sound starting point. It is one deployable application with clear internal boundaries. It avoids the operational complexity of microservices while preserving separation of responsibilities. A practical arrangement is: - Domain layer: entities, value concepts, and business rules.
- Application layer: use cases such as
CreateReservation. - Infrastructure layer: database repositories, HTTP adapters, email providers, and configuration.
- Interface layer: REST controllers, command-line interfaces, or web views.
The dependency direction should generally point toward business rules. Domain code should not need to know whether data is stored in PostgreSQL, an in-memory map, or a test double. A small domain modelThe central concepts are Member, Equipment, and Reservation. A reservation has an identifier, member identifier, equipment identifier, start date, end date, and status. Dates should be represented with an appropriate Java time type, such as LocalDate when the business meaning is a calendar date rather than an instant in time. An overlap rule must be stated precisely. For half-open date intervals, where the start is included and the end is excluded, intervals A and B overlap when: A.start < B.end ∧ B.start < A.end Here, A.start and A.end are the boundaries of interval A, B.start and B.end are the boundaries of interval B, and ∧ means “and.” This rule assumes valid intervals whose start precedes their end. Under this convention, a booking ending on 10 June does not conflict with one beginning on 10 June. If the business treats both dates as occupied calendar days, the rule and storage representation must be changed consistently. A Java domain method can express the rule: public record DateRange(LocalDate start, LocalDate end) {
public DateRange {
if (start == null || end == null || !start.isBefore(end)) {
throw new IllegalArgumentException("Start must precede end");
}
}
public boolean overlaps(DateRange other) {
return start.isBefore(other.end())
&& other.start().isBefore(end);
}
}The constructor establishes an invariant: every DateRange is valid after construction. An invariant is a condition that should remain true for every valid instance. Encapsulating this condition reduces the chance that invalid ranges circulate through the application. 3. Implementation Practices in Modern JavaModern Java supports concise data modeling through records, improved switch expressions, pattern matching features in recent releases, and a mature standard library. These features should be used because they improve clarity, not merely because they are new. A record is suitable for a small immutable data carrier: public record ReservationRequest(
long memberId,
long equipmentId,
LocalDate start,
LocalDate end) { }A record is not automatically a complete validation or domain model. Validation belongs at an appropriate boundary, and domain invariants still need to be enforced. An application service might depend on abstractions rather than concrete database classes: public interface EquipmentRepository {
Optional<Equipment> findById(long id);
}
public interface ReservationRepository {
boolean existsOverlapping(long equipmentId, DateRange period);
Reservation save(Reservation reservation);
}
public final class CreateReservation {
private final EquipmentRepository equipmentRepository;
private final ReservationRepository reservationRepository;
public CreateReservation(EquipmentRepository equipmentRepository,
ReservationRepository reservationRepository) {
this.equipmentRepository = equipmentRepository;
this.reservationRepository = reservationRepository;
}
public Reservation execute(ReservationRequest request) {
DateRange period = new DateRange(request.start(), request.end());
Equipment equipment = equipmentRepository.findById(request.equipmentId())
.orElseThrow(() -> new IllegalArgumentException("Equipment not found"));
if (!equipment.isBookable()) {
throw new IllegalStateException("Equipment is not bookable");
}
if (reservationRepository.existsOverlapping(request.equipmentId(), period)) {
throw new IllegalStateException("Equipment is already reserved");
}
Reservation reservation = Reservation.pending(
request.memberId(), request.equipmentId(), period);
return reservationRepository.save(reservation);
}
}This example illustrates separation of concerns, but it also reveals an important limitation: checking availability and saving are not automatically atomic. In a concurrent system, two requests could both observe availability before either saves. A production design needs a transaction and an appropriate database constraint or locking strategy. The application code is therefore only part of correctness. Exceptions and error boundariesExceptions should communicate meaningful failure categories. A malformed request, an unauthenticated request, a missing resource, and a database outage should not all appear as HTTP status 500. At an interface boundary, internal details should be logged securely while the client receives a stable, non-sensitive error response. Do not include passwords, access tokens, full personal records, or database connection details in logs. Structured logging with a correlation identifier assists diagnosis without requiring sensitive payloads. Configuration and secretsConfiguration such as a server port or database host may be externalized. Secrets must not be committed to source control or embedded in a container image. Production systems commonly obtain secrets from a managed secret store or protected runtime configuration. A .env file may be convenient for local development, but it is not a substitute for production secret management. 4. Testing as Evidence of QualityTesting does not prove that a program has no defects. It provides evidence about selected behaviors under selected conditions. Different test levels answer different questions. | Test level | Primary question | Typical scope |
|---|
| Unit | Does one component obey its rule? | DateRange.overlaps | | Integration | Do components work with a real dependency? | Repository with a test database | | Acceptance | Does the delivered behavior satisfy a user need? | Booking through the application boundary | | Regression | Does a change preserve previously supported behavior? | Automated test suite | | Security | Can misuse or unauthorized access succeed? | Authentication and authorization paths |
A unit test for the interval rule should cover overlapping ranges, adjacent ranges, invalid ranges, and containment. Tests should be deterministic and should explain the behavior they protect. @Test
void adjacentPeriodsDoNotOverlap() {
DateRange first = new DateRange(
LocalDate.of(2025, 6, 1), LocalDate.of(2025, 6, 10));
DateRange second = new DateRange(
LocalDate.of(2025, 6, 10), LocalDate.of(2025, 6, 15));
assertFalse(first.overlaps(second));
}A test that depends on the current clock, network, or shared mutable database is more difficult to reproduce. Injecting a Clock, using isolated test data, and controlling external dependencies improve reliability. Coverage percentage can identify untested code, but high coverage does not guarantee meaningful tests. A test suite can execute every line while failing to check authorization, boundary values, or error handling. Quality depends on the relevance of assertions and the risk addressed. 5. Cloud-Native Java and ContainersCloud-native development is not simply “putting an application in the cloud.” It commonly involves externally managed configuration, automated builds, observable services, horizontal scaling, resilient dependencies, and infrastructure treated as code. A small application need not adopt every cloud-native technology, but its design should avoid assumptions that prevent reliable deployment. A container packages an application and its runtime dependencies into an image that can run consistently across environments. A Java container should usually: - use a maintained, appropriately sized base image;
- run as a non-root user where practical;
- keep configuration outside the image;
- expose only required ports;
- produce logs suitable for the platform;
- define a health or readiness strategy;
- scan dependencies and the final image;
- use a reproducible build process.
A simplified multi-stage Dockerfile might be: FROM eclipse-temurin:21-jdk AS build
WORKDIR /workspace
COPY . .
RUN ./mvnw -q -DskipTests package
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /workspace/target/equipment-booking.jar app.jar
USER 10001
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"] The exact image tag should be selected from a maintained vendor distribution and pinned according to the organization’s update policy. A build that skips tests may be acceptable only in a controlled image-building stage if tests have already run in the pipeline; it should not replace testing. Operational readinessA deployable service should make its state visible without exposing confidential data. Useful signals include: - Logs: event records with severity and correlation identifiers.
- Metrics: measurements such as request count, error count, and latency.
- Traces: relationships among calls across service boundaries.
- Health checks: whether the process is alive and whether it can serve traffic.
A liveness check asks whether restarting may help. A readiness check asks whether the instance should receive traffic. Confusing the two can cause unnecessary restarts or route requests to an unready service. 6. AI-Assisted DevelopmentGenerative AI tools can propose code, tests, documentation, SQL, or refactoring suggestions. They can accelerate routine work, but generated output is not evidence of correctness. A developer remains responsible for requirements, review, security, licensing, and operational behavior. A disciplined workflow is: - State the requirement and constraints before asking for code.
- Request a small, reviewable change rather than an entire undocumented system.
- Inspect the output for correctness, edge cases, insecure defaults, and hidden assumptions.
- Compile and run tests, including tests not generated by the tool.
- Check dependencies, licenses, and data-handling implications.
- Record significant AI assistance when organizational policy requires it.
Do not paste confidential source code, credentials, personal data, or proprietary customer information into an unapproved service. Prompts and outputs may be retained or processed according to the provider’s terms. AI-generated code may contain vulnerable deserialization, weak authorization, hallucinated APIs, outdated library usage, or a license conflict. Static analysis, dependency scanning, peer review, and threat modeling remain necessary. AI can assist with a threat-modeling checklist, but it cannot determine an organization’s legal obligations or risk tolerance by itself. 7. Privacy, Security, Licensing, and GovernancePrivacy is a design concern, not a final compliance checkbox. The application should identify what personal data is needed, why it is needed, who can access it, how long it is retained, and how it can be deleted or corrected. Data minimization means collecting less rather than collecting everything “just in case.” Security controls should include authentication, authorization, input validation, secure password handling through a suitable established mechanism, protection against injection, dependency updates, encrypted transport, and safe error responses. Authorization must be checked at the server boundary; hiding a button in a client interface is not authorization. Governance assigns responsibility for decisions. A useful lightweight governance record can identify: - system owner and technical owner;
- data classification and retention policy;
- approved dependencies and license obligations;
- change-review requirements;
- incident reporting path;
- backup and recovery expectations;
- accessibility and inclusion criteria.
Open-source licensingSoftware dependencies carry license conditions. Permissive licenses often require preserving notices and disclaimers. Reciprocal licenses may impose obligations when covered software is distributed or combined in particular ways. The precise legal effect depends on the license text, use, and jurisdiction. Teams should maintain a software bill of materials (SBOM), preserve notices, review compatibility, and obtain qualified legal advice when uncertainty matters. A dependency is not “free of obligations” merely because it is publicly downloadable. License compliance and vulnerability management should be integrated into the build pipeline. 8. Maintainability and Digital TransformationMaintainability is the ease with which software can be understood, corrected, adapted, and tested. Clear names, small cohesive classes, explicit boundaries, useful documentation, automated tests, and observable behavior all contribute to it. Excessive abstraction can reduce maintainability when it hides simple behavior behind unnecessary layers. Technical debt is not simply bad code; it is a trade-off that creates future cost or risk. Debt can be deliberate, such as postponing a sophisticated reporting feature to deliver a safe minimum product, or accidental, such as duplicated validation and undocumented configuration. Debt should be recorded, prioritized, and repaid according to risk. Digital transformation is broader than replacing paper forms with a Java application. It may change workflows, roles, data ownership, customer expectations, and decision-making. A successful booking service therefore requires process analysis, user training, support procedures, and measurable outcomes—not only a deployed artifact. Useful outcome measures include booking completion rate, cancellation rate, support incidents, availability accuracy, and time saved by staff. Metrics should be interpreted carefully: optimizing completion rate while excluding users with accessibility needs would produce a misleading success claim. 9. Illustrative Capstone Case: Community Equipment BookingBackgroundA community technology center lends cameras, laptops, and audio equipment. Staff currently use a shared spreadsheet. Double bookings occur, personal data is visible to too many people, and staff cannot reliably determine who changed a record. Problem analysisThe initial scope is limited to member authentication, equipment availability, reservation creation, cancellation, and an administrative audit trail. Payment, delivery logistics, predictive demand, and mobile applications are explicitly deferred. This scope protects the project from becoming an unmanageable system. Risks include overlapping bookings, unauthorized access, accidental deletion, privacy exposure, dependency vulnerabilities, and operational failure during deployment. Approach and solutionThe team chooses a modular Java monolith with a REST interface and relational persistence. The domain layer owns date-range validation and booking rules. The application layer coordinates the use case. The infrastructure layer implements repositories and audit storage. A database transaction surrounds the conflict check and reservation creation, and the database design provides a defense against conflicting writes appropriate to the selected database technology. The delivery pipeline performs compilation, unit tests, integration tests, static analysis, dependency scanning, container image creation, and deployment to a test environment. Production deployment requires an approved change, backup verification, and a rollback plan. A minimal API might define: GET /equipment?availableOn=2025-06-12POST /reservationsDELETE /reservations/{id}GET /admin/audit-events for authorized staff only
The API should validate dates and identifiers, return stable error representations, and avoid revealing whether another person’s private reservation exists when that information would create a privacy risk. OutcomeIn this illustrative case, the center reduces spreadsheet conflicts and gains an audit trail. Staff can update equipment status without editing application code. The first release does not attempt to predict demand or automate all decisions. After observing real use, the team plans accessibility testing, retention review, and improved reporting. Learning points- A small, explicit scope improves delivery confidence.
- Business rules must be protected at more than one layer when concurrency matters.
- Deployment, privacy, and governance are part of the product.
- A modular monolith can provide a practical path before microservices.
- Outcomes should be evaluated using user and operational evidence, not only code metrics.
10. Contemporary Java DirectionsJava remains relevant because of its ecosystem, portability, tooling, compatibility discipline, and use in enterprise and cloud systems. Current practice includes long-term-support releases, regular feature releases, modern language syntax, container-aware runtime configuration, observability standards, and increasing use of ahead-of-time and native-image techniques for selected workloads. Virtual threads, standardized in modern Java releases, can simplify high-concurrency applications dominated by blocking I/O. They do not make CPU-heavy work faster, remove the need for bounded external resources, or eliminate database capacity limits. Their suitability must be tested against the framework, drivers, synchronization, and workload. Project Leyden and related ecosystem work reflect interest in faster startup and improved time-to-performance, especially for cloud workloads. Native compilation can reduce startup time and memory use in some applications, but may impose build complexity, reflection configuration, or reduced portability. Decisions should be based on measured operational requirements. Pattern matching, records, sealed types, and improved switch expressions can make domain models more expressive. Frameworks continue to evolve toward observability, reactive or asynchronous processing where appropriate, and stronger supply-chain security. The enduring principle is to adopt a feature when it solves a verified problem and remains understandable to the team. Best-Practice ChecklistBefore declaring a small Java application complete, verify that: - requirements and acceptance criteria are written;
- scope and deferred features are explicit;
- domain invariants are represented in code and tests;
- persistence operations handle concurrency appropriately;
- authentication and authorization are distinct and tested;
- secrets are externalized;
- logs avoid sensitive information;
- unit and integration tests run automatically;
- dependencies and licenses are reviewed;
- the container is reproducibly built and minimally privileged;
- health, logs, metrics, and rollback procedures are defined;
- privacy, accessibility, and retention are considered;
- AI-assisted contributions have been reviewed and verified;
- operational and user outcomes are evaluated after release.
Chapter SummaryContemporary Java practice integrates programming with the complete software lifecycle. A reliable application begins with testable requirements and a controlled scope. A modular architecture separates domain rules, use cases, infrastructure, and interfaces while keeping business logic independently testable. Java records, the time API, interfaces, and modern language features can improve clarity when used deliberately. Correctness includes concurrency, validation, authorization, privacy, and operational behavior. Unit, integration, acceptance, security, and regression tests provide different forms of evidence. Containers and cloud-native practices improve reproducibility and deployment, but they introduce requirements for configuration, observability, health checks, image security, and secrets management. AI-assisted development can increase productivity only when human review, testing, privacy protection, licensing analysis, and accountability remain in place. Governance includes data handling, dependency obligations, change control, and incident response. Maintainability and digital transformation concern long-term organizational value, not merely initial delivery. Emerging Java capabilities—including virtual threads, modern language constructs, and startup optimization—should be adopted based on measured needs and team capability. Key TermsRequirement: A needed capability, quality, or constraint of a system. Acceptance criterion: An observable condition used to decide whether a requirement is satisfied. Definition of done: A shared completion standard covering implementation, quality, documentation, and delivery readiness. Modular monolith: One deployable application organized into explicit internal modules. Domain model: Concepts, data, and rules representing the problem area. Invariant: A condition that remains true for every valid object or system state. Value object: An object defined primarily by its value and usually treated as immutable. Repository: An abstraction for obtaining and storing domain-related data. Transaction: A unit of work whose operations follow defined consistency and completion properties. Unit test: A test focused on a small component in relative isolation. Integration test: A test examining interaction with another component or real infrastructure. Container image: A packaged filesystem and metadata used to run an application consistently. Observability: The ability to understand system behavior through logs, metrics, and traces. Secret: Sensitive authentication or configuration data requiring protected handling. Data minimization: Collecting and retaining only the personal data necessary for a stated purpose. SBOM: A software bill of materials listing components included in a software product. Technical debt: A design or implementation choice that creates future cost, risk, or reduced adaptability. Virtual thread: A lightweight Java thread designed to support high-concurrency workloads, particularly blocking I/O scenarios. Governance: Structures and practices for assigning responsibility, controlling risk, and making accountable decisions. Review QuestionsShort Answer Questions- Why are acceptance criteria needed in addition to a user story?
- What is the principal advantage of beginning with a modular monolith?
- State the half-open interval overlap rule used in the chapter.
- Distinguish a liveness check from a readiness check.
- Why is generated AI code not automatically trustworthy?
Long Answer Questions- Explain how domain, application, infrastructure, and interface responsibilities can be separated in a Java application.
- Compare unit, integration, acceptance, and security testing for the equipment-booking system.
- Discuss the security, privacy, and governance controls required before deployment.
- Explain how containerization supports reproducibility and identify its principal operational responsibilities.
- Evaluate the advantages and limitations of a modular monolith compared with immediately adopting microservices.
Analytical / Application Questions- Two requests attempt to reserve the same equipment for overlapping periods at nearly the same time. Explain why an availability check followed by a save may fail, and propose a database or transaction-level remedy.
- A developer asks an external AI tool to refactor code containing customer names and access tokens. Identify the risks and design a safer review workflow.
- The booking application has high test coverage but repeated privacy incidents. Analyze why coverage alone was insufficient and propose three additional quality or governance measures.
Multiple Choice Questions with AnswersQuestion 1Which statement best describes a modular monolith? A. A system in which every class runs in a separate container B. One deployable application with clear internal boundaries C. A database with no application layer D. A distributed system that cannot share code Correct Answer: B. One deployable application with clear internal boundaries Question 2Under the chapter’s half-open interval convention, when do intervals A and B overlap? A. A.start < B.end and B.start < A.end B. A.start > B.end and B.start > A.end C. A.end = B.start only D. Their start dates must be equal Correct Answer: A. A.start < B.end and B.start < A.end Question 3Which test is most directly concerned with interaction between a repository and a real test database? A. Unit test B. Integration test C. Acceptance criterion D. Static code-formatting check Correct Answer: B. Integration test Question 4What is the primary purpose of a readiness check? A. To determine whether an instance should receive traffic B. To encrypt database records C. To compile Java source code D. To identify the software license Correct Answer: A. To determine whether an instance should receive traffic Question 5Which practice is most appropriate for production secrets? A. Commit them to a private source repository B. Embed them in the container image C. Manage them through protected runtime configuration or a secret store D. Print them in startup logs Correct Answer: C. Manage them through protected runtime configuration or a secret store Question 6Why must the booking conflict check and reservation creation be considered together? A. Java cannot compare dates separately B. Separate operations can allow concurrent requests to create conflicting bookings C. Records cannot contain dates D. Containers do not support transactions Correct Answer: B. Separate operations can allow concurrent requests to create conflicting bookings Question 7Which statement about AI-generated Java code is correct? A. It is correct whenever it compiles B. It eliminates the need for dependency review C. It should be reviewed, tested, and checked for security and licensing concerns D. It may safely receive any confidential data Correct Answer: C. It should be reviewed, tested, and checked for security and licensing concerns Question 8What does data minimization mean? A. Collecting all possible data for future analysis B. Collecting and retaining only data necessary for a stated purpose C. Deleting all application data immediately D. Encrypting data without limiting collection Correct Answer: B. Collecting and retaining only data necessary for a stated purpose Question 9Which artifact lists the software components included in a product? A. SBOM B. HTTP status code C. Liveness probe D. User story Correct Answer: A. SBOM Question 10What is a reasonable characteristic of virtual threads? A. They guarantee faster execution for CPU-intensive algorithms B. They can simplify high-concurrency workloads dominated by blocking I/O C. They remove database capacity limits D. They replace authorization controls Correct Answer: B. They can simplify high-concurrency workloads dominated by blocking I/O
|