Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

This project is a simplified tax filing application built to mirror the tech stack and architecture of the Intuit TurboTax Tax Filing team.

What This Project Covers

  • Backend: A Spring Boot (Java 21) service that orchestrates tax return submissions, saves returns to PostgreSQL, and simulates handing off filing data to a downstream e-Filing service.
  • Frontend: A React application providing a tax return form, a “Finish and File” review/print experience, and basic agent-facing views.
  • Infrastructure: Redis for caching, Kubernetes for deployment, and Maven for builds.

Mapping to the Real Team

This ProjectTurboTax Tax Filing Team
Tax return CRUD APIBackend orchestration of tax returns
Simulated e-Filing handoffSending data to the EFE (e-Filing) team
Print/download return“Finish and File” print experiences
React frontendAppFabric-based dynamic experiences
Kubernetes deploymentIKS (Intuit Kubernetes Service)
Maven buildMaven + Jenkins CI/CD

Development Environment

Prerequisites

ToolVersionPurpose
Java (JDK)25+Backend runtime
Maven3.9+Build and dependency management
Node.js20+Frontend runtime
npm/yarnlatestFrontend package management
Docker24+Containerization
kubectl1.28+Kubernetes CLI
minikubelatestLocal Kubernetes cluster
PostgreSQL16+Database
Redis7+Cache
mdbooklatestDocumentation

Installation

Java 25

# Arch Linux
sudo pacman -S jdk25-openjdk

# Verify
java --version

Maven

# Arch Linux
sudo pacman -S maven

# Verify
mvn --version

Node.js

# Arch Linux
sudo pacman -S nodejs npm

# Verify
node --version
npm --version

Docker

# Arch Linux
sudo pacman -S docker
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
# Log out and back in for group change to take effect

PostgreSQL & Redis (via Docker)

No local installation needed. Run them as containers:

# PostgreSQL
docker run -d --name postgres \
  -e POSTGRES_DB=intuit_crash_course \
  -e POSTGRES_USER=dev \
  -e POSTGRES_PASSWORD=dev \
  -p 5432:5432 \
  postgres:16

# Redis
docker run -d --name redis \
  -p 6379:6379 \
  redis:7

To stop/start later:

docker stop postgres redis
docker start postgres redis

Kubernetes (minikube)

# Arch Linux
sudo pacman -S minikube kubectl

# Start cluster
minikube start

Running the Project

Backend

cd backend
mvn spring-boot:run

The API will be available at http://localhost:8080.

Frontend

cd frontend
npm install
npm run dev

The app will be available at http://localhost:5173.

Documentation

cd doc
mdbook serve --open

IDE Setup

  • Import as Maven project
  • Set Project SDK to Java 25
  • Install Spring Boot plugin

VS Code (for React frontend)

  • Install extensions: ESLint, Prettier, ES7+ React snippets
  • Install extension: Extension Pack for Java (if working on backend too)

Architecture

System Overview

                         +------------------+
                         |   React Frontend |
                         |   (Vite + React) |
                         +--------+---------+
                                  |
                                  | REST API
                                  |
                         +--------v---------+
                         |  Spring Boot API |
                         |    (Java 25)     |
                         +--+----------+----+
                            |          |
                  +---------+          +----------+
                  |                               |
          +-------v-------+             +---------v--------+
          |  PostgreSQL    |             |      Redis       |
          |  (persistence) |             |     (cache)      |
          +----------------+             +------------------+

Components

Backend (Spring Boot)

The backend is a REST API built with Spring Boot and Java 25. It is responsible for:

  • Managing tax return data (CRUD operations)
  • Orchestrating the filing workflow (save, validate, submit)
  • Simulating the handoff to an e-Filing service (mimicking the EFE team)
  • Generating printable/downloadable return documents

Key layers:

  • Controller: REST endpoints
  • Service: Business logic and orchestration
  • Repository: Data access via Spring Data JPA
  • Model/Entity: Domain objects mapped to database tables

