# Dive Tool Architecture: How the Container Image Analyzer Is Structured

> Explore the modular, event-driven architecture of the Dive tool. Understand how its container image analyzer separates layers for efficient CLI command dispatch, image resolution, and UI rendering.

- Repository: [Alex Goodman/dive](https://github.com/wagoodman/dive)
- Tags: architecture
- Published: 2026-03-07

---

**The Dive tool architecture implements a modular, event-driven Go application that separates CLI command dispatch, image resolution, domain modeling, analysis computation, and terminal UI rendering into distinct, loosely-coupled layers.**

The **Dive** container image analyzer from `wagoodman/dive` is architected as a layered Go program designed to inspect Docker and OCI images efficiently. This architecture cleanly isolates container engine interactions from filesystem analysis and user interface concerns, enabling support for multiple backends while maintaining a responsive terminal experience through asynchronous event handling.

## Architectural Layers and Component Responsibilities

The codebase organizes functionality into distinct packages, each with well-defined responsibilities and minimal cross-dependencies.

### CLI and Command Dispatcher

The entry point and command routing layer handles flag parsing and application initialization. In [`cmd/dive/main.go`](https://github.com/wagoodman/dive/blob/main/cmd/dive/main.go), the application constructs a `clio.Application` instance and delegates to the root command defined in [`cmd/dive/cli/internal/command/root.go`](https://github.com/wagoodman/dive/blob/main/cmd/dive/cli/internal/command/root.go). This layer interprets sub-commands such as `build`, `export`, and `ci`, routing them to appropriate handlers while constructing the image resolver pipeline.

### Image Resolution Abstraction

Before analysis begins, the system must determine how to access the target image. The [`dive/get_image_resolver.go`](https://github.com/wagoodman/dive/blob/main/dive/get_image_resolver.go) file implements `GetImageResolver`, which detects the image source—whether Docker engine, Podman engine, or Docker archive—and returns a concrete implementation of the `image.Resolver` interface. This pluggable design allows new container backends to be added without modifying core analysis logic.

### Core Domain Models

The heart of the architecture resides in the `dive/image` and `dive/filetree` packages. The [`dive/image/image.go`](https://github.com/wagoodman/dive/blob/main/dive/image/image.go) file defines the `Image` struct, which aggregates a slice of `Layer` objects defined in [`dive/image/layer.go`](https://github.com/wagoodman/dive/blob/main/dive/image/layer.go). Each `Layer` maintains its own `filetree.FileTree` instance (from [`dive/filetree/file_tree.go`](https://github.com/wagoodman/dive/blob/main/dive/filetree/file_tree.go)), representing the complete filesystem snapshot at that layer's state. This layer-centric modeling mirrors the container image format directly and simplifies differential calculations.

### Analysis Engine

Once image data is loaded, [`dive/image/analysis.go`](https://github.com/wagoodman/dive/blob/main/dive/image/analysis.go) executes the analysis phase. This engine walks every layer's `FileTree` to compute metrics including added files, removed files, modifications, and efficiency scores. The results are stored back into the `Image` struct, creating a comprehensive audit trail of space usage and duplication across layers.

### Event-Driven UI Architecture

The terminal interface lives under `cmd/dive/cli/internal/ui/v1` and follows an event-driven pattern. The [`internal/bus/bus.go`](https://github.com/wagoodman/dive/blob/main/internal/bus/bus.go) implementation provides a publish-subscribe mechanism where analysis progress and user interactions emit events (such as `event.LayerSelected`). UI components in [`cmd/dive/cli/internal/ui/v1/app/app.go`](https://github.com/wagoodman/dive/blob/main/cmd/dive/cli/internal/ui/v1/app/app.go) and view files like [`cmd/dive/cli/internal/ui/v1/view/layer.go`](https://github.com/wagoodman/dive/blob/main/cmd/dive/cli/internal/ui/v1/view/layer.go) subscribe to these events, enabling asynchronous updates without blocking the main analysis goroutine.

### Command Adapters and Utilities

Bridging the CLI and domain layers, the [`cmd/dive/cli/internal/command/adapter/resolver.go`](https://github.com/wagoodman/dive/blob/main/cmd/dive/cli/internal/command/adapter/resolver.go) file contains adapters that translate high-level CLI actions into concrete operations. These adapters orchestrate the resolution, analysis, and export phases, ensuring that non-interactive commands (like CI evaluation or JSON export) can reuse the same core logic as the interactive TUI. Supporting infrastructure in [`internal/utils/format.go`](https://github.com/wagoodman/dive/blob/main/internal/utils/format.go) and [`internal/log/log.go`](https://github.com/wagoodman/dive/blob/main/internal/log/log.go) provides formatting helpers and structured logging across all layers.

## Execution Flow Through the System

A typical analysis session follows a precise pipeline through these architectural layers:

1. **Application Startup** – [`cmd/dive/main.go`](https://github.com/wagoodman/dive/blob/main/cmd/dive/main.go) instantiates the `clio.Application` and invokes `app.Run()`, triggering the root command setup.

2. **Source Detection** – The root command calls `dive.DeriveImageSource()` and `dive.GetImageResolver()` to instantiate the appropriate resolver for the image source (Docker daemon, Podman socket, or tar archive).

3. **Image Hydration** – The resolver fetches the image manifest and constructs `image.Layer` objects, each initialized with a `filetree.FileTree` representing that layer's filesystem contribution.

4. **Statistical Analysis** – The `image.Analysis` engine traverses every `FileTree`, calculating per-layer statistics including cumulative size, file additions, deletions, and efficiency ratios.

5. **Event Publication** – Throughout analysis, the system posts progress events to `internal/bus/event`, allowing the UI to display real-time status updates without interrupting computation.

6. **Interactive Rendering** – The UI package (`cmd/dive/cli/internal/ui/v1`) renders the layer list, file tree view, and diff view. User interactions generate bus events that trigger selective re-rendering of affected components.

7. **Output Generation** – For export or CI operations, command adapters consume the analyzed `Image` model and serialize results to JSON, CSV, or evaluation reports.

## Key Design Decisions

Several architectural choices distinguish Dive's implementation:

- **Separation of Concerns** – Core data structures in `dive/image` and `dive/filetree` contain no UI dependencies, ensuring that headless analysis commands (e.g., `dive --json`) execute without importing terminal rendering libraries.

- **Pluggable Resolver Interface** – The `image.Resolver` abstraction in [`dive/get_image_resolver.go`](https://github.com/wagoodman/dive/blob/main/dive/get_image_resolver.go) decouples the analysis engine from specific container runtimes, making it straightforward to add support for OCI registries or alternative container engines.

- **Event-Driven Decoupling** – By routing all state changes through the internal bus ([`internal/bus/bus.go`](https://github.com/wagoodman/dive/blob/main/internal/bus/bus.go)), long-running analysis operations remain non-blocking, while the UI maintains responsiveness through asynchronous event consumption.

- **Layer-Centric Domain Model** – Representing each container layer as a discrete `Layer` with its own `FileTree` (rather than global filesystem state) directly mirrors the underlying image specification and enables efficient differential analysis between arbitrary layer indices.

## Working with the Architecture

Developers extending Dive interact with these key APIs.

Creating and using an image resolver:

```go
src, name := dive.DeriveImageSource("docker://alpine:latest")
resolver, err := dive.GetImageResolver(src)
if err != nil {
    // handle resolution error
}
img, err := resolver.Resolve(name) // returns *image.Image

```

Inspecting layer metadata:

```go
for i, layer := range img.Layers {
    fmt.Printf("%d – %s – %s\n", i, layer.ShortId(),
        humanize.Bytes(layer.Size))
}

```

Subscribing to UI events:

```go
bus.Subscribe(event.LayerSelected, func(e event.Event) {
    // redraw file tree for the selected layer
    // e.Data contains the selected layer reference
})

```

## Summary

- **Dive tool architecture** implements a strict separation between CLI dispatch, image resolution, domain models, analysis computation, and UI rendering.
- The **Image Resolver** pattern in [`dive/get_image_resolver.go`](https://github.com/wagoodman/dive/blob/main/dive/get_image_resolver.go) abstracts multiple container engines behind a common interface.
- **FileTree** structures in [`dive/filetree/file_tree.go`](https://github.com/wagoodman/dive/blob/main/dive/filetree/file_tree.go) represent per-layer filesystem states, enabling efficient differential analysis.
- An **event bus** in [`internal/bus/bus.go`](https://github.com/wagoodman/dive/blob/main/internal/bus/bus.go) decouples long-running analysis from the terminal UI, maintaining application responsiveness.
- **Command adapters** translate CLI inputs into domain operations, supporting both interactive TUI and automated CI/export workflows.

## Frequently Asked Questions

### What role does the Image Resolver play in Dive's architecture?

The **Image Resolver** acts as a factory and strategy pattern implementation that detects the image source type (Docker daemon, Podman socket, or Docker archive) and returns a concrete `image.Resolver` capable of fetching that specific image format. Located in [`dive/get_image_resolver.go`](https://github.com/wagoodman/dive/blob/main/dive/get_image_resolver.go), this component isolates engine-specific logic from the core analysis engine, allowing Dive to support multiple container runtimes through a unified interface.

### How does Dive maintain UI responsiveness during long-running analysis?

Dive employs an **event-driven architecture** centered on the internal bus defined in [`internal/bus/bus.go`](https://github.com/wagoodman/dive/blob/main/internal/bus/bus.go). The analysis engine publishes progress events asynchronously while running in separate goroutines, and the UI components subscribe to these events to update views. This decoupling prevents the terminal interface from blocking while filesystem trees are being parsed and compared across layers.

### What data structures enable Dive's layer differential analysis?

The architecture uses a **layer-centric model** where each container layer is represented by a `Layer` struct (in [`dive/image/layer.go`](https://github.com/wagoodman/dive/blob/main/dive/image/layer.go)) containing its own `filetree.FileTree` instance. The `FileTree` implementation in [`dive/filetree/file_tree.go`](https://github.com/wagoodman/dive/blob/main/dive/filetree/file_tree.go) provides tree traversal and diff capabilities, allowing the analysis engine in [`dive/image/analysis.go`](https://github.com/wagoodman/dive/blob/main/dive/image/analysis.go) to compute exactly which files were added, removed, or modified between any two layer indices without re-processing the entire image.

### How does the command adapter pattern support different output modes?

The **command adapters** in [`cmd/dive/cli/internal/command/adapter/resolver.go`](https://github.com/wagoodman/dive/blob/main/cmd/dive/cli/internal/command/adapter/resolver.go) translate generic CLI requests into concrete domain operations. By sitting between the CLI layer and the core `Image` model, these adapters enable the same analyzed data to drive both the interactive TUI (rendered by `cmd/dive/cli/internal/ui/v1`) and automated outputs like JSON export or CI rule evaluation, ensuring consistent analysis logic across all usage modes.