Hands-On Software Architecture with Java - Giuseppe Bonocore - E-Book

Hands-On Software Architecture with Java E-Book

Giuseppe Bonocore

0,0
39,59 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.
Mehr erfahren.
Beschreibung

Well-written software architecture is the core of an efficient and scalable enterprise application. Java, the most widespread technology in current enterprises, provides complete toolkits to support the implementation of a well-designed architecture.
This book starts with the fundamentals of architecture and takes you through the basic components of application architecture. You'll cover the different types of software architectural patterns and application integration patterns and learn about their most widespread implementation in Java. You'll then explore cloud-native architectures and best practices for enhancing existing applications to better suit a cloud-enabled world. Later, the book highlights some cross-cutting concerns and the importance of monitoring and tracing for planning the evolution of the software, foreseeing predictable maintenance, and troubleshooting. The book concludes with an analysis of the current status of software architectures in Java programming and offers insights into transforming your architecture to reduce technical debt.
By the end of this software architecture book, you'll have acquired some of the most valuable and in-demand software architect skills to progress in your career.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB
MOBI

Seitenzahl: 744

Veröffentlichungsjahr: 2022

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Hands-On Software Architecture with Java

Learn key architectural techniques and strategies to design efficient and elegant Java applications

Giuseppe Bonocore

BIRMINGHAM—MUMBAI

Hands-On Software Architecture with Java

Copyright © 2022 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Group Product Manager: Aaron Lazar

Publishing Product Manager: Richa Tripathi

Senior Editor: Nisha Cleetus

Content Development Editor: Rosal Colaco

Technical Editor: Maran Fernandes

Copy Editor: Safis Editing

Project Coordinator: Manisha Singh

Proofreader: Safis Editing

Indexer: Hemangini Bari

Production Designer: Joshua Misquitta

Marketing Coordinator: Pooja Yadav

First published: February 2022

Production reference: 1100222

Published by Packt Publishing Ltd.

Livery Place

35 Livery Street

Birmingham

B3 2PB, UK.

ISBN 978-1-80020-730-1

www.packt.com

Contributors

About the author

Giuseppe Bonocore is a solution architect dealing with application development, Java technology, JBoss middleware, and Kubernetes projects since 2014. He has more than 10 years of experience in open source software, in different roles. His professional experience includes Red Hat, Accenture, and Docomo Digital, covering many technical leadership roles and deploying huge open source projects across Europe.

I want to thank the people who have been close to me and supported me, especially my wife.

About the reviewer

Andres Sacco is a technical lead at MercadoLibre and has experience in different languages, such as Java, PHP, and Node.js. In his previous job, he helped to find alternative ways to optimize the transference of data between microservices, which helps to reduce the cost of infrastructure by 55%. Also, he has dictated some internal courses about new technologies, and he has written some articles on Medium.

Stefano Violetta is a creative backend developer with over 14 years of expertise in software development and architecture, managing all stages of the development cycle; he has worked in many different companies, from start-ups to tech giants such as eBay. He likes putting together well-written code that helps to create advanced applications that are fit for purpose, functionally correct, and meet the user's precise needs. On a personal level, he possesses really strong interpersonal skills, being respectful and collaborative. He lives (and works) in the suburbs of Milan, Italy, with his wife and two kids. When he isn't dealing with software, he likes to read and watch movies.

Table of Contents

Preface

Section 1: Fundamentals of Software Architectures

Chapter 1: Designing Software Architectures in Java – Methods and Styles

The importance of software architecture

The objectives of architecture design in the software life cycle

The software architect – role and skills

Is architecture design still relevant in modern development?

Different types of architecture design – from doodling on paper to more accurate modeling

Sketching the main architectural components

Other kinds of architectural diagrams

Common types of architectural diagrams

The changing role of Java in cloud-native applications

Why Java technology is still relevant today

Java usage in enterprise environments

JEE evolution and criticism

Introducing cloud-native Java

The Java microservices ecosystem

Case studies and examples

Case study – mobile payments

Whiteboarding the overall architecture

Software components diagram

Summary

Further reading

Chapter 2: Software Requirements – Collecting, Documenting, Managing

Introducing requirements engineering

Feature, Advantage, and Benefit

Features and technical requirements

Types and characteristics of requirements

The life cycle of a requirement

Discovering and collecting requirements

The lean canvas

Event Storming

More discovery practices

Analyzing requirements

Checking for coherence and feasibility

Checking for explicitness and testability

Checking non-functional requirements and constraints

Specifying requirements according to the IEEE standard

The 830-1998 standard

The 29148 standard

Collecting requirements – formats and tools

Software requirements data to collect

Collecting software requirements in spreadsheets

Specialized tools for software requirements management

Spreadsheets versus tools

Validating requirements

Case studies and examples

The mobile payment application example

Event Storming for peer-to-peer payments

Requirements spreadsheet

Summary

Further reading

Chapter 3: Common Architecture Design Techniques

Introducing marchitectures – impactful and purely demonstrative schemas

Familiarizing ourselves with UML notation

Understanding the background to UML

Class diagrams

Sequence diagram

Wrapping up on UML

Exploring ArchiMate

The ArchiMate Core and Full Frameworks

Navigating the ArchiMate language tree

Comparing ArchiMate to UML

Comparing ArchiMate to TOGAF

Introducing the C4 model

Exploring the C4 model

Filling in the different levels

Other modeling techniques

BPMN

DMN

arc42

Case studies and examples

UML class diagrams for mobile payments

C4 diagrams for mobile payments

Summary

Further reading

Chapter 4: Best Practices for Design and Development

Understanding Domain Driven Design

The anemic domain model

Understanding ubiquitous language

Getting familiar with layered architecture

Learning about the domain model

Glancing at DDD patterns

Bounded Context

Introducing Test Driven Development

Exploring Behavior Driven Development

Comparing DDD, TDD, and BDD

Learning about user story mapping

The MVP

Case studies and examples

The mobile payments domain model

The layered architecture of mobile payments

BDD of mobile payments