Frontend (React)

The frontend provides:

  • A tax return input form
  • A “Finish and File” review and print experience
  • Return status tracking
  • An agent-facing view for filed return information

Database (PostgreSQL)

Stores tax return data, user information, and filing status history.

Cache (Redis)

Used for:

  • Caching frequently accessed return data
  • Session management
  • Rate limiting (optional)

API Design

The API follows REST conventions:

MethodEndpointDescription
POST/api/returnsCreate a new tax return
GET/api/returns/{id}Get a tax return by ID
GET/api/returnsList all returns
PUT/api/returns/{id}Update a tax return
DELETE/api/returns/{id}Delete a tax return
POST/api/returns/{id}/fileSubmit a return for filing
GET/api/returns/{id}/statusGet filing status
GET/api/returns/{id}/printGet printable return

Backend

Overview

The backend is a Spring Boot application using Java 25 and Maven. It provides REST APIs for tax return management and filing orchestration.

Project Layout

backend/
  src/
    main/
      java/com/example/taxfiling/
        controller/       # REST controllers
        service/          # Business logic
        repository/       # Spring Data JPA repositories
        model/            # Entity classes
        dto/              # Data transfer objects
        config/           # Spring configuration
        exception/        # Custom exceptions and error handling
      resources/
        application.yml   # Application configuration
  pom.xml                 # Maven build file

Key Dependencies

  • spring-boot-starter-web - REST API
  • spring-boot-starter-data-jpa - Database access
  • spring-boot-starter-data-redis - Redis caching
  • spring-boot-starter-validation - Input validation
  • spring-boot-starter-test - Testing
  • postgresql - PostgreSQL JDBC driver
  • lombok - Boilerplate reduction

Configuration

Application configuration is in application.yml:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/intuit_crash_course
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  data:
    redis:
      host: localhost
      port: 6379

server:
  port: 8080

Testing

# Run all tests
mvn test

# Run with coverage
mvn test jacoco:report

Spring Boot Basics

Project Structure

A file-by-file explanation of the Spring Boot project generated by Spring Initializr.

Project Tree

backend/
  pom.xml                          # Maven build definition
  mvnw                             # Maven wrapper script
  .mvn/wrapper/
    maven-wrapper.properties       # Maven wrapper config
  src/
    main/
      java/com/example/taxfiling/
        TaxfilingApplication.java  # Application entry point
        controller/
          HealthController.java    # REST controller (we added this)
      resources/
        application.properties     # Spring Boot configuration
        static/                    # Static files (HTML, CSS, JS) served directly
        templates/                 # Server-side templates (Thymeleaf, etc.)
    test/
      java/com/example/taxfiling/
        TaxfilingApplicationTests.java  # Test class
  target/                          # Build output (gitignored)

pom.xml - The Project Definition

POM stands for “Project Object Model”. It tells Maven everything about your project.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.0</version>
</parent>

Parent POM: Inherits from spring-boot-starter-parent, which pre-configures a lot of defaults (compiler settings, dependency versions, plugin configs). You don’t need to specify versions for most Spring dependencies because the parent manages them.

<properties>
    <java.version>25</java.version>
</properties>

Java version: Tells the Maven compiler plugin to use Java 25.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Dependencies: “Starters” are curated dependency bundles:

  • spring-boot-starter-web pulls in Spring MVC, embedded Tomcat, Jackson (JSON), and everything needed for a REST API. This single line replaces dozens of individual dependency declarations.
  • spring-boot-starter-test pulls in JUnit 5, Mockito, Spring Test, and other testing tools. <scope>test</scope> means it’s only available during testing, not in production.
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Build plugin: Enables mvn spring-boot:run and packages the app into a fat JAR (a single .jar file containing your code + all dependencies + embedded Tomcat).

mvnw - Maven Wrapper