User story mapping of mobile payments

Summary

Further reading

Chapter 5: Exploring the Most Common Development Models

Learning about Code and Fix

Glancing at the Waterfall model

Advantages and disadvantages of the Waterfall model

Understanding the Agile methodology

The Agile principles

Introducing Lean software development

Eliminating waste

Deciding as late as possible

Delivering as fast as possible

Optimizing the whole product

Pros and cons of Lean development

Exploring Scrum

Understanding the Scrum teams

Learning about Scrum Events

Understanding Scrum artifacts

Advantages and disadvantages of Scrum

Learning about other Agile practices

Kaizen

Planning Poker

Kanban board

Burndown chart

Understanding DevOps and its siblings

DevOps team size

Roles and responsibilities in a DevOps team

Devs, Ops, and more

DevOps and the bigger organization

Pros and cons of DevOps

Case studies and examples

Summary

Further reading

Section 2: Software Architecture Patterns

Chapter 6: Exploring Essential Java Architectural Patterns

Encapsulation and hexagonal architectures

Hexagonal architectures and Domain Driven Design

Encapsulation and microservices

Learning about multi-tier architectures

Exploring Model View Controller

Server-side MVC

Client-side MVC

Diving into event-driven and reactive approaches

Defining events, commands, and messages

Introducing the event-driven pattern and event-driven architecture

Designing for large-scale adoption

Defining performance goals

Stateless

Data

Scaling

Case studies and examples

Encapsulating with a hexagonal architecture

Componentizing with multi-tier architecture

Planning for performance and scalability

Summary

Further reading

Chapter 7: Exploring Middleware and Frameworks

Technical requirements

Introducing the JEE standard

Diving into JEE implementations

Introducing the WildFly application server

Exploring the WildFly architecture

Running the WildFly server

Understanding the most common JEE APIs

Dependency injection

Jakarta RESTful Web Services

WebSocket

Messaging

Persistence

What's missing in Java EE

What's great about Java EE

Going beyond Java Enterprise Edition

Packaging microservices applications

Introducing MicroProfile

MicroProfile specifications

Exploring Quarkus

Better performances

Developer joy

Quarkus – hello world

Building techniques with Quarkus

Configuration management in Quarkus

Most common Quarkus extensions

Content Dependency Injection

REST services with JAX-RS

WebSockets

Messaging

Persistence

Accelerated ORM development with Panache

Quarkus and the MicroProfile standard

Case studies and examples

Summary

Further reading

Chapter 8: Designing Application Integration and Business Automation

Integration – point-to-point versus centralized

Understanding service-oriented architecture

Enterprise service bus – what and why?

Integration in the cloud-native world

Citizen integration

Digging into enterprise integration patterns

Message routing

Message transformation

System management

Messaging

Exploring formats

XML

JSON

Protobuf

Exploring communication protocols

SOAP and REST

gRPC

GraphQL

Introducing data integration

Completing the picture with business automation

Business rules

Business workflows

Integration versus automation – where to draw the line

Case studies and examples

Integrating payment capabilities

Automating customer onboarding

Summary

Further reading

Chapter 9: Designing Cloud-Native Architectures

Why create cloud-native applications?

Learning about types of cloud service models

Introducing containers and Kubernetes

Defining twelve-factor applications

Twelve-factor apps and the supporting technology

Well-known issues in the cloud-native world

Fault tolerance

Transactionality

Orchestration

Adopting microservices and evolving existing applications

Going beyond microservices

Miniservices

Serverless and Function as a Service

Refactoring apps as microservices and serverless

The five Rs of application modernization

The strangler pattern

Important points for application modernization

Summary

Further reading

Chapter 10: Implementing User Interaction

User interface architecture – backend versus frontend

Web user interface using Jakarta Server Pages and Jakarta Server Faces

Introducing basic Java web technology – Jakarta Server Pages

JSP – the downsides

Jakarta Server Faces – a complex JEE web technology

JSF – the downsides

Introducing single-page applications

Basics of the JavaScript ecosystem

Introducing the React framework

Learning about mobile application development

The importance of mobile applications

The challenges of mobile application development

Mobile application development options

Exploring IVR, chatbots, and voice assistants

Interactive voice response

Chatbots

Voice assistants

Omnichannel strategy in enterprise applications

Summary

Further reading

Chapter 11: Dealing with Data

Exploring relational databases

Keys and relationships

Transactionality

Stored procedures

Commonly used implementations of relation databases

Advantages and disadvantages of relational databases

Introducing key/value stores

Data caching techniques

Data life cycle

Commonly used implementations of key/value stores

The pros and cons of key/value stores

Exploring NoSQL repositories

The CAP theorem

NoSQL database categories

Looking at filesystem storage

The advantages and disadvantages of filesystems

Modern approaches – a multi-tier storage strategy

Summary

Further reading

Section 3: Architectural Context

Chapter 12: Cross-Cutting Concerns

Identity management

Authentication

Authorization

Identity and Access Management

Security

Intrinsic software security

Overall application security

Security standards and regulations

Resiliency

Uptime

Increasing system resiliency

Further techniques for improving reliability

Summary

Further reading

Chapter 13: Exploring the Software Life Cycle

Technical requirements

Source Code Management

Introducing Git

Testing

Unit testing

Beyond unit testing

Further testing considerations

Deploying

Building the code

Managing artifacts

Completing the deployment

Continuous integration/continuous delivery (and deployment)

Common CI/CD software implementations

Releasing

Maintenance

Summary

Further reading

Chapter 14: Monitoring and Tracing Techniques

Technical requirements

Log management

Common concepts in logging frameworks

Log aggregation

Collecting application metrics

Defining application health checks

Application Performance Management

Service monitoring

Summary

Further reading

Chapter 15: What's New in Java?

Java versioning

Vendor ecosystem

What's new in Java 17

Sealed classes

Pattern matching for switch statements

Strongly encapsulating JDK internals

More changes in Java 17

Summary

Further reading

Other Books You May Enjoy

Preface