A shell script that downloads and runs a specific Maven version, so everyone on the team uses the same Maven version regardless of what’s installed locally. Use ./mvnw instead of mvn for reproducible builds. The version is configured in .mvn/wrapper/maven-wrapper.properties.

You already have Maven installed, so you can use either mvn or ./mvnw - they do the same thing.

TaxfilingApplication.java - Entry Point

@SpringBootApplication
public class TaxfilingApplication {
    public static void main(String[] args) {
        SpringApplication.run(TaxfilingApplication.class, args);
    }
}

This is the entire application bootstrap. @SpringBootApplication is a shortcut that combines three annotations:

  1. @Configuration - This class can define beans (objects managed by Spring)
  2. @EnableAutoConfiguration - Spring Boot automatically configures things based on your dependencies (e.g., it sees spring-boot-starter-web and sets up Tomcat + Spring MVC)
  3. @ComponentScan - Scans the current package and sub-packages for classes annotated with @Controller, @Service, @Repository, etc., and registers them automatically

SpringApplication.run() starts the embedded Tomcat server, initializes the Spring context, and your app is live.

HealthController.java - REST Controller

@RestController
public class HealthController {
    @GetMapping("/")
    public Map<String, String> hello() {
        return Map.of("message", "Tax Filing API is running");
    }
}
  • @RestController = @Controller + @ResponseBody. It tells Spring this class handles HTTP requests and return values are serialized directly to JSON (not rendered as a view template).
  • @GetMapping("/") maps HTTP GET requests to / to this method.
  • The Map return value is automatically serialized to {"message":"Tax Filing API is running"} by Jackson (included via starter-web).

Spring discovered this class automatically because it’s in a sub-package of com.example.taxfiling (where @ComponentScan starts).

application.properties - Configuration

spring.application.name=taxfiling

Currently minimal. This is where you configure database connections, server port, logging, etc. Spring Boot has sensible defaults for everything (e.g., port 8080), so you only override what you need. Can also be written as application.yml.

TaxfilingApplicationTests.java - Smoke Test

@SpringBootTest
class TaxfilingApplicationTests {
    @Test
    void contextLoads() {
    }
}

@SpringBootTest starts the full application context. The contextLoads() test has no assertions - it passes as long as the application starts without errors. This catches configuration mistakes, missing beans, and circular dependencies early.

Run with: mvn test

target/ Directory

Generated by Maven when you build or run. Contains:

  • classes/ - Compiled .class files and copied resources
  • test-classes/ - Compiled test classes
  • *.jar - Packaged application (after mvn package)

This directory is gitignored. Never commit it.