Despite being a technology born in 1995, Java is still alive and well.

Every now and then, an article pops up saying that Java is old and should be dismissed and replaced by other languages. But the reality is, Java is here to stay.

There are many reasons for that, but the most important is that it just works: it solves common issues in the software development world so well.

Java technology is the main topic of this book. However, in each of the chapters, we will have the opportunity to talk about many different ideas, and I think that most of them go beyond the Java programming language and are likely to be useful in other situations too.

Indeed, in this book, I've tried to distill concepts around many aspects of software development, particularly development in the enterprise world, in big and complex businesses and projects. The goal is to give you insights by focusing on the most important topics. Of course, given the breadth and complexity of the topics, it would be impossible to take a deep dive into every aspect. But you will be provided with some good starting points, and you can easily find more resources if you want further details.

Following more or less the timeline of a typical software project, we will start with the fundamentals of software architecture, from requirement gathering to modeling architecture basics. We will also look at the most common development models, including, of course, DevOps.

In the second section of the book, we will explore some common software architecture patterns. This will include Java architecture patterns, as well as middlewares (both for traditional and cloud-native approaches) and other essential parts of software architecture, such as integration, user interfaces, and data stores.

In the third and final section of the book, we will cover some additional topics, including cross-cutting concerns (such as security, monitoring, and tracing) as well as some considerations around software life cycle management. Finally, we will have a quick look at the latest version of the Java technology.

Who this book is for

This book is for Java software engineers who want to become software architects and learn the basic concepts that a modern software architect needs to know. The book is also for software architects, technical leaders, engineering managers, vice presidents of software engineering, and CTOs looking to extend their knowledge and stay up to date with the latest developments in the field of software architecture.

No previous knowledge is required, and even if you are already familiar with the Java language and the basic concepts of software development, you will still benefit from this book's recap of the different architecture-related topics.

What this book covers

Chapter 1, Designing Software Architectures in Java – Methods and Styles, introduces the approach toward the examples that we will take throughout this book. We will introduce a number of different scenarios and some real-world examples, in order to clarify abstract concepts and shift our point of view toward implementation.

Chapter 2, Software Requirements – Collecting, Documenting, Managing, explains some techniques for requirement gathering and some tools to document and track them.

Chapter 3, Common Architecture Design Techniques, covers the most commonly used architecture definition formats and the goals they aim to achieve. We will look at an example application, described using different architecture diagrams. Moreover, we will walk through some examples of modeling use cases using BPMN and a business rule using DMN.

Chapter 4, Best Practices for Design and Development, is where we will have a look at the different methods that can be used to help us with both our understanding of the overall solution and the implementation of it.

Chapter 5, Exploring the Most Common Development Models, is where we will have an overview of the most common software development models and their implications, including more traditional and historical ones (such as waterfall) as well as more modern approaches such as agile and DevOps.

Chapter 6, Exploring Essential Java Architectural Patterns, looks at architectural patterns. There are some architecture patterns that are so common that they have become more or less standard. While sometimes being overused, these architectures must be considered as basic building blocks that we need to know about in order to solve common architectural problems.

Chapter 7, Exploring Middleware and Frameworks, is where we will see how to use middleware and frameworks, understanding their role in designing and building our architecture.

Chapter 8, Designing Application Integration and Business Automation, is where, as a follow-up to the previous chapter, we will see two typical middleware implementations. Indeed, application integration and business automation are two commonly used middleware functions, used to build efficient and reusable enterprise architectures.

Chapter 9, Designing Cloud-Native Architectures, is where we will have a look at what a cloud-native application is, what the recommended practices are, and how to enhance existing applications to better suit a cloud-enabled world.

Chapter 10, Implementing User Interaction, is where we will detail the omnichannel approach by having a look at the different entry points for customer interaction.

Chapter 11, Dealing with Data, is where we will have a look at the different kinds of data persistence and how and when to mix them together.

Chapter 12, Cross-Cutting Concerns, is where we will summarize the most important cross-cutting topics to be taken into account, including identity management, security, and resilience.

Chapter 13, Exploring Software Life Cycle, will discuss all the ancillary concepts of software development projects, such as source code management, testing, and releasing. This will include some interesting concepts, such as Continuous Integration and Continuous Delivery/Deployment (also known as CI/CD).

Chapter 14, Monitoring and Tracing Techniques, will explore concepts related to the visibility and maintenance of applications running in production. This includes things such as log management, metric collection, and application performance management.

Chapter 15, What's New in Java?, will focus on the latest Java release (17) as well as a bit of the history of the language (including versioning schemes) and the ecosystem of Java vendors.

To get the most out of this book

The code samples provided with this book are generic enough to be run with the most recent Java versions, provided by any vendor. All the most common operating systems (Windows, macOS, and Linux) will work. The build and dependency management tool used is Maven.

The suggested configuration is Java OpenJDK 11 and Apache Maven 3.6. For the React examples, Node.js 8.1 and React 17 were used.

If you are using the digital version of this book, we advise you to type the code yourself or access the code from the book's GitHub repository (a link is available in the next section). Doing so will help you avoid any potential errors related to the copying and pasting of code.

Download the example code files

You can download the example code files for this book from GitHub at https://github.com/PacktPublishing/Hands-On-Software-Architecture-with-Java. If there's an update to the code, it will be updated in the GitHub repository.

We have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!

Download the color images

We also provide a PDF file that has color images of the screenshots and diagrams used in this book. You can download it here: https://static.packt-cdn.com/downloads/9781800207301_ColorImages.pdf.

Conventions used

There are a number of text conventions used throughout this book.

Code in text: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: "Each test method is identified by the @Test annotation."

A block of code is set as follows:

...

@Test

public void testConstructor()

{

Assertions.assertEquals(this.hello.getWho(),

"default");

}

...

Any command-line input or output is written as follows:

mvn io.quarkus:quarkus-maven plugin:1.12.2.Final :create

Bold: Indicates a new term, an important word, or words that you see onscreen. For instance, words in menus or dialog boxes appear in bold. Here is an example: "The following diagram shows you a comparison of IaaS, PaaS, and SaaS."

Tips or Important Notes

Appear like this.

Get in touch

Feedback from our readers is always welcome.

General feedback: If you have questions about any aspect of this book, email us at [email protected] and mention the book title in the subject of your message.

Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/support/errata and fill in the form.

Piracy: If you come across any illegal copies of our works in any form on the internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.

If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.

Share your thoughts

Once you've read Hands-On Software Architecture with Java, we'd love to hear your thoughts! Please click here to go straight to the Amazon review page for this book and share your feedback.

Your review is important to us and the tech community and will help us make sure we're delivering excellent quality content.

Section 1: Fundamentals of Software Architectures

In this section, you will gain all the foundations needed for defining and understanding complex software architectures.

We will start with what software architecture is, the different kinds of it, and the importance of properly defining it. We will then step into the first phases of a software development project, including requirement collection and architecture design.

The focus will then be on best practices for software design and development. Last but not least, we will have an overview of the most common development models, such as waterfall, Agile, and DevOps.

This section comprises the following chapters:

Chapter 1, Designing Software Architectures in Java – Methods and StylesChapter 2, Software Requirements – Collecting, Documenting, ManagingChapter 3, Common Architecture Design TechniquesChapter 4, Best Practices for Design and DevelopmentChapter 5, Exploring the Most Common Development Models

Chapter 1: Designing Software Architectures in Java – Methods and Styles

In this chapter, we will focus on some core concepts that we can use as a base to build on in the upcoming chapters. We will explore different ways to represent the software architecture, paying attention to the intended audience and their specific point of view. Additionally, we will elaborate on the importance of a proper architectural design and its role in the software development life cycle. Following this, we will move on to the Java ecosystem, which is the core topic of this book, to discover why it’s a good choice for implementing a complete enterprise application.

In particular, we will cover the following topics:

The importance of software architectureDifferent types of architecture design – from doodling on paper to more accurate modelingOther kinds of architectural diagramsThe changing role of Java in cloud-native applicationsCase studies and examplesSoftware components diagram

By the end of this chapter, you should have a clear view of why design is a critical part of the software development process and what the main types of architecture schemas are. Additionally, you will become familiar with the role of Java technology in modern application development.

These skills are crucial for implementing functional and elegant software solutions. It will also be a good basis for personal development and career enhancement.

The importance of software architecture

Often, software development is all about cost and time. No one knows exactly why, but the software industry is almost always associated with tight deadlines, insufficient resources, and long hours. Under this kind of pressure, it’s common to question the importance of everything that is not strictly coding. Testing is a common victim of this, along with documentation and, of course, design. But of course, these phases are essential for the success of a project. While we will quickly touch on most of those aspects, architecture design is the core of this book, and I believe that by understanding the practices and goals, the need for it will become clear to everybody.

In this section, we will discover what the fundamental objects of a properly designed architecture are. Highlighting those simple but crucial points is useful in raising awareness about the importance of this phase. If you start advocating those good practices in your team, the quality of your software deliverables will increase.

The objectives of architecture design in the software life cycle

The ultimate goal of this book is not to define the architecture per se; there are plenty of papers and interesting things available on that matter, including the awesome work of Martin Fowler. Nevertheless, there are a couple of considerations that we need to bear in mind.

The architecture should support the crucial decisions within our software project. However, the architecture itself is actually a loose concept, often including different plans (such as physical, logical, network, and more) and points of view (such as users, business logic, machine-to-machine interactions, and more).

Let’s take the most overused metaphor as an example: a software project is like a building. And similarly to a construction project, we require many different points of view, with different levels of detail, ranging from general overviews to detailed calculations and the bills of materials. A general overview is useful to give us an idea of where we are and where we want to go. In addition to this, it is an essential tool for being sure we are on the right path. However, a system overview doesn’t provide enough details for teams such as networking, security, sysops, and, ultimately, the developers that require a more substantiated and quantitative view to drive their day-to-day decisions.

The main goals of designing a proper software architecture include the following:

Prospecting a birds-eye view to project sponsors and investors. While it is not a good practice to drive a business discussion (for example, an elevator pitch) toward technical elements too soon, a higher level of management, venture capitalists, and the like are becoming increasingly curious about technical details, so a high-level overview of the application components can be crucial for winning this kind of discussion.Defining a shared lingo for components of our solution, which is crucial for collaborating across the team.Providing guidance for technological choices since putting our design decisions on paper will clarify important traits of our application. Will data be central?Do we need to focus on multiple geographies?Are user interactions the most common use case? Some of those reasonings will change over time. However, correctly designing our application will drive some crucial technology choices, in terms of choosing components and stacks to rely on.Splitting roles and responsibilities. While a proper project plan, a statement of work, or a Responsible, Accountable, Consulted, Informed (RACI) (which is a classical way to categorize who does what) table will be used for real project management, writing the software backbone down on paper is our first look at who we have to involve for proper project execution.

Indeed, the architecture is an excellent example of planning in advance. However, a proper software architecture should be much more than a technological datasheet.

Architecture, as with buildings, is more about the styles and guidelines to be followed all around the project. The final goal of a piece of software architecture is to find elegant solutions to the problems that will arise during the project plan. Ultimately, it will act as guidance throughout the project’s life cycle.

The software architect – role and skills

As a role, the software architect is often identified as the more senior technical resource in the IT team. In fact, the job role of an architect is almost always seen as a career progression for developers, especially in enterprise environments. While not necessary, being good at coding is crucial for a complete comprehension of the overall functioning of the system.

There are several different other skills that are required to be a successful architect, including creativity, the ability to synthesize, and vision. However, above all, experience is what it takes to become an architect.

This includes firsthand experience on many different projects, solving real-world issues: what a proper software design looks like and how the design has evolved. This skillset is very useful to have in the background of the architect.

Additionally, it’s vital to have a huge library of solutions to choose from in order to avoid reinventing the wheel. While we love to think that our problem is very unique, it’s very unlikely to be so.