How It All Fits Together

  1. You run mvn spring-boot:run
  2. Maven compiles src/main/java/** into target/classes/
  3. Maven calls TaxfilingApplication.main()
  4. @SpringBootApplication triggers auto-configuration and component scanning
  5. Spring finds HealthController via @ComponentScan, registers it
  6. Embedded Tomcat starts on port 8080
  7. HTTP GET / hits HealthController.hello(), returns JSON

Annotations

Annotations are the primary way you configure Spring Boot. Instead of XML config files, you put @Something on a class/method/field and Spring handles the rest.

How Annotations Work in Spring

Spring Boot starts up, scans your code for annotations, and builds an application context - a container of objects (called beans) that Spring manages. Annotations tell Spring:

  • What objects to create
  • How to wire them together
  • How to map HTTP requests to code

Core Annotations

@SpringBootApplication

The root annotation on your main class. It’s a shortcut for three annotations:

// These two are equivalent:
@SpringBootApplication
public class TaxfilingApplication { }

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class TaxfilingApplication { }
AnnotationWhat it does
@ConfigurationMarks this class as a source of bean definitions
@EnableAutoConfigurationSpring Boot guesses what you need based on dependencies (e.g., sees starter-web -> sets up Tomcat)
@ComponentScanScans current package + sub-packages for annotated classes

@Component and Its Specializations

@Component tells Spring: “create an instance of this class and manage it.” Spring provides specialized versions that work the same way but express intent:

@Component          <-- generic Spring-managed bean
  |-- @Controller   <-- handles HTTP requests (returns views)
  |-- @RestController <-- handles HTTP requests (returns JSON/data)
  |-- @Service      <-- business logic layer
  +-- @Repository   <-- data access layer

They all do the same thing at the core (register a bean), but:

  • @Repository adds automatic exception translation for database errors
  • @RestController adds @ResponseBody so return values become JSON
  • Using the right one makes your code self-documenting

Example of a typical layered architecture:

@RestController          // handles HTTP
public class TaxReturnController {
    private final TaxReturnService service;
    // ...
}

@Service                 // business logic
public class TaxReturnService {
    private final TaxReturnRepository repo;
    // ...
}

@Repository              // database access
public interface TaxReturnRepository extends JpaRepository<TaxReturn, Long> {
    // ...
}

Dependency Injection Annotations

@Autowired

Tells Spring to inject a dependency. Spring finds a matching bean and passes it in.

@Service
public class TaxReturnService {

    @Autowired
    private TaxReturnRepository repo;  // field injection
}

Constructor injection (preferred - no @Autowired needed if there’s only one constructor):

@Service
public class TaxReturnService {

    private final TaxReturnRepository repo;

    // Spring automatically injects the repo here
    public TaxReturnService(TaxReturnRepository repo) {
        this.repo = repo;
    }
}

Constructor injection is preferred because:

  • Fields can be final (immutable)
  • Dependencies are explicit
  • Easier to test (just pass mocks to the constructor)

@Bean

Defines a bean manually in a @Configuration class. Use this when you need to configure a third-party object that you can’t annotate with @Component.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();  // Spring manages this instance
    }
}

@Value

Injects values from application.properties:

@Value("${spring.application.name}")
private String appName;  // "taxfiling"

Web / REST Annotations

Request Mapping

@RestController
@RequestMapping("/api/returns")    // base path for all methods in this class
public class TaxReturnController {

    @GetMapping              // GET /api/returns
    public List<TaxReturn> list() { }

    @GetMapping("/{id}")     // GET /api/returns/42
    public TaxReturn get(@PathVariable Long id) { }

    @PostMapping             // POST /api/returns
    public TaxReturn create(@RequestBody TaxReturn taxReturn) { }

    @PutMapping("/{id}")     // PUT /api/returns/42
    public TaxReturn update(@PathVariable Long id, @RequestBody TaxReturn taxReturn) { }

    @DeleteMapping("/{id}")  // DELETE /api/returns/42
    public void delete(@PathVariable Long id) { }
}

Parameter Annotations

AnnotationSourceExample
@PathVariableURL path/returns/{id} -> @PathVariable Long id
@RequestParamQuery string/returns?status=filed -> @RequestParam String status
@RequestBodyJSON request bodyPOST payload -> @RequestBody TaxReturn data
@RequestHeaderHTTP headerAuthorization: Bearer ... -> @RequestHeader String authorization

Response Annotations

@PostMapping
@ResponseStatus(HttpStatus.CREATED)  // returns 201 instead of default 200
public TaxReturn create(@RequestBody TaxReturn taxReturn) { }

JPA / Database Annotations

These map Java classes to database tables (you’ll use these when adding PostgreSQL):

@Entity                              // this class maps to a DB table
@Table(name = "tax_returns")         // explicit table name
public class TaxReturn {

    @Id                              // primary key
    @GeneratedValue(strategy = GenerationType.IDENTITY)  // auto-increment
    private Long id;

    @Column(nullable = false)        // column constraint
    private String taxpayerName;

    @Enumerated(EnumType.STRING)     // store enum as string, not ordinal
    private FilingStatus status;

    @CreationTimestamp               // auto-set on insert
    private LocalDateTime createdAt;
}

Validation Annotations

Used with spring-boot-starter-validation to validate request data:

public class TaxReturnRequest {

    @NotBlank(message = "Name is required")
    private String taxpayerName;

    @Min(2020)
    private int taxYear;

    @Email
    private String email;
}

// In controller - @Valid triggers validation
@PostMapping
public TaxReturn create(@Valid @RequestBody TaxReturnRequest request) { }

Testing Annotations

@SpringBootTest                    // start full application context
class TaxfilingApplicationTests {

    @Test                          // marks a test method
    void contextLoads() { }
}

@WebMvcTest(TaxReturnController.class)  // test only the web layer
class TaxReturnControllerTest {

    @Autowired
    private MockMvc mockMvc;       // simulates HTTP requests

    @MockBean                      // replace real bean with a mock
    private TaxReturnService service;
}
AnnotationWhat it startsSpeed
@SpringBootTestFull application contextSlow
@WebMvcTestOnly web layer (controllers)Fast
@DataJpaTestOnly JPA layer (repositories + DB)Fast

Annotation Quick Reference

Application:   @SpringBootApplication
Beans:         @Component, @Service, @Repository, @Controller, @RestController
Config:        @Configuration, @Bean, @Value
Injection:     @Autowired (or just constructor injection)
Web:           @RequestMapping, @GetMapping, @PostMapping, @PutMapping, @DeleteMapping
Parameters:    @PathVariable, @RequestParam, @RequestBody
Database:      @Entity, @Table, @Id, @Column, @GeneratedValue
Validation:    @Valid, @NotBlank, @Min, @Max, @Email
Testing:       @SpringBootTest, @WebMvcTest, @DataJpaTest, @MockBean

Frontend

Overview

The frontend is a React application bootstrapped with Vite. It provides the user interface for creating, reviewing, and filing tax returns.

Project Layout

frontend/
  src/
    components/      # Reusable UI components
    pages/           # Page-level components (routes)
    services/        # API client functions
    hooks/           # Custom React hooks
    context/         # React context providers
    types/           # TypeScript type definitions
    App.tsx          # Root component
    main.tsx         # Entry point
  public/            # Static assets
  package.json
  vite.config.ts

Key Dependencies

  • react + react-dom - UI framework
  • react-router-dom - Client-side routing
  • axios - HTTP client
  • typescript - Type safety

Pages

RoutePageDescription
/HomeDashboard with return list
/returns/newNew ReturnTax return input form
/returns/:idReturn DetailView a single return
/returns/:id/reviewFinish and FileReview and submit the return
/returns/:id/printPrintPrintable/downloadable return view

Development

cd frontend
npm install
npm run dev

Building for Production

npm run build

Output goes to frontend/dist/.

Deployment

Overview

The application is containerized with Docker and deployed to Kubernetes. This mirrors Intuit’s use of IKS (Intuit Kubernetes Service) and CDD for deployments.

Docker

Backend

FROM eclipse-temurin:25-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Frontend

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80

Build Images

# Backend
cd backend && mvn clean package -DskipTests
docker build -t intuit-crash-course-backend .

# Frontend
cd frontend
docker build -t intuit-crash-course-frontend .

Kubernetes

Manifests live in the k8s/ directory.

Components

ManifestResource
backend.yamlDeployment + Service for backend
frontend.yamlDeployment + Service for frontend
postgres.yamlStatefulSet + Service for DB
redis.yamlDeployment + Service for cache
ingress.yamlIngress for routing

Deploy to minikube

# Start cluster
minikube start

# Apply manifests
kubectl apply -f k8s/

# Check status
kubectl get pods
kubectl get services

# Access the app
minikube service frontend-service

Useful Commands

# View logs
kubectl logs -f deployment/backend

# Scale
kubectl scale deployment/backend --replicas=3

# Rollback
kubectl rollout undo deployment/backend