This leads us to the approach that we will use in this book: we will not focus on just one aspect or technology to drill down on, but we will take a horizontal approach, discussing a number of different topics and offering ideas on how to approach potential problems. We hope to act as a handbook to support you when making real-world choices.

Is architecture design still relevant in modern development?

There will be a couple of chapters dedicated to discussing Microservices, DevOps, and the cloud-native avalanche, but it’s safe to assume that in one form or another, you will have plenty of opportunities to hear something about them.

As you might have gathered, most of these concepts are not really new. The Agile Manifesto, which is a seminal work detailing some of the practices commonly used in modern development techniques, was published in 2001, yet most of the common-sense principles it contains are misinterpreted. When I was working in IT consulting back in 2008, a common joke among development teams was "Yes, we do agile. We skip documentation and testing."

Of course, that’s just an opinion based on personal experience. There are plenty of teams who do not underestimate the importance of proper planning and documentation and are doing wonderfully while working with Agile. Yet, in some cases, less structured development methodologies have been taken as an excuse to skip some crucial steps of the development life cycle.

As we will elaborate, in Chapter 5, Exploring the Most Common Development Models, Agile is much more than slimming down boring phases of the project. Indeed, testing and documentation are still very relevant, and Agile is no excuse to skip that.

There are plenty of reflections you can take in terms of how to adapt your design techniques to DevOps, Agile, and more, and we will discuss this topic later in this book. However, one thing is certain: architecture matters. Design is very relevant. We have to spend the correct amount of time planning our choices, revisiting them when needed, and generally, sticking with some well-defined guiding principles. The alternative is poor quality deliverables or no deliverables at all.

Now, let’s take a look at what the first phases of software design usually look like.

Different types of architecture design – from doodling on paper to more accurate modeling

When we start to shape the architecture of a new application, the result is often familiar.

I would say that across different geographies, industries, and application types, some elements are common. The architectural sketches are usually made of boxes and lines, with labels, arrows, and similar artifacts. That’s an intuitive way to shape our thoughts on paper.

However, in the following section, we will go through different ways of expressing those concepts. This will make us aware of available styles and techniques and will make our diagram clearer and, ultimately, easier to share and understand.

But first, let’s find out what the characteristics of architectural sketching actually are.

Sketching the main architectural components

As we discussed earlier, there are a number of different components that are recurrent in a high-level architectural sketch. Let’s examine them one by one:

Boxes: These represent the software components. They can refer to one complete application or specific subcomponents (such as packages, modules, or similar things).Lines: These describe the relationships between the boxes. Those links imply some sort of communication, commonly in the form of APIs. The lines can also represent inheritance or a grouping of some sort. A direction (that is, an arrow) can also be specified.Layers: These are a dotted or dashed line, grouping components and their relationships. They are used to identify logical slices of the architecture (such as the frontend, backend, and more), the grouping of subcomponents (for example, validation and business logic), network segments (such as the intranet and DMZ), physical data centers, and more.Actors: Simulating the interactions of users within the systems, actors are usually represented as stickmen, sitting on top of some components (usually frontends or UIs of some sort). It is not uncommon to observe different channels represented, in the form of laptops or mobile phones, depending on the industry and type of application (for example, ATMs, branch offices, and physical industrial plants).

Now, let’s view an example sketch:

Figure 1.1 – The common components on a first architectural sketch

As we’ve already mentioned, the quick and dirty representation shown in this diagram is useful since it’s an easy way to start thinking about how our application should look. However, on a closer look, there are some common inaccuracies:

The software components (that is, our boxes) might be represented with different levels of zoom: sometimes representing applications, sometimes features, and sometimes software modules. This is inconsistent and could generate confusion.Some components are specialized (for example, databases), while others are not. As noted in the preceding point, this leads to an inhomogeneous view. In some parts of the diagram, we are representing use cases or information flows (for example, with the actors), while elsewhere, we are drawing a static picture of the components.Some points of view don’t cope well with others because we might be representing network firewalls but not referencing any other networking setup.

Now that we’ve learned what a naïve representation looks like and what its limits are, let’s take a look at some other types of diagrams and how they represent alternative points of view.

Other kinds of architectural diagrams

As we discovered in the previous section, the first sketches of a piece of architecture often end up as an intuitive and naïve view, lacking essential details. In this section, we will look at an overview of different types of architectural diagrams. This will help us to pick the right diagram for the right situation, defining a clearer view of our architecture. So, let’s dig into some details.

Common types of architectural diagrams

In order to define a clearer and more detailed view of what our software will look like, it’s essential to start picking layers and points of view to represent. This will naturally lead us to focus on more tailored designs. While not exhaustive, a list of possible architectural diagrams includes the following:

Software components: This kind of schema includes different software modules (such as applications or other components) and the interaction between them (for example, read from, write to, listen, and more). One particular instance of this diagram could include protocols and formats of communication between those components, becoming close to a complete API documentation:

Figure 1.2 – Software components diagram

Network architecture: This is a pretty common design type and is often considered the more scientific and detailed one. It includes data such as network segments (DMZ and INTRANET), Firewall, IP addressing, and more:

Figure 1.3 – Network architecture diagram

Physical architecture: This is a mapping of software modules into server deployments. Usually, it’s complete with information about the server hardware and model. In the case of a multiple datacenter setup (which is pretty common in enterprise environments), it can also contain details about racks and rooms. Storage is another relatively common component. Depending on the implementation, this architecture might include information about virtualization technology (for example, the mapping of VMS to the physical servers that are hosting it). Additionally, it could, where relevant, include references to cloud or container deployments:

Figure 1.4 – Physical architecture diagram

These are the very basic points of view in an architecture diagram and an essential starting point when detailing the design of your application. Diving further into the application specification life, other kinds of diagrams, often derivatives of those, could be elaborated (for example, cloud deployment diagrams, software modules, and more) depending on your specific needs. In the next section, we will focus on Java technology, which is the other fundamental topic of this book and crucial for completing our architectural view of modern applications.

The changing role of Java in cloud-native applications

Now that we’ve briefly touched on the various kinds of designs and diagrams of an application, let’s focus on the other fundamental topic of this book: the Java language.

It’s not uncommon to hear that Java is dead. However, if you are reading this book, you probably agree that this is far from the truth.

Of course, the panorama of software development languages for enterprise applications is now wider and more complicated than the golden age of Java; nevertheless, the language is still alive and widespread, especially in some areas.

In this section, we will explore the usage of Java technology in the enterprise software landscape. Then, we will take a quick glance at the history of Java Enterprise Edition (JEE). This will be a good foundation to understand existing enterprise architectures and model modern, cloud-native applications based on this technology.

Now, let’s examine why Java technology is still thriving.

Why Java technology is still relevant today

The most important reason for Java’s popularity is probably the availability of skill. There are plenty of experts on this language, as many polls and studies show (for example, PYPL and Tiobe). Another crucial point is the relevance of the ecosystem, in terms of the quantity and quality of libraries, resources, and tooling available for the Java platform.

Rewriting complex applications (including their dependencies) from Java to another language could probably take years, and, long story short, there might be no reason to do that. Java just works, and it’s an incredibly productive platform. It might be slow and resource-intensive in some scenarios, but this is balanced by its stability. The language has been battle-tested, is feature-rich, and essentially, covers all the use cases required in an enterprise, such as transactionality, integration with legacy environments, and manageability.

Now, let’s take a look at where and how Java technology is used in enterprise environments. This can be very useful to understand existing scenarios and fit new applications into existing application landscapes.

Java usage in enterprise environments

In order to fit our Java application in the overall architecture, it’s important to understand the typical context of a large enterprise, from a software architecture perspective.

Of course, the enterprise architecture depends a lot on the industry domain (for instance, banking, telecommunications, media, and more), geography, and the tenure of the organization, so my vision might be slightly biased toward the segment I have worked with for the longest (a large enterprise in the EMEA area). Still, I think we can summarize it as follows:

Legacy: Big applications, usually running very core functions of the enterprise for many years (at least more than 10 and commonly more than 20). Needless to say, the technology here is not the most current (Cobol is widespread in this area, but it is not uncommon to see other things such as PL SQL, huge batch scripts, and even C/C++ code). However, the language is seldom an issue here. Of course, nowadays, those skills are very rare to find on the job market, but usually, the software just works. The point here is that most of the time, nobody exactly knows what the software does, as it’s poorly documented and tested. Moreover, you usually don’t have automated release procedures, so every time you perform a bugfix, you have to cross your fingers. Needless to say, a proper testing environment has never been utilized, so most of the things have to be tested in production.Web (and mobile): This is another big chunk of the enterprise architecture. Usually, it is easier to govern than legacy but still very critical. Indeed, by design, these applications are heavily customer-facing, so you can’t afford downtime or critical bugs. In terms of technologies, the situation here is more fragmented. Newer deployments are almost exclusively made of Single-Page Applications (SPAs) based on JavaScript (implemented with frameworks such as Angular, Vue, and React). Backends are REST services implemented in JavaScript (Node.js) or Java. Business applications: Often, the gap between web applications and business applications is very thin. Here, the rule of thumb is that business applications are less web-centric (even if they often have a web GUI), and usually, they are not customer exposed. The most common kind of business application is the management of internal back-office processes. It’s hard to find a recurrent pattern in business applications since it’s an area that contains very different things (such as CRMs, HR applications, branch office management, and more). BigData: Under various names and nuances (such as data warehouses, data lakes, and AI), BigData is commonly a very huge workload in terms of the resources required. Here, the technologies are often packaged software, while custom development is done using various languages, depending on the core engine chosen. The most common languages in this area are Java (Scala), R (which is decreasing in popularity), and Python (which is increasing in popularity). In some implementations, a big chunk of SQL is used to stitch calculations together.Middlewares and infrastructure: Here falls everything that glues the other apps together. The most common pattern here is the integration (synchronous or asynchronous). The keywords are ESB, SOA, and messaging. Other things such as Single Sign-On and identity providers can be included here.

As I mentioned, this is just a coarse-grained classification, useful as reference points regarding where our application will fit and which other actor our application will be interacting with.

Notice that the technologies mentioned are mostly traditional ones. With the emergence of modern paradigms (such as the cloud, microservices, and serverless), new languages and stacks are quickly gaining their place. Notable examples are Go in the microservice development area and Rust for system programming.

However, those technologies and approaches are often just evolutions (or brand-new applications) belonging to the same categories. Here, the most interesting exception is in the middleware area, where some approaches are decreasing in popularity (for example, SOA) in favor of lighter alternatives. We will discuss this in Chapter 7, Exploring Middleware and Frameworks.

Now that we’ve explored the widespread usage of Java in an enterprise context, let’s take a look at its recent history.

JEE evolution and criticism

JEE, as we have learned, is still central in common enterprise applications. The heritage of this language is just great. The effort that has been done in terms of standardizing a set of APIs for common features (such as transactionality, web services, and persistence) is just amazing, and the cooperation between different vendors, to provide interoperability and reference implementation, has been a very successful one.

However, in the last couple of years, a different set of needs has emerged. The issue with JEE is that in order to preserve long-term stability and cross-vendor compatibility, the evolution of the technology is not very quick. With the emergence of cloud and more modular applications, features such as observability, modular packaging, and access to no SQL databases have become essential for modern applications. Of course, standards and committees have also had their moments, with developers starting to move away from vanilla implementations and using third-party libraries and non-standard approaches.

Important Note:

The objective of this book is not to recap the history and controversy of the JEE platform. However, organizational issues (culminating with the donation of the project to the Eclipse Foundation) and less frequent releases have contributed to the decrease in popularity of the platform.

The upcoming of the Platform-as-a-Service (PaaS) paradigm is another important event that is changing the landscape. Modern orchestration platforms (with Kubernetes as the most famous example), both in the cloud or on-premises, are moving toward a different approach. We will examine this in greater detail later, but essentially, the core concept is that for the sake of scalability and control, some of the typical features of the application server (for example, clustering and the service registry) are delegated to the platform itself. This has a strict liaison with the microservice approach and the benefits they bring. In the JEE world, this means that those features become duplicated.

Another point is about containerization. One of the focal points of container technology is immutability and its impacts in terms of stability and the quality of the applications. You package one application into a container and easily move it between different environments. Of course, this is, not in the same direction as JEE servers, which have been engineered to host multiple applications, managing hot deploys and live changes of configurations.

A further consideration regarding application servers is that they are, by design, optimized for transaction throughput (often at the expense of startup times), and their runtime is general-purpose (including libraries covering many different use cases). Conversely, the cloud-native approach is usually aimed at a faster startup time and a runtime that is as small as possible, bringing only the features needed by that particular application. This will be the focus of our next section.

Introducing cloud-native Java

Since the inception of the microservices concept, in the Java development community, the paradigm has increasingly shifted toward the fat jar approach. This concept is nothing new, as the first examples of uber jars (a synonym of the fat jar) have been around since the early 2000s, mainly in the desktop development area. The idea around them is pretty simple: instead of using dynamic loading of libraries at runtime, let’s package them all together into an executable jar to simplify the distribution of our application. This is actually the opposite of the model of the application servers, which aim to create an environment as configurable as possible, supporting things such as hot deployment and the hot-swapping of libraries, privileging the uptime to immutability (and predictability).

In container-based and cloud-native applications, fat jar approaches have begun to be viewed as the perfect candidate for the implementation of cloud-native, microservices-oriented applications. This is for many different reasons:

Testability: You can easily run and test the application in a local environment (it’s enough to have a compatible Java Virtual Machine or JVM). Moreover, if the interface is properly defined and documented, it’s easy to mock other components and simulate integration testing.Ease of installation: The handover of the application to ops groups (or to testers) is pretty easy. Usually, it’s enough to have the .jar file and configuration (normally, on a text file or environment variable).Stability across environments: Since everything is self-contained, it’s easy to avoid the works-on-my-machine effect. The development execution environment (usually, the developer machine) is designed pretty similarly to the production environment (aside from the configuration, which is usually well separated from the code, and of course, the external systems such as the databases). This behavior mirrors what is provided by containers, and it’s probably one of the most important reasons for the adoption of this approach in the development of microservices.

There is one last important consideration to pay attention to: curiously enough, the all-in-one fat jar approach, in contrast with what I’ve just said, is theoretically conflicting with the optimization provided by the containerization.

Indeed, one of the benefits provided by every container technology is layerization. Put simply, every container is composed by starting with a base image and just adding what’s needed. A pretty common scenario in the Java world is to create the application as a tower composed of the operating system plus the JVM plus dependencies plus the application artifact. Let’s take a glance at what this looks like in the following diagram. In gray, you will see the base image, which doesn’t change with a new release of the application. Indeed, a change to the application artifact means only redeploying the last layer on top of the underlying Base Image:

Figure 1.5 – Layering container images

As you can see in the preceding diagram, the release in this scenario is as light as simply replacing the Application Artifact layer (that is, the top layer).

By using the fat jar approach, you cannot implement this behavior. If you change something in your application but nothing in the dependencies, you have to rebuild the whole Fat JAR and put it on top of the JVM layer. You can observe what this look like in the following diagram:

Figure 1.6 – Layering container images and fat jars

In this scenario, the release includes all of the application dependencies, other than the application by itself.

While this might appear to be a trivial issue, it could mean hundreds of megabytes copied back and forth into your environment, impacting the development and release time since most of the things composing the container cannot be cached by the container runtime.

Some ecosystems do a bit of experimentation in the field of hollow jars to essentially replicate an approach similar to the application server. Here, the composed (fat) jar is split between the application layer and the dependencies layer in order to avoid having to repackage/move everything each time. However, this approach is far from being widespread.

The Java microservices ecosystem

One last consideration goes to the ecosystem in the Java microservices world. As we were beginning to mention earlier, the approach here is to delegate more things to the platform. The service itself becomes simpler, having only the dependency that is required (to reduce the size and the resource footprint) and focusing only on the business logic.

However, some of the features delegated to the application server are still required. The service registry, clustering, and configuration are the simplest examples that come to mind.

Additionally, other, newer needs start to emerge:

HealthCheck is the first need. Since there is no application server to ensure your application is up and running, and the application is implemented as more than one running artifact, you will end up having to monitor every single microservice and possibly restarting it (or doing something different) if it becomes unhealthy. Visibility is another need. I might want to visualize the network of connections and dependencies, the traffic flowing between components, and more. Last but not least: resiliency. This is often translated as the circuit breaker even if it’s not the only pattern to help with that. If something in the chain of calls fails, you don’t want the failure to cascade.

So, as we will discover in the upcoming chapters, a new ecosystem will be needed to survive outside the JEE world.

Microservices has been a groundbreaking innovation in the world of software architectures, and it has started a whole new trend in the world of so-called cloud-native architectures (which is the main topic of this book). With this in mind, I cannot avoid mentioning another very promising paradigm: Serverless.

Serverless borrows some concepts from microservices, such as standardization and horizontal scaling, and takes it to the extreme, by relieving the developer of any responsibility outside the code itself and delegating aspects such as packaging and deployment to an underlying platform. Serverless, as a trend, has become popular as a proprietary technology on cloud platforms, but it is increasingly used in hybrid cloud scenarios.

Java is not famous in the serverless world. The need for compilation and the weight added by the JVM has, traditionally, been seen as a showstopper in the serverless world. However, as we will explore further in Chapter 9, Designing Cloud-Native Architectures, Java technology is now also gaining some momentum in that area.

And now, in order to better clarify different architectural designs, we will examine some examples based on a reference case study.

Case studies and examples

Following up on the handbook approach, each time we face a complex concept, I will try to clarify it by providing case studies. Of course, while the cases are not real (for reasons you can imagine), the challenges closely resemble several first-hand experiences I’ve incurred in my professional history.

In this section, we will start from scratch by designing a piece of software architecture. Then, we will add details to portray a more precise view. This will help you to better understand the first steps in the design of a complex piece of architecture.

Case study – mobile payments

In this case study, we will simulate the architecture design of a mobile payment solution. As contextual background, let’s suppose that a huge bank, in order to increase the service offering toward their customers and following some market research, wants to implement a mobile payment application. By definition, a mobile payment is a pretty broad term, and it includes many different use cases involving financial transactions completed using smartphones.

In this particular implementation, we will consider the use case of paying with your smartphone by charging you via your mobile phone bill.

Essentially, this means implementing a client-server architecture (with the clients implemented as a mobile application), interacting both with existing enterprise applications and external systems exposed by telecommunication operators. Now, let’s now try to analyze some use cases related to this scenario and model it by using the different schemas we’ve discussed so far.

Whiteboarding the overall architecture

Beginning on white space, let’s start whiteboarding the overall architecture. As we’ve learned, the first step is usually to sketch, at a high level, the relevant modules and the relationships between them. It’s not important to be super detailed, nor to use a particular style. We are just brainstorming the first shapes on paper:

Figure 1.7 – Architecture whiteboarding

Here, we have drafted a birds-eye view of the use case. We now know where the transaction starts, where the data is saved, and how the user interacts with the system.

Additionally, we have identified the main components of the application:

The mobile application (represented together with the user)The (CDN) to serve static resources to the applicationThe (CMS) to configure content to be delivered to the appThe backend (mobile Backend as a Service or mBaaS) to proxy requests and responsesThe business logic of the applicationSession and Cache, to store non-persistent data of the usersDatabase, to store persistent dataOther parts of the application: reporting and data warehousing, authentication, Transactional Backend, and Customer Relationship Management (CRM)

As expected, this kind of design has some intrinsic issues:

You can observe mixed-use cases (both of the mobile user and the CMS administrator), which can be foreseen by the arrows between different components, but it’s barely designed.There is a view in the project timeline regarding the implementation of components (reporting and data warehousing appear to be optional in the first phase of the project).

Some protocols in the interactions are named (for example, SOAP and REST), but it’s not an API specification, nor a network schema. Anyway, even if it’s not super detailed, this schema is a good starting point. It helps us to define the main application boundaries, it gives a high-level overview of the integration points, and overall, it’s a good way to kick off a more detailed analysis. We will improve on this in the next section.

Software components diagram

In order to address some of the issues highlighted in the previous section, I’ve modeled the same system by focusing on software components. This does not follow any specific standard even if is pretty similar to the C4 approach (where C4 stands for Context, Containers, Components, and Code; we will discuss this further in later chapters):

Figure 1.8 – Software components diagram

As you can see, this schema is more homogeneous and better organized than the first sketch. At a first glance, you can view what features are provided to the user. Additionally, it highlights how the system interacts with each other in a structured way (for example, using API calls, reads and writes, and more).

Compared to the first sketch, there are some considerations that we can observe:

The components are almost the same as the other schema.The diagram is less focused on the use case, even if the user is still represented (together with a high-level recap of the features available to them).There is no view on the project phases. This helps you to focus on just one point of view (architectural components), making the schema less confusing.No protocols are named, only high-level interactions (such as reads, writes, and API calls).Some technical components are preserved (the database), while others are skipped since they have less impact on the functional view (for example, the CDN, which is probably more relevant on a network schema).

In this section, we learned how to approach the first design of our mobile payments application; first, with a more naïve view, then by trying to detail the view in a more structured way. In the upcoming chapters, we will discuss how to further clarify and enrich those views.

Summary

In this first chapter, we just scratched the surface on the two most essential topics of this book: the different types of architectural design and the relevance of Java technology in the enterprise world.

We have discovered what the first sketches of our software architecture look like and why they are relevant, even if they are not very detailed. Then, we moved on to different schemas (such as software components, the infrastructure, and the network) to get a glimpse of other schema styles, which is useful to address specific areas of interest. On the Java side, we made some considerations about the role of Java in the enterprise landscape and how the language is evolving to meet the challenges of modern cloud environments.

These concepts will be useful starting points for the two core concepts of this book. On the architectural side, we’ve grasped how complex and important it is to view, analyze, and design a proper architecture. From a technological point of view, we’ve learned how Java, the technology we will focus on for the rest of this book, is very widespread in the enterprise context and how it is still relevant for building modern, cloud-native applications.

In the next chapter, we will start working with requirements. Requirement gathering and specifications are essential in order to rework our architectural design, adding more details and ensuring the final product will meet customer expectations.

Further reading

Who Needs an Architect? by Martin Fowler (http://files.catwell.info/misc/mirror/2003-martin-fowler-who-needs-an-architect.pdf)Don’t Put Fat Jars in Docker Images by Philipp Hauer (https://phauer.com/2019/no-fat-jar-in-docker-image)

Chapter 2: Software Requirements – Collecting, Documenting, Managing

Collecting requirements is arguably one of the most frustrating activities in software production for several reasons. Difficulties often arise because it is never completely clear who the owner is, as well as because architects cannot do a good design without certain requisites, and developers, of course, can't do a proper job without the designs.

However, it is fairly common practice for a development team to start doing something without a complete requirements collection job because there is no time. Indeed, what often happens, especially in regards to large and complex projects, is that the milestones are put in place before the project scope is completely defined. In this industry, since software is an intangible product (not like a building or a bridge), budget approval is usually a more fluid process. Therefore, it's not unusual to have a project approved before all the details (including requirements, feasibility, and architectural design) are fully defined. Needless to say, this is an inherently bad practice.

In this chapter, we will look at different techniques for requirements gathering and analysis in order to increase the quality of our software deliverables.

You will learn about the following:

The different types of requirements: functional and non-functionalWhat characteristics a requisite must haveHow to formalize requirements in standard formatsHow to collect requirements by using agile and interactive techniques